]> source.dussan.org Git - redmine.git/commitdiff
Role based custom queries (#1019).
authorJean-Philippe Lang <jp_lang@yahoo.fr>
Thu, 11 Jul 2013 17:45:10 +0000 (17:45 +0000)
committerJean-Philippe Lang <jp_lang@yahoo.fr>
Thu, 11 Jul 2013 17:45:10 +0000 (17:45 +0000)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@11994 e93f8b46-1217-0410-a6f0-8f06a7374b81

18 files changed:
app/controllers/queries_controller.rb
app/helpers/issues_helper.rb
app/models/issue_query.rb
app/models/query.rb
app/models/user.rb
app/views/queries/_form.html.erb
app/views/queries/index.api.rsb
config/locales/en.yml
config/locales/fr.yml
db/migrate/20130602092539_create_queries_roles.rb [new file with mode: 0644]
db/migrate/20130710182539_add_queries_visibility.rb [new file with mode: 0644]
public/stylesheets/application.css
test/fixtures/queries.yml
test/functional/calendars_controller_test.rb
test/functional/issues_controller_test.rb
test/functional/queries_controller_test.rb
test/unit/query_test.rb
test/unit/user_test.rb

index 63925512597e580e64fec2d6af9d1ca97d930bf3..cd1ec87a29027e8eacca0e8f41643a7804f973c3 100644 (file)
@@ -45,7 +45,7 @@ class QueriesController < ApplicationController
     @query = IssueQuery.new
     @query.user = User.current
     @query.project = @project
-    @query.is_public = false unless User.current.allowed_to?(:manage_public_queries, @project) || User.current.admin?
+    @query.visibility = IssueQuery::VISIBILITY_PRIVATE unless User.current.allowed_to?(:manage_public_queries, @project) || User.current.admin?
     @query.build_from_params(params)
   end
 
@@ -53,7 +53,7 @@ class QueriesController < ApplicationController
     @query = IssueQuery.new(params[:query])
     @query.user = User.current
     @query.project = params[:query_is_for_all] ? nil : @project
-    @query.is_public = false unless User.current.allowed_to?(:manage_public_queries, @project) || User.current.admin?
+    @query.visibility = IssueQuery::VISIBILITY_PRIVATE unless User.current.allowed_to?(:manage_public_queries, @project) || User.current.admin?
     @query.build_from_params(params)
     @query.column_names = nil if params[:default_columns]
 
@@ -71,7 +71,7 @@ class QueriesController < ApplicationController
   def update
     @query.attributes = params[:query]
     @query.project = nil if params[:query_is_for_all]
-    @query.is_public = false unless User.current.allowed_to?(:manage_public_queries, @project) || User.current.admin?
+    @query.visibility = IssueQuery::VISIBILITY_PRIVATE unless User.current.allowed_to?(:manage_public_queries, @project) || User.current.admin?
     @query.build_from_params(params)
     @query.column_names = nil if params[:default_columns]
 
index 05090c7b321b05960dc805823469e3476f171ea0..f88a61e218295211b7cc98c033d1e547b0534dbf 100644 (file)
@@ -225,8 +225,8 @@ module IssuesHelper
 
   def render_sidebar_queries
     out = ''.html_safe
-    out << query_links(l(:label_my_queries), sidebar_queries.reject(&:is_public?))
-    out << query_links(l(:label_query_plural), sidebar_queries.select(&:is_public?))
+    out << query_links(l(:label_my_queries), sidebar_queries.select(&:is_private?))
+    out << query_links(l(:label_query_plural), sidebar_queries.reject(&:is_private?))
     out
   end
 
index f9adf4697d6c7590c4a3e21828d84abb35b89c13..b2e470d7a6403b0e61f7abbbe910c56035621be5 100644 (file)
@@ -45,9 +45,25 @@ class IssueQuery < Query
   scope :visible, lambda {|*args|
     user = args.shift || User.current
     base = Project.allowed_to_condition(user, :view_issues, *args)
-    user_id = user.logged? ? user.id : 0
-
-    includes(:project).where("(#{table_name}.project_id IS NULL OR (#{base})) AND (#{table_name}.is_public = ? OR #{table_name}.user_id = ?)", true, user_id)
+    scope = includes(:project).where("#{table_name}.project_id IS NULL OR (#{base})")
+
+    if user.admin?
+      scope.where("#{table_name}.visibility <> ? OR #{table_name}.user_id = ?", VISIBILITY_PRIVATE, user.id)
+    elsif user.memberships.any?
+      scope.where("#{table_name}.visibility = ?" +
+        " OR (#{table_name}.visibility = ? AND #{table_name}.id IN (" +
+          "SELECT DISTINCT q.id FROM #{table_name} q" +
+          " INNER JOIN #{table_name_prefix}queries_roles#{table_name_suffix} qr on qr.query_id = q.id" +
+          " INNER JOIN #{MemberRole.table_name} mr ON mr.role_id = qr.role_id" +
+          " INNER JOIN #{Member.table_name} m ON m.id = mr.member_id AND m.user_id = ?" +
+          " WHERE q.project_id IS NULL OR q.project_id = m.project_id))" +
+        " OR #{table_name}.user_id = ?",
+        VISIBILITY_PUBLIC, VISIBILITY_ROLES, user.id, user.id)
+    elsif user.logged?
+      scope.where("#{table_name}.visibility = ? OR #{table_name}.user_id = ?", VISIBILITY_PUBLIC, user.id)
+    else
+      scope.where("#{table_name}.visibility = ?", VISIBILITY_PUBLIC)
+    end
   }
 
   def initialize(attributes=nil, *args)
@@ -57,7 +73,28 @@ class IssueQuery < Query
 
   # Returns true if the query is visible to +user+ or the current user.
   def visible?(user=User.current)
-    (project.nil? || user.allowed_to?(:view_issues, project)) && (self.is_public? || self.user_id == user.id)
+    return true if user.admin?
+    return false unless project.nil? || user.allowed_to?(:view_issues, project)
+    case visibility
+    when VISIBILITY_PUBLIC
+      true
+    when VISIBILITY_ROLES
+      if project
+        (user.roles_for_project(project) & roles).any?
+      else
+        Member.where(:user_id => user.id).joins(:roles).where(:member_roles => {:role_id => roles.map(&:id)}).any?
+      end
+    else
+      user == self.user
+    end
+  end
+
+  def is_private?
+    visibility == VISIBILITY_PRIVATE
+  end
+
+  def is_public?
+    !is_private?
   end
 
   def initialize_available_filters
index aa24857bd9f88c035210fa98b5dadbe438d2d55d..2753c276887b9a1360fe60e973f17ab79d1eef4e 100644 (file)
@@ -116,8 +116,13 @@ class Query < ActiveRecord::Base
   class StatementInvalid < ::ActiveRecord::StatementInvalid
   end
 
+  VISIBILITY_PRIVATE = 0
+  VISIBILITY_ROLES   = 1
+  VISIBILITY_PUBLIC  = 2
+
   belongs_to :project
   belongs_to :user
+  has_and_belongs_to_many :roles, :join_table => "#{table_name_prefix}queries_roles#{table_name_suffix}", :foreign_key => "query_id"
   serialize :filters
   serialize :column_names
   serialize :sort_criteria, Array
@@ -126,7 +131,17 @@ class Query < ActiveRecord::Base
 
   validates_presence_of :name
   validates_length_of :name, :maximum => 255
+  validates :visibility, :inclusion => { :in => [VISIBILITY_PUBLIC, VISIBILITY_ROLES, VISIBILITY_PRIVATE] }
   validate :validate_query_filters
+  validate do |query|
+    errors.add(:base, l(:label_role_plural) + ' ' + l('activerecord.errors.messages.blank')) if query.visibility == VISIBILITY_ROLES && roles.blank?
+  end
+
+  after_save do |query|
+    if query.visibility_changed? && query.visibility != VISIBILITY_ROLES
+           query.roles.clear
+         end
+  end
 
   class_attribute :operators
   self.operators = {
@@ -245,9 +260,9 @@ class Query < ActiveRecord::Base
   def editable_by?(user)
     return false unless user
     # Admin can edit them all and regular users can edit their private queries
-    return true if user.admin? || (!is_public && self.user_id == user.id)
+    return true if user.admin? || (is_private? && self.user_id == user.id)
     # Members can not edit public queries that are for all project (only admin is allowed to)
-    is_public && !@is_for_all && user.allowed_to?(:manage_public_queries, project)
+    is_public? && !@is_for_all && user.allowed_to?(:manage_public_queries, project)
   end
 
   def trackers
index 40a7120b463e6684d9d0a35470e01863db2ccb1d..441e6b5b8f9b9b5ff842817489360a8dc9f8c457 100644 (file)
@@ -669,7 +669,7 @@ class User < Principal
     Message.update_all ['author_id = ?', substitute.id], ['author_id = ?', id]
     News.update_all ['author_id = ?', substitute.id], ['author_id = ?', id]
     # Remove private queries and keep public ones
-    ::Query.delete_all ['user_id = ? AND is_public = ?', id, false]
+    ::Query.delete_all ['user_id = ? AND visibility = ?', id, ::Query::VISIBILITY_PRIVATE]
     ::Query.update_all ['user_id = ?', substitute.id], ['user_id = ?', id]
     TimeEntry.update_all ['user_id = ?', substitute.id], ['user_id = ?', id]
     Token.delete_all ['user_id = ?', id]
index 1070b4716c1c6742fd7589a56ccd397be5fe7390..1168215299243b1a1a80582e899d2b135bd51547 100644 (file)
@@ -6,15 +6,22 @@
 <%= text_field 'query', 'name', :size => 80 %></p>
 
 <% if User.current.admin? || User.current.allowed_to?(:manage_public_queries, @project) %>
-<p><label for="query_is_public"><%=l(:field_is_public)%></label>
-<%= check_box 'query', 'is_public',
-      :onchange => (User.current.admin? ? nil : 'if (this.checked) {$("#query_is_for_all").removeAttr("checked"); $("#query_is_for_all").attr("disabled", true);} else {$("#query_is_for_all").removeAttr("disabled");}') %></p>
+<p><label><%=l(:field_visible)%></label>
+  <label class="block"><%= radio_button 'query', 'visibility', Query::VISIBILITY_PRIVATE %> <%= l(:label_visibility_private) %></label>
+  <label class="block"><%= radio_button 'query', 'visibility', Query::VISIBILITY_ROLES %> <%= l(:label_visibility_roles) %>:</label>
+  <% Role.givable.sorted.each do |role| %>
+    <label class="block role-visibility"><%= check_box_tag 'query[role_ids][]', role.id, @query.roles.include?(role), :id => nil %> <%= role.name %></label>
+  <% end %>
+  <label class="block"><%= radio_button 'query', 'visibility', Query::VISIBILITY_PUBLIC %> <%= l(:label_visibility_public) %></label>
+  <%= hidden_field_tag 'query[role_ids][]', '' %>
+</p>
 <% end %>
 
 <p><label for="query_is_for_all"><%=l(:field_is_for_all)%></label>
 <%= check_box_tag 'query_is_for_all', 1, @query.project.nil?,
       :disabled => (!@query.new_record? && (@query.project.nil? || (@query.is_public? && !User.current.admin?))) %></p>
 
+<fieldset><legend><%= l(:label_options) %></legend>
 <p><label for="query_default_columns"><%=l(:label_default_columns)%></label>
 <%= check_box_tag 'default_columns', 1, @query.has_default_columns?, :id => 'query_default_columns',
       :onclick => 'if (this.checked) {$("#columns").hide();} else {$("#columns").show();}' %></p>
@@ -24,6 +31,7 @@
 
 <p><label><%= l(:button_show) %></label>
 <%= available_block_columns_tags(@query) %></p>
+</fieldset>
 </div>
 
 <fieldset id="filters"><legend><%= l(:label_filter_plural) %></legend>
 <% end %>
 
 </div>
+
+<%= javascript_tag do %>
+$(document).ready(function(){
+  $("input[name='query[visibility]']").change(function(){
+    var checked = $('#query_visibility_1').is(':checked');
+    $("input[name='query[role_ids][]'][type=checkbox]").attr('disabled', !checked);
+  }).trigger('change');
+});
+<% end %>
index 800b4964e97f8909e1de2e718c94ffd9a9cb89e7..cdf8e4020fbf531fd4edda4510fb4c08ee0f793c 100644 (file)
@@ -3,7 +3,7 @@ api.array :queries, api_meta(:total_count => @query_count, :offset => @offset, :
     api.query do
       api.id          query.id
       api.name        query.name
-      api.is_public   query.is_public
+      api.is_public   query.is_public?
       api.project_id  query.project_id
     end
   end
index eebc34500f37818c48c18ac5870c8735c5e31634..7099de8c4351559cd5ca402212628e133748d461 100644 (file)
@@ -896,6 +896,9 @@ en:
   label_cross_project_hierarchy: With project hierarchy
   label_cross_project_system: With all projects
   label_gantt_progress_line: Progress line
+  label_visibility_private: to me only
+  label_visibility_roles: to these roles only
+  label_visibility_public: to any users
 
   button_login: Login
   button_submit: Submit
index 0847308b053bbcbece1767f14e39b46b741fe05d..510913a1778068c9235aecc4388a0b85c79a3956 100644 (file)
@@ -872,6 +872,9 @@ fr:
   label_cross_project_hierarchy: Avec toute la hiĆ©rarchie
   label_cross_project_system: Avec tous les projets
   label_gantt_progress_line: Ligne de progression
+  label_visibility_private: par moi uniquement
+  label_visibility_roles: par ces roles uniquement
+  label_visibility_public: par tout le monde
 
   button_login: Connexion
   button_submit: Soumettre
diff --git a/db/migrate/20130602092539_create_queries_roles.rb b/db/migrate/20130602092539_create_queries_roles.rb
new file mode 100644 (file)
index 0000000..7713a7a
--- /dev/null
@@ -0,0 +1,13 @@
+class CreateQueriesRoles < ActiveRecord::Migration
+  def self.up
+    create_table :queries_roles, :id => false do |t|
+      t.column :query_id, :integer, :null => false
+      t.column :role_id, :integer, :null => false
+    end
+    add_index :queries_roles, [:query_id, :role_id], :unique => true, :name => :queries_roles_ids
+  end
+
+  def self.down
+    drop_table :queries_roles
+  end
+end
diff --git a/db/migrate/20130710182539_add_queries_visibility.rb b/db/migrate/20130710182539_add_queries_visibility.rb
new file mode 100644 (file)
index 0000000..d6cd1a7
--- /dev/null
@@ -0,0 +1,13 @@
+class AddQueriesVisibility < ActiveRecord::Migration
+  def up
+    add_column :queries, :visibility, :integer, :default => 0
+    Query.where(:is_public => true).update_all(:visibility => 2)
+    remove_column :queries, :is_public
+  end
+
+  def down
+    add_column :queries, :is_public, :boolean, :default => true, :null => false
+    Query.where('visibility <> ?', 2).update_all(:is_public => false)
+    remove_column :queries, :visibility
+  end
+end
index 31e56b88ed1e7c83b5e604491fc5721b87233211..514e33145a753041ca6626a24fbfbff22e5b7be3 100644 (file)
@@ -584,6 +584,8 @@ input.autocomplete.ajax-loading {
   background-image: url(../images/loading.gif);
 }
 
+.role-visibility {padding-left:2em;}
+
 /***** Flash & error messages ****/
 #errorExplanation, div.flash, .nodata, .warning, .conflict {
   padding: 4px 4px 4px 30px;
index f88da94d16103bfd7961b76c4a4f3ce975e27db6..b92079aeb8fa130a09de4ebd8d167c7d3ba693af 100644 (file)
@@ -3,7 +3,7 @@ queries_001:
   id: 1
   type: IssueQuery
   project_id: 1
-  is_public: true
+  visibility: 2
   name: Multiple custom fields query
   filters: |
     --- 
@@ -26,7 +26,7 @@ queries_002:
   id: 2
   type: IssueQuery
   project_id: 1
-  is_public: false
+  visibility: 0
   name: Private query for cookbook
   filters: |
     --- 
@@ -45,7 +45,7 @@ queries_003:
   id: 3
   type: IssueQuery
   project_id: 
-  is_public: false
+  visibility: 0
   name: Private query for all projects
   filters: |
     --- 
@@ -60,7 +60,7 @@ queries_004:
   id: 4
   type: IssueQuery
   project_id: 
-  is_public: true
+  visibility: 2
   name: Public query for all projects
   filters: |
     --- 
@@ -75,7 +75,7 @@ queries_005:
   id: 5
   type: IssueQuery
   project_id: 
-  is_public: true
+  visibility: 2
   name: Open issues by priority and tracker
   filters: |
     --- 
@@ -96,7 +96,7 @@ queries_006:
   id: 6
   type: IssueQuery
   project_id: 
-  is_public: true
+  visibility: 2
   name: Open issues grouped by tracker
   filters: |
     --- 
@@ -116,7 +116,7 @@ queries_007:
   id: 7
   type: IssueQuery
   project_id: 2
-  is_public: true
+  visibility: 2
   name: Public query for project 2
   filters: |
     --- 
@@ -131,7 +131,7 @@ queries_008:
   id: 8
   type: IssueQuery
   project_id: 2
-  is_public: false
+  visibility: 0
   name: Private query for project 2
   filters: |
     --- 
@@ -146,7 +146,7 @@ queries_009:
   id: 9
   type: IssueQuery
   project_id: 
-  is_public: true
+  visibility: 2
   name: Open issues grouped by list custom field
   filters: |
     --- 
index dc302f45600cb1791057a1ba780e84b23f67f7b3..5848e7601218e457db7da4ff10868af07d89cd0a 100644 (file)
@@ -34,7 +34,7 @@ class CalendarsControllerTest < ActionController::TestCase
   end
 
   def test_show_should_run_custom_queries
-    @query = IssueQuery.create!(:name => 'Calendar', :is_public => true)
+    @query = IssueQuery.create!(:name => 'Calendar', :visibility => IssueQuery::VISIBILITY_PUBLIC)
 
     get :show, :query_id => @query.id
     assert_response :success
index 707b57438dfd9232d87cd10cdab02ae18e2c34ff..ec10af116649bab2cb10d2adceec5f0ae84926fe 100644 (file)
@@ -327,7 +327,7 @@ class IssuesControllerTest < ActionController::TestCase
   end
 
   def test_index_with_cross_project_query_in_session_should_show_project_issues
-    q = IssueQuery.create!(:name => "test", :user_id => 2, :is_public => false, :project => nil)
+    q = IssueQuery.create!(:name => "test", :user_id => 2, :visibility => IssueQuery::VISIBILITY_PRIVATE, :project => nil)
     @request.session[:query] = {:id => q.id, :project_id => 1}
 
     with_settings :display_subprojects_issues => '0' do
@@ -341,7 +341,7 @@ class IssuesControllerTest < ActionController::TestCase
   end
 
   def test_private_query_should_not_be_available_to_other_users
-    q = IssueQuery.create!(:name => "private", :user => User.find(2), :is_public => false, :project => nil)
+    q = IssueQuery.create!(:name => "private", :user => User.find(2), :visibility => IssueQuery::VISIBILITY_PRIVATE, :project => nil)
     @request.session[:user_id] = 3
 
     get :index, :query_id => q.id
@@ -349,7 +349,7 @@ class IssuesControllerTest < ActionController::TestCase
   end
 
   def test_private_query_should_be_available_to_its_user
-    q = IssueQuery.create!(:name => "private", :user => User.find(2), :is_public => false, :project => nil)
+    q = IssueQuery.create!(:name => "private", :user => User.find(2), :visibility => IssueQuery::VISIBILITY_PRIVATE, :project => nil)
     @request.session[:user_id] = 2
 
     get :index, :query_id => q.id
@@ -357,7 +357,7 @@ class IssuesControllerTest < ActionController::TestCase
   end
 
   def test_public_query_should_be_available_to_other_users
-    q = IssueQuery.create!(:name => "private", :user => User.find(2), :is_public => true, :project => nil)
+    q = IssueQuery.create!(:name => "private", :user => User.find(2), :visibility => IssueQuery::VISIBILITY_PUBLIC, :project => nil)
     @request.session[:user_id] = 3
 
     get :index, :query_id => q.id
@@ -1151,7 +1151,7 @@ class IssuesControllerTest < ActionController::TestCase
   end
 
   def test_show_should_display_prev_next_links_with_saved_query_in_session
-    query = IssueQuery.create!(:name => 'test', :is_public => true,  :user_id => 1,
+    query = IssueQuery.create!(:name => 'test', :visibility => IssueQuery::VISIBILITY_PUBLIC,  :user_id => 1,
       :filters => {'status_id' => {:values => ['5'], :operator => '='}},
       :sort_criteria => [['id', 'asc']])
     @request.session[:query] = {:id => query.id, :project_id => nil}
@@ -1243,7 +1243,7 @@ class IssuesControllerTest < ActionController::TestCase
     CustomValue.create!(:custom_field => cf, :customized => Issue.find(3), :value => '3')
     CustomValue.create!(:custom_field => cf, :customized => Issue.find(5), :value => '')
 
-    query = IssueQuery.create!(:name => 'test', :is_public => true,  :user_id => 1, :filters => {},
+    query = IssueQuery.create!(:name => 'test', :visibility => IssueQuery::VISIBILITY_PUBLIC,  :user_id => 1, :filters => {},
       :sort_criteria => [["cf_#{cf.id}", 'asc'], ['id', 'asc']])
     @request.session[:query] = {:id => query.id, :project_id => nil}
 
index a232fec2ef199720ff63c22da8ad8b04926793cf..b2bbb22fd691fb7c495d342c5add7cb982a81745 100644 (file)
@@ -35,9 +35,7 @@ class QueriesControllerTest < ActionController::TestCase
     get :new, :project_id => 1
     assert_response :success
     assert_template 'new'
-    assert_tag :tag => 'input', :attributes => { :type => 'checkbox',
-                                                 :name => 'query[is_public]',
-                                                 :checked => nil }
+    assert_select 'input[name=?][value=0][checked=checked]', 'query[visibility]'
     assert_tag :tag => 'input', :attributes => { :type => 'checkbox',
                                                  :name => 'query_is_for_all',
                                                  :checked => nil,
@@ -53,8 +51,7 @@ class QueriesControllerTest < ActionController::TestCase
     get :new
     assert_response :success
     assert_template 'new'
-    assert_no_tag :tag => 'input', :attributes => { :type => 'checkbox',
-                                                    :name => 'query[is_public]' }
+    assert_select 'input[name=?]', 'query[visibility]', 0
     assert_tag :tag => 'input', :attributes => { :type => 'checkbox',
                                                  :name => 'query_is_for_all',
                                                  :checked => 'checked',
@@ -75,7 +72,7 @@ class QueriesControllerTest < ActionController::TestCase
          :f => ["status_id", "assigned_to_id"],
          :op => {"assigned_to_id" => "=", "status_id" => "o"},
          :v => { "assigned_to_id" => ["1"], "status_id" => ["1"]},
-         :query => {"name" => "test_new_project_public_query", "is_public" => "1"}
+         :query => {"name" => "test_new_project_public_query", "visibility" => "2"}
 
     q = Query.find_by_name('test_new_project_public_query')
     assert_redirected_to :controller => 'issues', :action => 'index', :project_id => 'ecookbook', :query_id => q
@@ -92,7 +89,7 @@ class QueriesControllerTest < ActionController::TestCase
          :fields => ["status_id", "assigned_to_id"],
          :operators => {"assigned_to_id" => "=", "status_id" => "o"},
          :values => { "assigned_to_id" => ["1"], "status_id" => ["1"]},
-         :query => {"name" => "test_new_project_private_query", "is_public" => "1"}
+         :query => {"name" => "test_new_project_private_query", "visibility" => "2"}
 
     q = Query.find_by_name('test_new_project_private_query')
     assert_redirected_to :controller => 'issues', :action => 'index', :project_id => 'ecookbook', :query_id => q
@@ -107,7 +104,7 @@ class QueriesControllerTest < ActionController::TestCase
          :fields => ["status_id", "assigned_to_id"],
          :operators => {"assigned_to_id" => "=", "status_id" => "o"},
          :values => { "assigned_to_id" => ["me"], "status_id" => ["1"]},
-         :query => {"name" => "test_new_global_private_query", "is_public" => "1"},
+         :query => {"name" => "test_new_global_private_query", "visibility" => "2"},
          :c => ["", "tracker", "subject", "priority", "category"]
 
     q = Query.find_by_name('test_new_global_private_query')
@@ -140,7 +137,7 @@ class QueriesControllerTest < ActionController::TestCase
          :operators => {"status_id" => "o"},
          :values => {"status_id" => ["1"]},
          :query => {:name => "test_new_with_sort",
-                    :is_public => "1",
+                    :visibility => "2",
                     :sort_criteria => {"0" => ["due_date", "desc"], "1" => ["tracker", ""]}}
 
     query = Query.find_by_name("test_new_with_sort")
@@ -163,9 +160,7 @@ class QueriesControllerTest < ActionController::TestCase
     get :edit, :id => 4
     assert_response :success
     assert_template 'edit'
-    assert_tag :tag => 'input', :attributes => { :type => 'checkbox',
-                                                 :name => 'query[is_public]',
-                                                 :checked => 'checked' }
+    assert_select 'input[name=?][value=2][checked=checked]', 'query[visibility]'
     assert_tag :tag => 'input', :attributes => { :type => 'checkbox',
                                                  :name => 'query_is_for_all',
                                                  :checked => 'checked',
@@ -177,8 +172,7 @@ class QueriesControllerTest < ActionController::TestCase
     get :edit, :id => 3
     assert_response :success
     assert_template 'edit'
-    assert_no_tag :tag => 'input', :attributes => { :type => 'checkbox',
-                                                    :name => 'query[is_public]' }
+    assert_select 'input[name=?]', 'query[visibility]', 0
     assert_tag :tag => 'input', :attributes => { :type => 'checkbox',
                                                  :name => 'query_is_for_all',
                                                  :checked => 'checked',
@@ -190,8 +184,7 @@ class QueriesControllerTest < ActionController::TestCase
     get :edit, :id => 2
     assert_response :success
     assert_template 'edit'
-    assert_no_tag :tag => 'input', :attributes => { :type => 'checkbox',
-                                                    :name => 'query[is_public]' }
+    assert_select 'input[name=?]', 'query[visibility]', 0
     assert_tag :tag => 'input', :attributes => { :type => 'checkbox',
                                                  :name => 'query_is_for_all',
                                                  :checked => nil,
@@ -203,10 +196,7 @@ class QueriesControllerTest < ActionController::TestCase
     get :edit, :id => 1
     assert_response :success
     assert_template 'edit'
-    assert_tag :tag => 'input', :attributes => { :type => 'checkbox',
-                                                 :name => 'query[is_public]',
-                                                 :checked => 'checked'
-                                                  }
+    assert_select 'input[name=?][value=2][checked=checked]', 'query[visibility]'
     assert_tag :tag => 'input', :attributes => { :type => 'checkbox',
                                                  :name => 'query_is_for_all',
                                                  :checked => nil,
@@ -240,7 +230,7 @@ class QueriesControllerTest < ActionController::TestCase
          :fields => ["status_id", "assigned_to_id"],
          :operators => {"assigned_to_id" => "=", "status_id" => "o"},
          :values => { "assigned_to_id" => ["me"], "status_id" => ["1"]},
-         :query => {"name" => "test_edit_global_private_query", "is_public" => "1"}
+         :query => {"name" => "test_edit_global_private_query", "visibility" => "2"}
 
     assert_redirected_to :controller => 'issues', :action => 'index', :query_id => 3
     q = Query.find_by_name('test_edit_global_private_query')
@@ -257,7 +247,7 @@ class QueriesControllerTest < ActionController::TestCase
          :fields => ["status_id", "assigned_to_id"],
          :operators => {"assigned_to_id" => "=", "status_id" => "o"},
          :values => { "assigned_to_id" => ["1"], "status_id" => ["1"]},
-         :query => {"name" => "test_edit_global_public_query", "is_public" => "1"}
+         :query => {"name" => "test_edit_global_public_query", "visibility" => "2"}
 
     assert_redirected_to :controller => 'issues', :action => 'index', :query_id => 4
     q = Query.find_by_name('test_edit_global_public_query')
index 59639384fe414cdedda225e128b94a6f1bdfb7b1..15f1cf21c0c79f576edc545fd781d4095223d659 100644 (file)
@@ -28,6 +28,24 @@ class QueryTest < ActiveSupport::TestCase
            :projects_trackers,
            :custom_fields_trackers
 
+  def test_query_with_roles_visibility_should_validate_roles
+    set_language_if_valid 'en'
+    query = IssueQuery.new(:name => 'Query', :visibility => IssueQuery::VISIBILITY_ROLES)
+    assert !query.save
+    assert_include "Roles can't be blank", query.errors.full_messages
+    query.role_ids = [1, 2]
+    assert query.save
+  end
+
+  def test_changing_roles_visibility_should_clear_roles
+    query = IssueQuery.create!(:name => 'Query', :visibility => IssueQuery::VISIBILITY_ROLES, :role_ids => [1, 2])
+    assert_equal 2, query.roles.count
+
+    query.visibility = IssueQuery::VISIBILITY_PUBLIC
+    query.save!
+    assert_equal 0, query.roles.count
+  end
+
   def test_available_filters_should_be_ordered
     set_language_if_valid 'en'
     query = IssueQuery.new
@@ -1089,6 +1107,54 @@ class QueryTest < ActiveSupport::TestCase
     assert !query_ids.include?(7), 'public query on private project was visible'
   end
 
+  def test_query_with_public_visibility_should_be_visible_to_anyone
+    q = IssueQuery.create!(:name => 'Query', :visibility => IssueQuery::VISIBILITY_PUBLIC)
+
+    assert q.visible?(User.anonymous)
+    assert IssueQuery.visible(User.anonymous).find_by_id(q.id)
+
+    assert q.visible?(User.find(7))
+    assert IssueQuery.visible(User.find(7)).find_by_id(q.id)
+
+    assert q.visible?(User.find(2))
+    assert IssueQuery.visible(User.find(2)).find_by_id(q.id)
+
+    assert q.visible?(User.find(1))
+    assert IssueQuery.visible(User.find(1)).find_by_id(q.id)
+  end
+
+  def test_query_with_roles_visibility_should_be_visible_to_user_with_role
+    q = IssueQuery.create!(:name => 'Query', :visibility => IssueQuery::VISIBILITY_ROLES, :role_ids => [1,2])
+
+    assert !q.visible?(User.anonymous)
+    assert_nil IssueQuery.visible(User.anonymous).find_by_id(q.id)
+
+    assert !q.visible?(User.find(7))
+    assert_nil IssueQuery.visible(User.find(7)).find_by_id(q.id)
+
+    assert q.visible?(User.find(2))
+    assert IssueQuery.visible(User.find(2)).find_by_id(q.id)
+
+    assert q.visible?(User.find(1))
+    assert IssueQuery.visible(User.find(1)).find_by_id(q.id)
+  end
+
+  def test_query_with_private_visibility_should_be_visible_to_owner
+    q = IssueQuery.create!(:name => 'Query', :visibility => IssueQuery::VISIBILITY_PRIVATE, :user => User.find(7))
+
+    assert !q.visible?(User.anonymous)
+    assert_nil IssueQuery.visible(User.anonymous).find_by_id(q.id)
+
+    assert q.visible?(User.find(7))
+    assert IssueQuery.visible(User.find(7)).find_by_id(q.id)
+
+    assert !q.visible?(User.find(2))
+    assert_nil IssueQuery.visible(User.find(2)).find_by_id(q.id)
+
+    assert q.visible?(User.find(1))
+    assert_nil IssueQuery.visible(User.find(1)).find_by_id(q.id)
+  end
+
   test "#available_filters should include users of visible projects in cross-project view" do
     users = IssueQuery.new.available_filters["assigned_to_id"]
     assert_not_nil users
index 4594d8e1a563d5be8eed65f99392b09f574e5102..52a7d64b72a6694757f40cb84cdb843fa665da2a 100644 (file)
@@ -291,7 +291,7 @@ class UserTest < ActiveSupport::TestCase
   end
 
   def test_destroy_should_delete_private_queries
-    query = Query.new(:name => 'foo', :is_public => false)
+    query = Query.new(:name => 'foo', :visibility => Query::VISIBILITY_PRIVATE)
     query.project_id = 1
     query.user_id = 2
     query.save!
@@ -302,7 +302,7 @@ class UserTest < ActiveSupport::TestCase
   end
 
   def test_destroy_should_update_public_queries
-    query = Query.new(:name => 'foo', :is_public => true)
+    query = Query.new(:name => 'foo', :visibility => Query::VISIBILITY_PUBLIC)
     query.project_id = 1
     query.user_id = 2
     query.save!