##// END OF EJS Templates
Queries can be marked as 'For all projects'. Such queries will be available on all projects and on the global issue list (#897, closes #671)....
Jean-Philippe Lang -
r1296:287d86e36325
parent child
Show More
@@ -0,0 +1,185
1 # redMine - project management software
2 # Copyright (C) 2006-2008 Jean-Philippe Lang
3 #
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
18 require File.dirname(__FILE__) + '/../test_helper'
19 require 'queries_controller'
20
21 # Re-raise errors caught by the controller.
22 class QueriesController; def rescue_action(e) raise e end; end
23
24 class QueriesControllerTest < Test::Unit::TestCase
25 fixtures :projects, :users, :members, :roles, :trackers, :issue_statuses, :issue_categories, :enumerations, :issues, :custom_fields, :custom_values, :queries
26
27 def setup
28 @controller = QueriesController.new
29 @request = ActionController::TestRequest.new
30 @response = ActionController::TestResponse.new
31 User.current = nil
32 end
33
34 def test_get_new
35 @request.session[:user_id] = 2
36 get :new, :project_id => 1
37 assert_response :success
38 assert_template 'new'
39 assert_tag :tag => 'input', :attributes => { :type => 'checkbox',
40 :name => 'query[is_public]',
41 :checked => nil }
42 assert_tag :tag => 'input', :attributes => { :type => 'checkbox',
43 :name => 'query_is_for_all',
44 :checked => nil,
45 :disabled => nil }
46 end
47
48 def test_new_project_public_query
49 @request.session[:user_id] = 2
50 post :new,
51 :project_id => 'ecookbook',
52 :confirm => '1',
53 :default_columns => '1',
54 :fields => ["status_id", "assigned_to_id"],
55 :operators => {"assigned_to_id" => "=", "status_id" => "o"},
56 :values => { "assigned_to_id" => ["1"], "status_id" => ["1"]},
57 :query => {"name" => "test_new_project_public_query", "is_public" => "1"},
58 :column_names => ["", "tracker", "status", "priority", "subject", "updated_on", "category"]
59
60 q = Query.find_by_name('test_new_project_public_query')
61 assert_redirected_to :controller => 'issues', :action => 'index', :query_id => q
62 assert q.is_public?
63 assert q.has_default_columns?
64 assert q.valid?
65 end
66
67 def test_new_project_private_query
68 @request.session[:user_id] = 3
69 post :new,
70 :project_id => 'ecookbook',
71 :confirm => '1',
72 :default_columns => '1',
73 :fields => ["status_id", "assigned_to_id"],
74 :operators => {"assigned_to_id" => "=", "status_id" => "o"},
75 :values => { "assigned_to_id" => ["1"], "status_id" => ["1"]},
76 :query => {"name" => "test_new_project_private_query", "is_public" => "1"},
77 :column_names => ["", "tracker", "status", "priority", "subject", "updated_on", "category"]
78
79 q = Query.find_by_name('test_new_project_private_query')
80 assert_redirected_to :controller => 'issues', :action => 'index', :query_id => q
81 assert !q.is_public?
82 assert q.has_default_columns?
83 assert q.valid?
84 end
85
86 def test_get_edit_global_public_query
87 @request.session[:user_id] = 1
88 get :edit, :id => 4
89 assert_response :success
90 assert_template 'edit'
91 assert_tag :tag => 'input', :attributes => { :type => 'checkbox',
92 :name => 'query[is_public]',
93 :checked => 'checked' }
94 assert_tag :tag => 'input', :attributes => { :type => 'checkbox',
95 :name => 'query_is_for_all',
96 :checked => 'checked',
97 :disabled => 'disabled' }
98 end
99
100 def test_edit_global_public_query
101 @request.session[:user_id] = 1
102 post :edit,
103 :id => 4,
104 :confirm => '1',
105 :default_columns => '1',
106 :fields => ["status_id", "assigned_to_id"],
107 :operators => {"assigned_to_id" => "=", "status_id" => "o"},
108 :values => { "assigned_to_id" => ["1"], "status_id" => ["1"]},
109 :query => {"name" => "test_edit_global_public_query", "is_public" => "1"},
110 :column_names => ["", "tracker", "status", "priority", "subject", "updated_on", "category"]
111
112 assert_redirected_to :controller => 'issues', :action => 'index', :query_id => 4
113 q = Query.find_by_name('test_edit_global_public_query')
114 assert q.is_public?
115 assert q.has_default_columns?
116 assert q.valid?
117 end
118
119 def test_get_edit_global_private_query
120 @request.session[:user_id] = 3
121 get :edit, :id => 3
122 assert_response :success
123 assert_template 'edit'
124 assert_no_tag :tag => 'input', :attributes => { :type => 'checkbox',
125 :name => 'query[is_public]' }
126 assert_tag :tag => 'input', :attributes => { :type => 'checkbox',
127 :name => 'query_is_for_all',
128 :checked => 'checked',
129 :disabled => 'disabled' }
130 end
131
132 def test_edit_global_private_query
133 @request.session[:user_id] = 3
134 post :edit,
135 :id => 3,
136 :confirm => '1',
137 :default_columns => '1',
138 :fields => ["status_id", "assigned_to_id"],
139 :operators => {"assigned_to_id" => "=", "status_id" => "o"},
140 :values => { "assigned_to_id" => ["me"], "status_id" => ["1"]},
141 :query => {"name" => "test_edit_global_private_query", "is_public" => "1"},
142 :column_names => ["", "tracker", "status", "priority", "subject", "updated_on", "category"]
143
144 assert_redirected_to :controller => 'issues', :action => 'index', :query_id => 3
145 q = Query.find_by_name('test_edit_global_private_query')
146 assert !q.is_public?
147 assert q.has_default_columns?
148 assert q.valid?
149 end
150
151 def test_get_edit_project_private_query
152 @request.session[:user_id] = 3
153 get :edit, :id => 2
154 assert_response :success
155 assert_template 'edit'
156 assert_no_tag :tag => 'input', :attributes => { :type => 'checkbox',
157 :name => 'query[is_public]' }
158 assert_tag :tag => 'input', :attributes => { :type => 'checkbox',
159 :name => 'query_is_for_all',
160 :checked => nil,
161 :disabled => nil }
162 end
163
164 def test_get_edit_project_public_query
165 @request.session[:user_id] = 2
166 get :edit, :id => 1
167 assert_response :success
168 assert_template 'edit'
169 assert_tag :tag => 'input', :attributes => { :type => 'checkbox',
170 :name => 'query[is_public]',
171 :checked => 'checked'
172 }
173 assert_tag :tag => 'input', :attributes => { :type => 'checkbox',
174 :name => 'query_is_for_all',
175 :checked => nil,
176 :disabled => 'disabled' }
177 end
178
179 def test_destroy
180 @request.session[:user_id] = 2
181 post :destroy, :id => 1
182 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => 'ecookbook', :set_filter => 1, :query_id => nil
183 assert_nil Query.find_by_id(1)
184 end
185 end
@@ -73,6 +73,8 class IssuesController < ApplicationController
73 # Send html if the query is not valid
73 # Send html if the query is not valid
74 render(:template => 'issues/index.rhtml', :layout => !request.xhr?)
74 render(:template => 'issues/index.rhtml', :layout => !request.xhr?)
75 end
75 end
76 rescue ActiveRecord::RecordNotFound
77 render_404
76 end
78 end
77
79
78 def changes
80 def changes
@@ -87,6 +89,8 class IssuesController < ApplicationController
87 end
89 end
88 @title = (@project ? @project.name : Setting.app_title) + ": " + (@query.new_record? ? l(:label_changes_details) : @query.name)
90 @title = (@project ? @project.name : Setting.app_title) + ": " + (@query.new_record? ? l(:label_changes_details) : @query.name)
89 render :layout => false, :content_type => 'application/atom+xml'
91 render :layout => false, :content_type => 'application/atom+xml'
92 rescue ActiveRecord::RecordNotFound
93 render_404
90 end
94 end
91
95
92 def show
96 def show
@@ -384,7 +388,10 private
384 # Retrieve query from session or build a new query
388 # Retrieve query from session or build a new query
385 def retrieve_query
389 def retrieve_query
386 if !params[:query_id].blank?
390 if !params[:query_id].blank?
387 @query = Query.find(params[:query_id], :conditions => {:project_id => (@project ? @project.id : nil)})
391 cond = "project_id IS NULL"
392 cond << " OR project_id = #{@project.id}" if @project
393 @query = Query.find(params[:query_id], :conditions => cond)
394 @query.project = @project
388 session[:query] = {:id => @query.id, :project_id => @query.project_id}
395 session[:query] = {:id => @query.id, :project_id => @query.project_id}
389 else
396 else
390 if params[:set_filter] || session[:query].nil? || session[:query][:project_id] != (@project ? @project.id : nil)
397 if params[:set_filter] || session[:query].nil? || session[:query][:project_id] != (@project ? @project.id : nil)
@@ -18,19 +18,14
18 class QueriesController < ApplicationController
18 class QueriesController < ApplicationController
19 layout 'base'
19 layout 'base'
20 menu_item :issues
20 menu_item :issues
21 before_filter :find_project, :authorize
21 before_filter :find_query, :except => :new
22
22 before_filter :find_project, :authorize, :only => :new
23 def index
24 @queries = @project.queries.find(:all,
25 :order => "name ASC",
26 :conditions => ["is_public = ? or user_id = ?", true, (User.current.logged? ? User.current.id : 0)])
27 end
28
23
29 def new
24 def new
30 @query = Query.new(params[:query])
25 @query = Query.new(params[:query])
31 @query.project = @project
26 @query.project = params[:query_is_for_all] ? nil : @project
32 @query.user = User.current
27 @query.user = User.current
33 @query.is_public = false unless current_role.allowed_to?(:manage_public_queries)
28 @query.is_public = false unless (@query.project && current_role.allowed_to?(:manage_public_queries)) || User.current.admin?
34 @query.column_names = nil if params[:default_columns]
29 @query.column_names = nil if params[:default_columns]
35
30
36 params[:fields].each do |field|
31 params[:fields].each do |field|
@@ -52,7 +47,8 class QueriesController < ApplicationController
52 @query.add_filter(field, params[:operators][field], params[:values][field])
47 @query.add_filter(field, params[:operators][field], params[:values][field])
53 end if params[:fields]
48 end if params[:fields]
54 @query.attributes = params[:query]
49 @query.attributes = params[:query]
55 @query.is_public = false unless current_role.allowed_to?(:manage_public_queries)
50 @query.project = nil if params[:query_is_for_all]
51 @query.is_public = false unless (@query.project && current_role.allowed_to?(:manage_public_queries)) || User.current.admin?
56 @query.column_names = nil if params[:default_columns]
52 @query.column_names = nil if params[:default_columns]
57
53
58 if @query.save
54 if @query.save
@@ -64,18 +60,20 class QueriesController < ApplicationController
64
60
65 def destroy
61 def destroy
66 @query.destroy if request.post?
62 @query.destroy if request.post?
67 redirect_to :controller => 'queries', :project_id => @project
63 redirect_to :controller => 'issues', :action => 'index', :project_id => @project, :set_filter => 1
68 end
64 end
69
65
70 private
66 private
67 def find_query
68 @query = Query.find(params[:id])
69 @project = @query.project
70 render_403 unless @query.editable_by?(User.current)
71 rescue ActiveRecord::RecordNotFound
72 render_404
73 end
74
71 def find_project
75 def find_project
72 if params[:id]
76 @project = Project.find(params[:project_id])
73 @query = Query.find(params[:id])
74 @project = @query.project
75 render_403 unless @query.editable_by?(User.current)
76 else
77 @project = Project.find(params[:project_id])
78 end
79 rescue ActiveRecord::RecordNotFound
77 rescue ActiveRecord::RecordNotFound
80 render_404
78 render_404
81 end
79 end
@@ -32,6 +32,19 module IssuesHelper
32 "<strong>#{@cached_label_assigned_to}</strong>: #{issue.assigned_to}<br />" +
32 "<strong>#{@cached_label_assigned_to}</strong>: #{issue.assigned_to}<br />" +
33 "<strong>#{@cached_label_priority}</strong>: #{issue.priority.name}"
33 "<strong>#{@cached_label_priority}</strong>: #{issue.priority.name}"
34 end
34 end
35
36 def sidebar_queries
37 unless @sidebar_queries
38 # User can see public queries and his own queries
39 visible = ARCondition.new(["is_public = ? OR user_id = ?", true, (User.current.logged? ? User.current.id : 0)])
40 # Project specific queries and global queries
41 visible << (@project.nil? ? ["project_id IS NULL"] : ["project_id IS NULL OR project_id = ?", @project.id])
42 @sidebar_queries = Query.find(:all,
43 :order => "name ASC",
44 :conditions => visible.conditions)
45 end
46 @sidebar_queries
47 end
35
48
36 def show_detail(detail, no_html=false)
49 def show_detail(detail, no_html=false)
37 case detail.property
50 case detail.property
@@ -116,6 +116,11 class Query < ActiveRecord::Base
116 set_language_if_valid(User.current.language)
116 set_language_if_valid(User.current.language)
117 end
117 end
118
118
119 def after_initialize
120 # Store the fact that project is nil (used in #editable_by?)
121 @is_for_all = project.nil?
122 end
123
119 def validate
124 def validate
120 filters.each_key do |field|
125 filters.each_key do |field|
121 errors.add label_for(field), :activerecord_error_blank unless
126 errors.add label_for(field), :activerecord_error_blank unless
@@ -128,8 +133,10 class Query < ActiveRecord::Base
128
133
129 def editable_by?(user)
134 def editable_by?(user)
130 return false unless user
135 return false unless user
131 return true if !is_public && self.user_id == user.id
136 # Admin can edit them all and regular users can edit their private queries
132 is_public && user.allowed_to?(:manage_public_queries, project)
137 return true if user.admin? || (!is_public && self.user_id == user.id)
138 # Members can not edit public queries that are for all project (only admin is allowed to)
139 is_public && !@is_for_all && user.allowed_to?(:manage_public_queries, project)
133 end
140 end
134
141
135 def available_filters
142 def available_filters
@@ -1,13 +1,14
1 <% if @project %>
1 <h3><%= l(:label_issue_plural) %></h3>
2 <h3><%= l(:label_issue_plural) %></h3>
2 <%= link_to l(:label_issue_view_all), { :controller => 'issues', :action => 'index', :project_id => @project, :set_filter => 1 } %><br />
3 <%= link_to l(:label_issue_view_all), { :controller => 'issues', :action => 'index', :project_id => @project, :set_filter => 1 } %><br />
3 <%= link_to l(:field_summary), :controller => 'reports', :action => 'issue_report', :id => @project %><br />
4 <%= link_to l(:field_summary), :controller => 'reports', :action => 'issue_report', :id => @project %><br />
4 <%= link_to l(:label_change_log), :controller => 'projects', :action => 'changelog', :id => @project %>
5 <%= link_to l(:label_change_log), :controller => 'projects', :action => 'changelog', :id => @project %>
6 <% end %>
5
7
8 <% unless sidebar_queries.empty? -%>
6 <h3><%= l(:label_query_plural) %></h3>
9 <h3><%= l(:label_query_plural) %></h3>
7
10
8 <% queries = @project.queries.find(:all,
11 <% sidebar_queries.each do |query| -%>
9 :order => "name ASC",
10 :conditions => ["is_public = ? or user_id = ?", true, (User.current.logged? ? User.current.id : 0)])
11 queries.each do |query| %>
12 <%= link_to query.name, :controller => 'issues', :action => 'index', :project_id => @project, :query_id => query %><br />
12 <%= link_to query.name, :controller => 'issues', :action => 'index', :project_id => @project, :query_id => query %><br />
13 <% end %>
13 <% end -%>
14 <% end -%>
@@ -54,7 +54,7
54
54
55 <% content_for :sidebar do %>
55 <% content_for :sidebar do %>
56 <%= render :partial => 'issues/sidebar' %>
56 <%= render :partial => 'issues/sidebar' %>
57 <% end if @project%>
57 <% end %>
58
58
59 <% content_for :header_tags do %>
59 <% content_for :header_tags do %>
60 <%= auto_discovery_link_tag(:atom, {:query_id => @query, :format => 'atom', :page => nil, :key => User.current.rss_key}, :title => l(:label_issue_plural)) %>
60 <%= auto_discovery_link_tag(:atom, {:query_id => @query, :format => 'atom', :page => nil, :key => User.current.rss_key}, :title => l(:label_issue_plural)) %>
@@ -6,11 +6,16
6 <p><label for="query_name"><%=l(:field_name)%></label>
6 <p><label for="query_name"><%=l(:field_name)%></label>
7 <%= text_field 'query', 'name', :size => 80 %></p>
7 <%= text_field 'query', 'name', :size => 80 %></p>
8
8
9 <% if current_role.allowed_to?(:manage_public_queries) %>
9 <% if User.current.admin? || (@project && current_role.allowed_to?(:manage_public_queries)) %>
10 <p><label for="query_is_public"><%=l(:field_is_public)%></label>
10 <p><label for="query_is_public"><%=l(:field_is_public)%></label>
11 <%= check_box 'query', 'is_public' %></p>
11 <%= check_box 'query', 'is_public',
12 :onchange => (User.current.admin? ? nil : 'if (this.checked) {$("query_is_for_all").checked = false; $("query_is_for_all").disabled = true;} else {$("query_is_for_all").disabled = false;}') %></p>
12 <% end %>
13 <% end %>
13
14
15 <p><label for="query_is_for_all"><%=l(:field_is_for_all)%></label>
16 <%= check_box_tag 'query_is_for_all', 1, @query.project.nil?,
17 :disabled => (!@query.new_record? && (@query.project.nil? || (@query.is_public? && !User.current.admin?))) %></p>
18
14 <p><label for="query_default_columns"><%=l(:label_default_columns)%></label>
19 <p><label for="query_default_columns"><%=l(:label_default_columns)%></label>
15 <%= check_box_tag 'default_columns', 1, @query.has_default_columns?, :id => 'query_default_columns',
20 <%= check_box_tag 'default_columns', 1, @query.has_default_columns?, :id => 'query_default_columns',
16 :onclick => 'if (this.checked) {Element.hide("columns")} else {Element.show("columns")}' %></p>
21 :onclick => 'if (this.checked) {Element.hide("columns")} else {Element.show("columns")}' %></p>
@@ -1,7 +1,9
1 ---
1 ---
2 queries_001:
2 queries_001:
3 name: Multiple custom fields query
3 id: 1
4 project_id: 1
4 project_id: 1
5 is_public: true
6 name: Multiple custom fields query
5 filters: |
7 filters: |
6 ---
8 ---
7 cf_1:
9 cf_1:
@@ -17,6 +19,51 queries_001:
17 - "125"
19 - "125"
18 :operator: "="
20 :operator: "="
19
21
20 id: 1
21 is_public: true
22 user_id: 1
22 user_id: 1
23 column_names:
24 queries_002:
25 id: 2
26 project_id: 1
27 is_public: false
28 name: Private query for cookbook
29 filters: |
30 ---
31 tracker_id:
32 :values:
33 - "3"
34 :operator: "="
35 status_id:
36 :values:
37 - "1"
38 :operator: o
39
40 user_id: 3
41 column_names:
42 queries_003:
43 id: 3
44 project_id:
45 is_public: false
46 name: Private query for all projects
47 filters: |
48 ---
49 tracker_id:
50 :values:
51 - "3"
52 :operator: "="
53
54 user_id: 3
55 column_names:
56 queries_004:
57 id: 4
58 project_id:
59 is_public: true
60 name: Public query for all projects
61 filters: |
62 ---
63 tracker_id:
64 :values:
65 - "3"
66 :operator: "="
67
68 user_id: 2
69 column_names:
@@ -57,7 +57,6 roles_002:
57 - :add_issue_notes
57 - :add_issue_notes
58 - :move_issues
58 - :move_issues
59 - :delete_issues
59 - :delete_issues
60 - :manage_public_queries
61 - :save_queries
60 - :save_queries
62 - :view_gantt
61 - :view_gantt
63 - :view_calendar
62 - :view_calendar
@@ -94,7 +93,6 roles_003:
94 - :manage_issue_relations
93 - :manage_issue_relations
95 - :add_issue_notes
94 - :add_issue_notes
96 - :move_issues
95 - :move_issues
97 - :manage_public_queries
98 - :save_queries
96 - :save_queries
99 - :view_gantt
97 - :view_gantt
100 - :view_calendar
98 - :view_calendar
@@ -1,5 +1,5
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2008 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
@@ -18,7 +18,7
18 require File.dirname(__FILE__) + '/../test_helper'
18 require File.dirname(__FILE__) + '/../test_helper'
19
19
20 class QueryTest < Test::Unit::TestCase
20 class QueryTest < Test::Unit::TestCase
21 fixtures :projects, :users, :trackers, :issue_statuses, :issue_categories, :enumerations, :issues, :custom_fields, :custom_values, :queries
21 fixtures :projects, :users, :members, :roles, :trackers, :issue_statuses, :issue_categories, :enumerations, :issues, :custom_fields, :custom_values, :queries
22
22
23 def test_query_with_multiple_custom_fields
23 def test_query_with_multiple_custom_fields
24 query = Query.find(1)
24 query = Query.find(1)
@@ -41,4 +41,34 class QueryTest < Test::Unit::TestCase
41 c = q.columns.first
41 c = q.columns.first
42 assert q.has_column?(c)
42 assert q.has_column?(c)
43 end
43 end
44
45 def test_editable_by
46 admin = User.find(1)
47 manager = User.find(2)
48 developer = User.find(3)
49
50 # Public query on project 1
51 q = Query.find(1)
52 assert q.editable_by?(admin)
53 assert q.editable_by?(manager)
54 assert !q.editable_by?(developer)
55
56 # Private query on project 1
57 q = Query.find(2)
58 assert q.editable_by?(admin)
59 assert !q.editable_by?(manager)
60 assert q.editable_by?(developer)
61
62 # Private query for all projects
63 q = Query.find(3)
64 assert q.editable_by?(admin)
65 assert !q.editable_by?(manager)
66 assert q.editable_by?(developer)
67
68 # Public query for all projects
69 q = Query.find(4)
70 assert q.editable_by?(admin)
71 assert !q.editable_by?(manager)
72 assert !q.editable_by?(developer)
73 end
44 end
74 end
General Comments 0
You need to be logged in to leave comments. Login now