##// END OF EJS Templates
Added per user custom queries....
Jean-Philippe Lang -
r563:1c44600c62dc
parent child
Show More
@@ -0,0 +1,29
1 <div class="contextual">
2 <% if loggedin? %>
3 <%= link_to l(:label_query_new), {:controller => 'queries', :action => 'new', :project_id => @project}, :class => 'icon icon-add' %>
4 <% end %>
5 </div>
6
7 <h2><%= l(:label_query_plural) %></h2>
8
9 <% if @queries.empty? %>
10 <p><i><%=l(:label_no_data)%></i></p>
11 <% else %>
12 <table class="list">
13 <% @queries.each do |query| %>
14 <tr class="<%= cycle('odd', 'even') %>">
15 <td>
16 <%= link_to query.name, :controller => 'projects', :action => 'list_issues', :id => @project, :query_id => query %>
17 </td>
18 <td align="right">
19 <small>
20 <% if query.editable_by?(@logged_in_user) %>
21 <%= link_to l(:button_edit), {:controller => 'queries', :action => 'edit', :id => query}, :class => 'icon icon-edit' %>
22 <%= link_to l(:button_delete), {:controller => 'queries', :action => 'destroy', :id => query}, :confirm => l(:text_are_you_sure), :method => :post, :class => 'icon icon-del' %>
23 </small>
24 <% end %>
25 </td>
26 </tr>
27 <% end %>
28 </table>
29 <% end %>
@@ -0,0 +1,6
1 <h2><%= l(:label_query_new) %></h2>
2
3 <% form_tag({:action => 'new', :project_id => @query.project}) do %>
4 <%= render :partial => 'form', :locals => {:query => @query} %>
5 <%= submit_tag l(:button_save) %>
6 <% end %>
@@ -1,684 +1,667
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2007 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
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
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
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.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require 'csv'
18 require 'csv'
19
19
20 class ProjectsController < ApplicationController
20 class ProjectsController < ApplicationController
21 layout 'base'
21 layout 'base'
22 before_filter :find_project, :except => [ :index, :list, :add ]
22 before_filter :find_project, :except => [ :index, :list, :add ]
23 before_filter :authorize, :except => [ :index, :list, :add, :archive, :unarchive, :destroy ]
23 before_filter :authorize, :except => [ :index, :list, :add, :archive, :unarchive, :destroy ]
24 before_filter :require_admin, :only => [ :add, :archive, :unarchive, :destroy ]
24 before_filter :require_admin, :only => [ :add, :archive, :unarchive, :destroy ]
25
25
26 cache_sweeper :project_sweeper, :only => [ :add, :edit, :archive, :unarchive, :destroy ]
26 cache_sweeper :project_sweeper, :only => [ :add, :edit, :archive, :unarchive, :destroy ]
27 cache_sweeper :issue_sweeper, :only => [ :add_issue ]
27 cache_sweeper :issue_sweeper, :only => [ :add_issue ]
28
28
29 helper :sort
29 helper :sort
30 include SortHelper
30 include SortHelper
31 helper :custom_fields
31 helper :custom_fields
32 include CustomFieldsHelper
32 include CustomFieldsHelper
33 helper :ifpdf
33 helper :ifpdf
34 include IfpdfHelper
34 include IfpdfHelper
35 helper IssuesHelper
35 helper IssuesHelper
36 helper :queries
36 helper :queries
37 include QueriesHelper
37 include QueriesHelper
38 helper :repositories
38 helper :repositories
39 include RepositoriesHelper
39 include RepositoriesHelper
40
40
41 def index
41 def index
42 list
42 list
43 render :action => 'list' unless request.xhr?
43 render :action => 'list' unless request.xhr?
44 end
44 end
45
45
46 # Lists public projects
46 # Lists public projects
47 def list
47 def list
48 sort_init "#{Project.table_name}.name", "asc"
48 sort_init "#{Project.table_name}.name", "asc"
49 sort_update
49 sort_update
50 @project_count = Project.count(:all, :conditions => Project.visible_by(logged_in_user))
50 @project_count = Project.count(:all, :conditions => Project.visible_by(logged_in_user))
51 @project_pages = Paginator.new self, @project_count,
51 @project_pages = Paginator.new self, @project_count,
52 15,
52 15,
53 params['page']
53 params['page']
54 @projects = Project.find :all, :order => sort_clause,
54 @projects = Project.find :all, :order => sort_clause,
55 :conditions => Project.visible_by(logged_in_user),
55 :conditions => Project.visible_by(logged_in_user),
56 :include => :parent,
56 :include => :parent,
57 :limit => @project_pages.items_per_page,
57 :limit => @project_pages.items_per_page,
58 :offset => @project_pages.current.offset
58 :offset => @project_pages.current.offset
59
59
60 render :action => "list", :layout => false if request.xhr?
60 render :action => "list", :layout => false if request.xhr?
61 end
61 end
62
62
63 # Add a new project
63 # Add a new project
64 def add
64 def add
65 @custom_fields = IssueCustomField.find(:all)
65 @custom_fields = IssueCustomField.find(:all)
66 @root_projects = Project.find(:all, :conditions => "parent_id is null")
66 @root_projects = Project.find(:all, :conditions => "parent_id is null")
67 @project = Project.new(params[:project])
67 @project = Project.new(params[:project])
68 if request.get?
68 if request.get?
69 @custom_values = ProjectCustomField.find(:all).collect { |x| CustomValue.new(:custom_field => x, :customized => @project) }
69 @custom_values = ProjectCustomField.find(:all).collect { |x| CustomValue.new(:custom_field => x, :customized => @project) }
70 else
70 else
71 @project.custom_fields = CustomField.find(params[:custom_field_ids]) if params[:custom_field_ids]
71 @project.custom_fields = CustomField.find(params[:custom_field_ids]) if params[:custom_field_ids]
72 @custom_values = ProjectCustomField.find(:all).collect { |x| CustomValue.new(:custom_field => x, :customized => @project, :value => params["custom_fields"][x.id.to_s]) }
72 @custom_values = ProjectCustomField.find(:all).collect { |x| CustomValue.new(:custom_field => x, :customized => @project, :value => params["custom_fields"][x.id.to_s]) }
73 @project.custom_values = @custom_values
73 @project.custom_values = @custom_values
74 if params[:repository_enabled] && params[:repository_enabled] == "1"
74 if params[:repository_enabled] && params[:repository_enabled] == "1"
75 @project.repository = Repository.factory(params[:repository_scm])
75 @project.repository = Repository.factory(params[:repository_scm])
76 @project.repository.attributes = params[:repository]
76 @project.repository.attributes = params[:repository]
77 end
77 end
78 if "1" == params[:wiki_enabled]
78 if "1" == params[:wiki_enabled]
79 @project.wiki = Wiki.new
79 @project.wiki = Wiki.new
80 @project.wiki.attributes = params[:wiki]
80 @project.wiki.attributes = params[:wiki]
81 end
81 end
82 if @project.save
82 if @project.save
83 flash[:notice] = l(:notice_successful_create)
83 flash[:notice] = l(:notice_successful_create)
84 redirect_to :controller => 'admin', :action => 'projects'
84 redirect_to :controller => 'admin', :action => 'projects'
85 end
85 end
86 end
86 end
87 end
87 end
88
88
89 # Show @project
89 # Show @project
90 def show
90 def show
91 @custom_values = @project.custom_values.find(:all, :include => :custom_field)
91 @custom_values = @project.custom_values.find(:all, :include => :custom_field)
92 @members_by_role = @project.members.find(:all, :include => [:user, :role], :order => 'position').group_by {|m| m.role}
92 @members_by_role = @project.members.find(:all, :include => [:user, :role], :order => 'position').group_by {|m| m.role}
93 @subprojects = @project.active_children
93 @subprojects = @project.active_children
94 @news = @project.news.find(:all, :limit => 5, :include => [ :author, :project ], :order => "#{News.table_name}.created_on DESC")
94 @news = @project.news.find(:all, :limit => 5, :include => [ :author, :project ], :order => "#{News.table_name}.created_on DESC")
95 @trackers = Tracker.find(:all, :order => 'position')
95 @trackers = Tracker.find(:all, :order => 'position')
96 @open_issues_by_tracker = Issue.count(:group => :tracker, :joins => "INNER JOIN #{IssueStatus.table_name} ON #{IssueStatus.table_name}.id = #{Issue.table_name}.status_id", :conditions => ["project_id=? and #{IssueStatus.table_name}.is_closed=?", @project.id, false])
96 @open_issues_by_tracker = Issue.count(:group => :tracker, :joins => "INNER JOIN #{IssueStatus.table_name} ON #{IssueStatus.table_name}.id = #{Issue.table_name}.status_id", :conditions => ["project_id=? and #{IssueStatus.table_name}.is_closed=?", @project.id, false])
97 @total_issues_by_tracker = Issue.count(:group => :tracker, :conditions => ["project_id=?", @project.id])
97 @total_issues_by_tracker = Issue.count(:group => :tracker, :conditions => ["project_id=?", @project.id])
98 end
98 end
99
99
100 def settings
100 def settings
101 @root_projects = Project::find(:all, :conditions => ["parent_id is null and id <> ?", @project.id])
101 @root_projects = Project::find(:all, :conditions => ["parent_id is null and id <> ?", @project.id])
102 @custom_fields = IssueCustomField.find(:all)
102 @custom_fields = IssueCustomField.find(:all)
103 @issue_category ||= IssueCategory.new
103 @issue_category ||= IssueCategory.new
104 @member ||= @project.members.new
104 @member ||= @project.members.new
105 @custom_values ||= ProjectCustomField.find(:all).collect { |x| @project.custom_values.find_by_custom_field_id(x.id) || CustomValue.new(:custom_field => x) }
105 @custom_values ||= ProjectCustomField.find(:all).collect { |x| @project.custom_values.find_by_custom_field_id(x.id) || CustomValue.new(:custom_field => x) }
106 end
106 end
107
107
108 # Edit @project
108 # Edit @project
109 def edit
109 def edit
110 if request.post?
110 if request.post?
111 @project.custom_fields = IssueCustomField.find(params[:custom_field_ids]) if params[:custom_field_ids]
111 @project.custom_fields = IssueCustomField.find(params[:custom_field_ids]) if params[:custom_field_ids]
112 if params[:custom_fields]
112 if params[:custom_fields]
113 @custom_values = ProjectCustomField.find(:all).collect { |x| CustomValue.new(:custom_field => x, :customized => @project, :value => params["custom_fields"][x.id.to_s]) }
113 @custom_values = ProjectCustomField.find(:all).collect { |x| CustomValue.new(:custom_field => x, :customized => @project, :value => params["custom_fields"][x.id.to_s]) }
114 @project.custom_values = @custom_values
114 @project.custom_values = @custom_values
115 end
115 end
116 if params[:repository_enabled]
116 if params[:repository_enabled]
117 case params[:repository_enabled]
117 case params[:repository_enabled]
118 when "0"
118 when "0"
119 @project.repository = nil
119 @project.repository = nil
120 when "1"
120 when "1"
121 @project.repository ||= Repository.factory(params[:repository_scm])
121 @project.repository ||= Repository.factory(params[:repository_scm])
122 @project.repository.update_attributes params[:repository] if @project.repository
122 @project.repository.update_attributes params[:repository] if @project.repository
123 end
123 end
124 end
124 end
125 if params[:wiki_enabled]
125 if params[:wiki_enabled]
126 case params[:wiki_enabled]
126 case params[:wiki_enabled]
127 when "0"
127 when "0"
128 @project.wiki.destroy if @project.wiki
128 @project.wiki.destroy if @project.wiki
129 when "1"
129 when "1"
130 @project.wiki ||= Wiki.new
130 @project.wiki ||= Wiki.new
131 @project.wiki.update_attributes params[:wiki]
131 @project.wiki.update_attributes params[:wiki]
132 end
132 end
133 end
133 end
134 @project.attributes = params[:project]
134 @project.attributes = params[:project]
135 if @project.save
135 if @project.save
136 flash[:notice] = l(:notice_successful_update)
136 flash[:notice] = l(:notice_successful_update)
137 redirect_to :action => 'settings', :id => @project
137 redirect_to :action => 'settings', :id => @project
138 else
138 else
139 settings
139 settings
140 render :action => 'settings'
140 render :action => 'settings'
141 end
141 end
142 end
142 end
143 end
143 end
144
144
145 def archive
145 def archive
146 @project.archive if request.post? && @project.active?
146 @project.archive if request.post? && @project.active?
147 redirect_to :controller => 'admin', :action => 'projects'
147 redirect_to :controller => 'admin', :action => 'projects'
148 end
148 end
149
149
150 def unarchive
150 def unarchive
151 @project.unarchive if request.post? && !@project.active?
151 @project.unarchive if request.post? && !@project.active?
152 redirect_to :controller => 'admin', :action => 'projects'
152 redirect_to :controller => 'admin', :action => 'projects'
153 end
153 end
154
154
155 # Delete @project
155 # Delete @project
156 def destroy
156 def destroy
157 @project_to_destroy = @project
157 @project_to_destroy = @project
158 if request.post? and params[:confirm]
158 if request.post? and params[:confirm]
159 @project_to_destroy.destroy
159 @project_to_destroy.destroy
160 redirect_to :controller => 'admin', :action => 'projects'
160 redirect_to :controller => 'admin', :action => 'projects'
161 end
161 end
162 # hide project in layout
162 # hide project in layout
163 @project = nil
163 @project = nil
164 end
164 end
165
165
166 # Add a new issue category to @project
166 # Add a new issue category to @project
167 def add_issue_category
167 def add_issue_category
168 if request.post?
168 if request.post?
169 @issue_category = @project.issue_categories.build(params[:issue_category])
169 @issue_category = @project.issue_categories.build(params[:issue_category])
170 if @issue_category.save
170 if @issue_category.save
171 flash[:notice] = l(:notice_successful_create)
171 flash[:notice] = l(:notice_successful_create)
172 redirect_to :action => 'settings', :tab => 'categories', :id => @project
172 redirect_to :action => 'settings', :tab => 'categories', :id => @project
173 else
173 else
174 settings
174 settings
175 render :action => 'settings'
175 render :action => 'settings'
176 end
176 end
177 end
177 end
178 end
178 end
179
179
180 # Add a new version to @project
180 # Add a new version to @project
181 def add_version
181 def add_version
182 @version = @project.versions.build(params[:version])
182 @version = @project.versions.build(params[:version])
183 if request.post? and @version.save
183 if request.post? and @version.save
184 flash[:notice] = l(:notice_successful_create)
184 flash[:notice] = l(:notice_successful_create)
185 redirect_to :action => 'settings', :tab => 'versions', :id => @project
185 redirect_to :action => 'settings', :tab => 'versions', :id => @project
186 end
186 end
187 end
187 end
188
188
189 # Add a new member to @project
189 # Add a new member to @project
190 def add_member
190 def add_member
191 @member = @project.members.build(params[:member])
191 @member = @project.members.build(params[:member])
192 if request.post? && @member.save
192 if request.post? && @member.save
193 respond_to do |format|
193 respond_to do |format|
194 format.html { redirect_to :action => 'settings', :tab => 'members', :id => @project }
194 format.html { redirect_to :action => 'settings', :tab => 'members', :id => @project }
195 format.js { render(:update) {|page| page.replace_html "tab-content-members", :partial => 'members'} }
195 format.js { render(:update) {|page| page.replace_html "tab-content-members", :partial => 'members'} }
196 end
196 end
197 else
197 else
198 settings
198 settings
199 render :action => 'settings'
199 render :action => 'settings'
200 end
200 end
201 end
201 end
202
202
203 # Show members list of @project
203 # Show members list of @project
204 def list_members
204 def list_members
205 @members = @project.members.find(:all)
205 @members = @project.members.find(:all)
206 end
206 end
207
207
208 # Add a new document to @project
208 # Add a new document to @project
209 def add_document
209 def add_document
210 @categories = Enumeration::get_values('DCAT')
210 @categories = Enumeration::get_values('DCAT')
211 @document = @project.documents.build(params[:document])
211 @document = @project.documents.build(params[:document])
212 if request.post? and @document.save
212 if request.post? and @document.save
213 # Save the attachments
213 # Save the attachments
214 params[:attachments].each { |a|
214 params[:attachments].each { |a|
215 Attachment.create(:container => @document, :file => a, :author => logged_in_user) unless a.size == 0
215 Attachment.create(:container => @document, :file => a, :author => logged_in_user) unless a.size == 0
216 } if params[:attachments] and params[:attachments].is_a? Array
216 } if params[:attachments] and params[:attachments].is_a? Array
217 flash[:notice] = l(:notice_successful_create)
217 flash[:notice] = l(:notice_successful_create)
218 Mailer.deliver_document_add(@document) if Permission.find_by_controller_and_action(params[:controller], params[:action]).mail_enabled?
218 Mailer.deliver_document_add(@document) if Permission.find_by_controller_and_action(params[:controller], params[:action]).mail_enabled?
219 redirect_to :action => 'list_documents', :id => @project
219 redirect_to :action => 'list_documents', :id => @project
220 end
220 end
221 end
221 end
222
222
223 # Show documents list of @project
223 # Show documents list of @project
224 def list_documents
224 def list_documents
225 @documents = @project.documents.find :all, :include => :category
225 @documents = @project.documents.find :all, :include => :category
226 end
226 end
227
227
228 # Add a new issue to @project
228 # Add a new issue to @project
229 def add_issue
229 def add_issue
230 @tracker = Tracker.find(params[:tracker_id])
230 @tracker = Tracker.find(params[:tracker_id])
231 @priorities = Enumeration::get_values('IPRI')
231 @priorities = Enumeration::get_values('IPRI')
232
232
233 default_status = IssueStatus.default
233 default_status = IssueStatus.default
234 unless default_status
234 unless default_status
235 flash.now[:notice] = 'No default issue status defined. Please check your configuration.'
235 flash.now[:notice] = 'No default issue status defined. Please check your configuration.'
236 render :nothing => true, :layout => true
236 render :nothing => true, :layout => true
237 return
237 return
238 end
238 end
239 @issue = Issue.new(:project => @project, :tracker => @tracker)
239 @issue = Issue.new(:project => @project, :tracker => @tracker)
240 @issue.status = default_status
240 @issue.status = default_status
241 @allowed_statuses = ([default_status] + default_status.find_new_statuses_allowed_to(logged_in_user.role_for_project(@project), @issue.tracker))if logged_in_user
241 @allowed_statuses = ([default_status] + default_status.find_new_statuses_allowed_to(logged_in_user.role_for_project(@project), @issue.tracker))if logged_in_user
242 if request.get?
242 if request.get?
243 @issue.start_date = Date.today
243 @issue.start_date = Date.today
244 @custom_values = @project.custom_fields_for_issues(@tracker).collect { |x| CustomValue.new(:custom_field => x, :customized => @issue) }
244 @custom_values = @project.custom_fields_for_issues(@tracker).collect { |x| CustomValue.new(:custom_field => x, :customized => @issue) }
245 else
245 else
246 @issue.attributes = params[:issue]
246 @issue.attributes = params[:issue]
247
247
248 requested_status = IssueStatus.find_by_id(params[:issue][:status_id])
248 requested_status = IssueStatus.find_by_id(params[:issue][:status_id])
249 @issue.status = (@allowed_statuses.include? requested_status) ? requested_status : default_status
249 @issue.status = (@allowed_statuses.include? requested_status) ? requested_status : default_status
250
250
251 @issue.author_id = self.logged_in_user.id if self.logged_in_user
251 @issue.author_id = self.logged_in_user.id if self.logged_in_user
252 # Multiple file upload
252 # Multiple file upload
253 @attachments = []
253 @attachments = []
254 params[:attachments].each { |a|
254 params[:attachments].each { |a|
255 @attachments << Attachment.new(:container => @issue, :file => a, :author => logged_in_user) unless a.size == 0
255 @attachments << Attachment.new(:container => @issue, :file => a, :author => logged_in_user) unless a.size == 0
256 } if params[:attachments] and params[:attachments].is_a? Array
256 } if params[:attachments] and params[:attachments].is_a? Array
257 @custom_values = @project.custom_fields_for_issues(@tracker).collect { |x| CustomValue.new(:custom_field => x, :customized => @issue, :value => params["custom_fields"][x.id.to_s]) }
257 @custom_values = @project.custom_fields_for_issues(@tracker).collect { |x| CustomValue.new(:custom_field => x, :customized => @issue, :value => params["custom_fields"][x.id.to_s]) }
258 @issue.custom_values = @custom_values
258 @issue.custom_values = @custom_values
259 if @issue.save
259 if @issue.save
260 @attachments.each(&:save)
260 @attachments.each(&:save)
261 flash[:notice] = l(:notice_successful_create)
261 flash[:notice] = l(:notice_successful_create)
262 Mailer.deliver_issue_add(@issue) if Permission.find_by_controller_and_action(params[:controller], params[:action]).mail_enabled?
262 Mailer.deliver_issue_add(@issue) if Permission.find_by_controller_and_action(params[:controller], params[:action]).mail_enabled?
263 redirect_to :action => 'list_issues', :id => @project
263 redirect_to :action => 'list_issues', :id => @project
264 end
264 end
265 end
265 end
266 end
266 end
267
267
268 # Show filtered/sorted issues list of @project
268 # Show filtered/sorted issues list of @project
269 def list_issues
269 def list_issues
270 sort_init "#{Issue.table_name}.id", "desc"
270 sort_init "#{Issue.table_name}.id", "desc"
271 sort_update
271 sort_update
272
272
273 retrieve_query
273 retrieve_query
274
274
275 @results_per_page_options = [ 15, 25, 50, 100 ]
275 @results_per_page_options = [ 15, 25, 50, 100 ]
276 if params[:per_page] and @results_per_page_options.include? params[:per_page].to_i
276 if params[:per_page] and @results_per_page_options.include? params[:per_page].to_i
277 @results_per_page = params[:per_page].to_i
277 @results_per_page = params[:per_page].to_i
278 session[:results_per_page] = @results_per_page
278 session[:results_per_page] = @results_per_page
279 else
279 else
280 @results_per_page = session[:results_per_page] || 25
280 @results_per_page = session[:results_per_page] || 25
281 end
281 end
282
282
283 if @query.valid?
283 if @query.valid?
284 @issue_count = Issue.count(:include => [:status, :project, :custom_values], :conditions => @query.statement)
284 @issue_count = Issue.count(:include => [:status, :project, :custom_values], :conditions => @query.statement)
285 @issue_pages = Paginator.new self, @issue_count, @results_per_page, params['page']
285 @issue_pages = Paginator.new self, @issue_count, @results_per_page, params['page']
286 @issues = Issue.find :all, :order => sort_clause,
286 @issues = Issue.find :all, :order => sort_clause,
287 :include => [ :assigned_to, :status, :tracker, :project, :priority, :custom_values ],
287 :include => [ :assigned_to, :status, :tracker, :project, :priority, :custom_values ],
288 :conditions => @query.statement,
288 :conditions => @query.statement,
289 :limit => @issue_pages.items_per_page,
289 :limit => @issue_pages.items_per_page,
290 :offset => @issue_pages.current.offset
290 :offset => @issue_pages.current.offset
291 end
291 end
292 @trackers = Tracker.find :all, :order => 'position'
293 render :layout => false if request.xhr?
292 render :layout => false if request.xhr?
294 end
293 end
295
294
296 # Export filtered/sorted issues list to CSV
295 # Export filtered/sorted issues list to CSV
297 def export_issues_csv
296 def export_issues_csv
298 sort_init "#{Issue.table_name}.id", "desc"
297 sort_init "#{Issue.table_name}.id", "desc"
299 sort_update
298 sort_update
300
299
301 retrieve_query
300 retrieve_query
302 render :action => 'list_issues' and return unless @query.valid?
301 render :action => 'list_issues' and return unless @query.valid?
303
302
304 @issues = Issue.find :all, :order => sort_clause,
303 @issues = Issue.find :all, :order => sort_clause,
305 :include => [ :assigned_to, :author, :status, :tracker, :priority, :project, {:custom_values => :custom_field} ],
304 :include => [ :assigned_to, :author, :status, :tracker, :priority, :project, {:custom_values => :custom_field} ],
306 :conditions => @query.statement,
305 :conditions => @query.statement,
307 :limit => Setting.issues_export_limit.to_i
306 :limit => Setting.issues_export_limit.to_i
308
307
309 ic = Iconv.new(l(:general_csv_encoding), 'UTF-8')
308 ic = Iconv.new(l(:general_csv_encoding), 'UTF-8')
310 export = StringIO.new
309 export = StringIO.new
311 CSV::Writer.generate(export, l(:general_csv_separator)) do |csv|
310 CSV::Writer.generate(export, l(:general_csv_separator)) do |csv|
312 # csv header fields
311 # csv header fields
313 headers = [ "#", l(:field_status),
312 headers = [ "#", l(:field_status),
314 l(:field_project),
313 l(:field_project),
315 l(:field_tracker),
314 l(:field_tracker),
316 l(:field_priority),
315 l(:field_priority),
317 l(:field_subject),
316 l(:field_subject),
318 l(:field_assigned_to),
317 l(:field_assigned_to),
319 l(:field_author),
318 l(:field_author),
320 l(:field_start_date),
319 l(:field_start_date),
321 l(:field_due_date),
320 l(:field_due_date),
322 l(:field_done_ratio),
321 l(:field_done_ratio),
323 l(:field_created_on),
322 l(:field_created_on),
324 l(:field_updated_on)
323 l(:field_updated_on)
325 ]
324 ]
326 for custom_field in @project.all_custom_fields
325 for custom_field in @project.all_custom_fields
327 headers << custom_field.name
326 headers << custom_field.name
328 end
327 end
329 csv << headers.collect {|c| ic.iconv(c) }
328 csv << headers.collect {|c| ic.iconv(c) }
330 # csv lines
329 # csv lines
331 @issues.each do |issue|
330 @issues.each do |issue|
332 fields = [issue.id, issue.status.name,
331 fields = [issue.id, issue.status.name,
333 issue.project.name,
332 issue.project.name,
334 issue.tracker.name,
333 issue.tracker.name,
335 issue.priority.name,
334 issue.priority.name,
336 issue.subject,
335 issue.subject,
337 (issue.assigned_to ? issue.assigned_to.name : ""),
336 (issue.assigned_to ? issue.assigned_to.name : ""),
338 issue.author.name,
337 issue.author.name,
339 issue.start_date ? l_date(issue.start_date) : nil,
338 issue.start_date ? l_date(issue.start_date) : nil,
340 issue.due_date ? l_date(issue.due_date) : nil,
339 issue.due_date ? l_date(issue.due_date) : nil,
341 issue.done_ratio,
340 issue.done_ratio,
342 l_datetime(issue.created_on),
341 l_datetime(issue.created_on),
343 l_datetime(issue.updated_on)
342 l_datetime(issue.updated_on)
344 ]
343 ]
345 for custom_field in @project.all_custom_fields
344 for custom_field in @project.all_custom_fields
346 fields << (show_value issue.custom_value_for(custom_field))
345 fields << (show_value issue.custom_value_for(custom_field))
347 end
346 end
348 csv << fields.collect {|c| ic.iconv(c.to_s) }
347 csv << fields.collect {|c| ic.iconv(c.to_s) }
349 end
348 end
350 end
349 end
351 export.rewind
350 export.rewind
352 send_data(export.read, :type => 'text/csv; header=present', :filename => 'export.csv')
351 send_data(export.read, :type => 'text/csv; header=present', :filename => 'export.csv')
353 end
352 end
354
353
355 # Export filtered/sorted issues to PDF
354 # Export filtered/sorted issues to PDF
356 def export_issues_pdf
355 def export_issues_pdf
357 sort_init "#{Issue.table_name}.id", "desc"
356 sort_init "#{Issue.table_name}.id", "desc"
358 sort_update
357 sort_update
359
358
360 retrieve_query
359 retrieve_query
361 render :action => 'list_issues' and return unless @query.valid?
360 render :action => 'list_issues' and return unless @query.valid?
362
361
363 @issues = Issue.find :all, :order => sort_clause,
362 @issues = Issue.find :all, :order => sort_clause,
364 :include => [ :author, :status, :tracker, :priority, :project, :custom_values ],
363 :include => [ :author, :status, :tracker, :priority, :project, :custom_values ],
365 :conditions => @query.statement,
364 :conditions => @query.statement,
366 :limit => Setting.issues_export_limit.to_i
365 :limit => Setting.issues_export_limit.to_i
367
366
368 @options_for_rfpdf ||= {}
367 @options_for_rfpdf ||= {}
369 @options_for_rfpdf[:file_name] = "export.pdf"
368 @options_for_rfpdf[:file_name] = "export.pdf"
370 render :layout => false
369 render :layout => false
371 end
370 end
372
371
373 def move_issues
372 def move_issues
374 @issues = @project.issues.find(params[:issue_ids]) if params[:issue_ids]
373 @issues = @project.issues.find(params[:issue_ids]) if params[:issue_ids]
375 redirect_to :action => 'list_issues', :id => @project and return unless @issues
374 redirect_to :action => 'list_issues', :id => @project and return unless @issues
376 @projects = []
375 @projects = []
377 # find projects to which the user is allowed to move the issue
376 # find projects to which the user is allowed to move the issue
378 @logged_in_user.memberships.each {|m| @projects << m.project if Permission.allowed_to_role("projects/move_issues", m.role)}
377 @logged_in_user.memberships.each {|m| @projects << m.project if Permission.allowed_to_role("projects/move_issues", m.role)}
379 # issue can be moved to any tracker
378 # issue can be moved to any tracker
380 @trackers = Tracker.find(:all)
379 @trackers = Tracker.find(:all)
381 if request.post? and params[:new_project_id] and params[:new_tracker_id]
380 if request.post? and params[:new_project_id] and params[:new_tracker_id]
382 new_project = Project.find(params[:new_project_id])
381 new_project = Project.find(params[:new_project_id])
383 new_tracker = Tracker.find(params[:new_tracker_id])
382 new_tracker = Tracker.find(params[:new_tracker_id])
384 @issues.each { |i|
383 @issues.each { |i|
385 # project dependent properties
384 # project dependent properties
386 unless i.project_id == new_project.id
385 unless i.project_id == new_project.id
387 i.category = nil
386 i.category = nil
388 i.fixed_version = nil
387 i.fixed_version = nil
389 # delete issue relations
388 # delete issue relations
390 i.relations_from.clear
389 i.relations_from.clear
391 i.relations_to.clear
390 i.relations_to.clear
392 end
391 end
393 # move the issue
392 # move the issue
394 i.project = new_project
393 i.project = new_project
395 i.tracker = new_tracker
394 i.tracker = new_tracker
396 i.save
395 i.save
397 }
396 }
398 flash[:notice] = l(:notice_successful_update)
397 flash[:notice] = l(:notice_successful_update)
399 redirect_to :action => 'list_issues', :id => @project
398 redirect_to :action => 'list_issues', :id => @project
400 end
399 end
401 end
400 end
402
401
403 def add_query
404 @query = Query.new(params[:query])
405 @query.project = @project
406 @query.user = logged_in_user
407
408 params[:fields].each do |field|
409 @query.add_filter(field, params[:operators][field], params[:values][field])
410 end if params[:fields]
411
412 if request.post? and @query.save
413 flash[:notice] = l(:notice_successful_create)
414 redirect_to :controller => 'reports', :action => 'issue_report', :id => @project
415 end
416 render :layout => false if request.xhr?
417 end
418
419 # Add a news to @project
402 # Add a news to @project
420 def add_news
403 def add_news
421 @news = News.new(:project => @project)
404 @news = News.new(:project => @project)
422 if request.post?
405 if request.post?
423 @news.attributes = params[:news]
406 @news.attributes = params[:news]
424 @news.author_id = self.logged_in_user.id if self.logged_in_user
407 @news.author_id = self.logged_in_user.id if self.logged_in_user
425 if @news.save
408 if @news.save
426 flash[:notice] = l(:notice_successful_create)
409 flash[:notice] = l(:notice_successful_create)
427 redirect_to :action => 'list_news', :id => @project
410 redirect_to :action => 'list_news', :id => @project
428 end
411 end
429 end
412 end
430 end
413 end
431
414
432 # Show news list of @project
415 # Show news list of @project
433 def list_news
416 def list_news
434 @news_pages, @news = paginate :news, :per_page => 10, :conditions => ["project_id=?", @project.id], :include => :author, :order => "#{News.table_name}.created_on DESC"
417 @news_pages, @news = paginate :news, :per_page => 10, :conditions => ["project_id=?", @project.id], :include => :author, :order => "#{News.table_name}.created_on DESC"
435 render :action => "list_news", :layout => false if request.xhr?
418 render :action => "list_news", :layout => false if request.xhr?
436 end
419 end
437
420
438 def add_file
421 def add_file
439 if request.post?
422 if request.post?
440 @version = @project.versions.find_by_id(params[:version_id])
423 @version = @project.versions.find_by_id(params[:version_id])
441 # Save the attachments
424 # Save the attachments
442 @attachments = []
425 @attachments = []
443 params[:attachments].each { |file|
426 params[:attachments].each { |file|
444 next unless file.size > 0
427 next unless file.size > 0
445 a = Attachment.create(:container => @version, :file => file, :author => logged_in_user)
428 a = Attachment.create(:container => @version, :file => file, :author => logged_in_user)
446 @attachments << a unless a.new_record?
429 @attachments << a unless a.new_record?
447 } if params[:attachments] and params[:attachments].is_a? Array
430 } if params[:attachments] and params[:attachments].is_a? Array
448 Mailer.deliver_attachments_add(@attachments) if !@attachments.empty? and Permission.find_by_controller_and_action(params[:controller], params[:action]).mail_enabled?
431 Mailer.deliver_attachments_add(@attachments) if !@attachments.empty? and Permission.find_by_controller_and_action(params[:controller], params[:action]).mail_enabled?
449 redirect_to :controller => 'projects', :action => 'list_files', :id => @project
432 redirect_to :controller => 'projects', :action => 'list_files', :id => @project
450 end
433 end
451 @versions = @project.versions.sort
434 @versions = @project.versions.sort
452 end
435 end
453
436
454 def list_files
437 def list_files
455 @versions = @project.versions.sort
438 @versions = @project.versions.sort
456 end
439 end
457
440
458 # Show changelog for @project
441 # Show changelog for @project
459 def changelog
442 def changelog
460 @trackers = Tracker.find(:all, :conditions => ["is_in_chlog=?", true], :order => 'position')
443 @trackers = Tracker.find(:all, :conditions => ["is_in_chlog=?", true], :order => 'position')
461 retrieve_selected_tracker_ids(@trackers)
444 retrieve_selected_tracker_ids(@trackers)
462 @versions = @project.versions.sort
445 @versions = @project.versions.sort
463 end
446 end
464
447
465 def roadmap
448 def roadmap
466 @trackers = Tracker.find(:all, :conditions => ["is_in_roadmap=?", true], :order => 'position')
449 @trackers = Tracker.find(:all, :conditions => ["is_in_roadmap=?", true], :order => 'position')
467 retrieve_selected_tracker_ids(@trackers)
450 retrieve_selected_tracker_ids(@trackers)
468 conditions = ("1" == params[:completed] ? nil : [ "#{Version.table_name}.effective_date > ? OR #{Version.table_name}.effective_date IS NULL", Date.today])
451 conditions = ("1" == params[:completed] ? nil : [ "#{Version.table_name}.effective_date > ? OR #{Version.table_name}.effective_date IS NULL", Date.today])
469 @versions = @project.versions.find(:all, :conditions => conditions).sort
452 @versions = @project.versions.find(:all, :conditions => conditions).sort
470 end
453 end
471
454
472 def activity
455 def activity
473 if params[:year] and params[:year].to_i > 1900
456 if params[:year] and params[:year].to_i > 1900
474 @year = params[:year].to_i
457 @year = params[:year].to_i
475 if params[:month] and params[:month].to_i > 0 and params[:month].to_i < 13
458 if params[:month] and params[:month].to_i > 0 and params[:month].to_i < 13
476 @month = params[:month].to_i
459 @month = params[:month].to_i
477 end
460 end
478 end
461 end
479 @year ||= Date.today.year
462 @year ||= Date.today.year
480 @month ||= Date.today.month
463 @month ||= Date.today.month
481
464
482 @date_from = Date.civil(@year, @month, 1)
465 @date_from = Date.civil(@year, @month, 1)
483 @date_to = @date_from >> 1
466 @date_to = @date_from >> 1
484
467
485 @events_by_day = {}
468 @events_by_day = {}
486
469
487 unless params[:show_issues] == "0"
470 unless params[:show_issues] == "0"
488 @project.issues.find(:all, :include => [:author], :conditions => ["#{Issue.table_name}.created_on>=? and #{Issue.table_name}.created_on<=?", @date_from, @date_to] ).each { |i|
471 @project.issues.find(:all, :include => [:author], :conditions => ["#{Issue.table_name}.created_on>=? and #{Issue.table_name}.created_on<=?", @date_from, @date_to] ).each { |i|
489 @events_by_day[i.created_on.to_date] ||= []
472 @events_by_day[i.created_on.to_date] ||= []
490 @events_by_day[i.created_on.to_date] << i
473 @events_by_day[i.created_on.to_date] << i
491 }
474 }
492 @show_issues = 1
475 @show_issues = 1
493 end
476 end
494
477
495 unless params[:show_news] == "0"
478 unless params[:show_news] == "0"
496 @project.news.find(:all, :conditions => ["#{News.table_name}.created_on>=? and #{News.table_name}.created_on<=?", @date_from, @date_to], :include => :author ).each { |i|
479 @project.news.find(:all, :conditions => ["#{News.table_name}.created_on>=? and #{News.table_name}.created_on<=?", @date_from, @date_to], :include => :author ).each { |i|
497 @events_by_day[i.created_on.to_date] ||= []
480 @events_by_day[i.created_on.to_date] ||= []
498 @events_by_day[i.created_on.to_date] << i
481 @events_by_day[i.created_on.to_date] << i
499 }
482 }
500 @show_news = 1
483 @show_news = 1
501 end
484 end
502
485
503 unless params[:show_files] == "0"
486 unless params[:show_files] == "0"
504 Attachment.find(:all, :select => "#{Attachment.table_name}.*", :joins => "LEFT JOIN #{Version.table_name} ON #{Version.table_name}.id = #{Attachment.table_name}.container_id", :conditions => ["#{Attachment.table_name}.container_type='Version' and #{Version.table_name}.project_id=? and #{Attachment.table_name}.created_on>=? and #{Attachment.table_name}.created_on<=?", @project.id, @date_from, @date_to], :include => :author ).each { |i|
487 Attachment.find(:all, :select => "#{Attachment.table_name}.*", :joins => "LEFT JOIN #{Version.table_name} ON #{Version.table_name}.id = #{Attachment.table_name}.container_id", :conditions => ["#{Attachment.table_name}.container_type='Version' and #{Version.table_name}.project_id=? and #{Attachment.table_name}.created_on>=? and #{Attachment.table_name}.created_on<=?", @project.id, @date_from, @date_to], :include => :author ).each { |i|
505 @events_by_day[i.created_on.to_date] ||= []
488 @events_by_day[i.created_on.to_date] ||= []
506 @events_by_day[i.created_on.to_date] << i
489 @events_by_day[i.created_on.to_date] << i
507 }
490 }
508 @show_files = 1
491 @show_files = 1
509 end
492 end
510
493
511 unless params[:show_documents] == "0"
494 unless params[:show_documents] == "0"
512 @project.documents.find(:all, :conditions => ["#{Document.table_name}.created_on>=? and #{Document.table_name}.created_on<=?", @date_from, @date_to] ).each { |i|
495 @project.documents.find(:all, :conditions => ["#{Document.table_name}.created_on>=? and #{Document.table_name}.created_on<=?", @date_from, @date_to] ).each { |i|
513 @events_by_day[i.created_on.to_date] ||= []
496 @events_by_day[i.created_on.to_date] ||= []
514 @events_by_day[i.created_on.to_date] << i
497 @events_by_day[i.created_on.to_date] << i
515 }
498 }
516 Attachment.find(:all, :select => "attachments.*", :joins => "LEFT JOIN #{Document.table_name} ON #{Document.table_name}.id = #{Attachment.table_name}.container_id", :conditions => ["#{Attachment.table_name}.container_type='Document' and #{Document.table_name}.project_id=? and #{Attachment.table_name}.created_on>=? and #{Attachment.table_name}.created_on<=?", @project.id, @date_from, @date_to], :include => :author ).each { |i|
499 Attachment.find(:all, :select => "attachments.*", :joins => "LEFT JOIN #{Document.table_name} ON #{Document.table_name}.id = #{Attachment.table_name}.container_id", :conditions => ["#{Attachment.table_name}.container_type='Document' and #{Document.table_name}.project_id=? and #{Attachment.table_name}.created_on>=? and #{Attachment.table_name}.created_on<=?", @project.id, @date_from, @date_to], :include => :author ).each { |i|
517 @events_by_day[i.created_on.to_date] ||= []
500 @events_by_day[i.created_on.to_date] ||= []
518 @events_by_day[i.created_on.to_date] << i
501 @events_by_day[i.created_on.to_date] << i
519 }
502 }
520 @show_documents = 1
503 @show_documents = 1
521 end
504 end
522
505
523 unless @project.wiki.nil? || params[:show_wiki_edits] == "0"
506 unless @project.wiki.nil? || params[:show_wiki_edits] == "0"
524 select = "#{WikiContent.versioned_table_name}.updated_on, #{WikiContent.versioned_table_name}.comments, " +
507 select = "#{WikiContent.versioned_table_name}.updated_on, #{WikiContent.versioned_table_name}.comments, " +
525 "#{WikiContent.versioned_table_name}.#{WikiContent.version_column}, #{WikiPage.table_name}.title"
508 "#{WikiContent.versioned_table_name}.#{WikiContent.version_column}, #{WikiPage.table_name}.title"
526 joins = "LEFT JOIN #{WikiPage.table_name} ON #{WikiPage.table_name}.id = #{WikiContent.versioned_table_name}.page_id " +
509 joins = "LEFT JOIN #{WikiPage.table_name} ON #{WikiPage.table_name}.id = #{WikiContent.versioned_table_name}.page_id " +
527 "LEFT JOIN #{Wiki.table_name} ON #{Wiki.table_name}.id = #{WikiPage.table_name}.wiki_id "
510 "LEFT JOIN #{Wiki.table_name} ON #{Wiki.table_name}.id = #{WikiPage.table_name}.wiki_id "
528 conditions = ["#{Wiki.table_name}.project_id = ? AND #{WikiContent.versioned_table_name}.updated_on BETWEEN ? AND ?",
511 conditions = ["#{Wiki.table_name}.project_id = ? AND #{WikiContent.versioned_table_name}.updated_on BETWEEN ? AND ?",
529 @project.id, @date_from, @date_to]
512 @project.id, @date_from, @date_to]
530
513
531 WikiContent.versioned_class.find(:all, :select => select, :joins => joins, :conditions => conditions).each { |i|
514 WikiContent.versioned_class.find(:all, :select => select, :joins => joins, :conditions => conditions).each { |i|
532 # We provide this alias so all events can be treated in the same manner
515 # We provide this alias so all events can be treated in the same manner
533 def i.created_on
516 def i.created_on
534 self.updated_on
517 self.updated_on
535 end
518 end
536
519
537 @events_by_day[i.created_on.to_date] ||= []
520 @events_by_day[i.created_on.to_date] ||= []
538 @events_by_day[i.created_on.to_date] << i
521 @events_by_day[i.created_on.to_date] << i
539 }
522 }
540 @show_wiki_edits = 1
523 @show_wiki_edits = 1
541 end
524 end
542
525
543 unless @project.repository.nil? || params[:show_changesets] == "0"
526 unless @project.repository.nil? || params[:show_changesets] == "0"
544 @project.repository.changesets.find(:all, :conditions => ["#{Changeset.table_name}.committed_on BETWEEN ? AND ?", @date_from, @date_to]).each { |i|
527 @project.repository.changesets.find(:all, :conditions => ["#{Changeset.table_name}.committed_on BETWEEN ? AND ?", @date_from, @date_to]).each { |i|
545 def i.created_on
528 def i.created_on
546 self.committed_on
529 self.committed_on
547 end
530 end
548 @events_by_day[i.created_on.to_date] ||= []
531 @events_by_day[i.created_on.to_date] ||= []
549 @events_by_day[i.created_on.to_date] << i
532 @events_by_day[i.created_on.to_date] << i
550 }
533 }
551 @show_changesets = 1
534 @show_changesets = 1
552 end
535 end
553
536
554 render :layout => false if request.xhr?
537 render :layout => false if request.xhr?
555 end
538 end
556
539
557 def calendar
540 def calendar
558 @trackers = Tracker.find(:all, :order => 'position')
541 @trackers = Tracker.find(:all, :order => 'position')
559 retrieve_selected_tracker_ids(@trackers)
542 retrieve_selected_tracker_ids(@trackers)
560
543
561 if params[:year] and params[:year].to_i > 1900
544 if params[:year] and params[:year].to_i > 1900
562 @year = params[:year].to_i
545 @year = params[:year].to_i
563 if params[:month] and params[:month].to_i > 0 and params[:month].to_i < 13
546 if params[:month] and params[:month].to_i > 0 and params[:month].to_i < 13
564 @month = params[:month].to_i
547 @month = params[:month].to_i
565 end
548 end
566 end
549 end
567 @year ||= Date.today.year
550 @year ||= Date.today.year
568 @month ||= Date.today.month
551 @month ||= Date.today.month
569
552
570 @date_from = Date.civil(@year, @month, 1)
553 @date_from = Date.civil(@year, @month, 1)
571 @date_to = (@date_from >> 1)-1
554 @date_to = (@date_from >> 1)-1
572 # start on monday
555 # start on monday
573 @date_from = @date_from - (@date_from.cwday-1)
556 @date_from = @date_from - (@date_from.cwday-1)
574 # finish on sunday
557 # finish on sunday
575 @date_to = @date_to + (7-@date_to.cwday)
558 @date_to = @date_to + (7-@date_to.cwday)
576
559
577 @events = []
560 @events = []
578 @project.issues_with_subprojects(params[:with_subprojects]) do
561 @project.issues_with_subprojects(params[:with_subprojects]) do
579 @events += Issue.find(:all,
562 @events += Issue.find(:all,
580 :include => [:tracker, :status, :assigned_to, :priority, :project],
563 :include => [:tracker, :status, :assigned_to, :priority, :project],
581 :conditions => ["((start_date>=? and start_date<=?) or (due_date>=? and due_date<=?)) and #{Issue.table_name}.tracker_id in (#{@selected_tracker_ids.join(',')})", @date_from, @date_to, @date_from, @date_to]
564 :conditions => ["((start_date>=? and start_date<=?) or (due_date>=? and due_date<=?)) and #{Issue.table_name}.tracker_id in (#{@selected_tracker_ids.join(',')})", @date_from, @date_to, @date_from, @date_to]
582 ) unless @selected_tracker_ids.empty?
565 ) unless @selected_tracker_ids.empty?
583 end
566 end
584 @events += @project.versions.find(:all, :conditions => ["effective_date BETWEEN ? AND ?", @date_from, @date_to])
567 @events += @project.versions.find(:all, :conditions => ["effective_date BETWEEN ? AND ?", @date_from, @date_to])
585
568
586 @ending_events_by_days = @events.group_by {|event| event.due_date}
569 @ending_events_by_days = @events.group_by {|event| event.due_date}
587 @starting_events_by_days = @events.group_by {|event| event.start_date}
570 @starting_events_by_days = @events.group_by {|event| event.start_date}
588
571
589 render :layout => false if request.xhr?
572 render :layout => false if request.xhr?
590 end
573 end
591
574
592 def gantt
575 def gantt
593 @trackers = Tracker.find(:all, :order => 'position')
576 @trackers = Tracker.find(:all, :order => 'position')
594 retrieve_selected_tracker_ids(@trackers)
577 retrieve_selected_tracker_ids(@trackers)
595
578
596 if params[:year] and params[:year].to_i >0
579 if params[:year] and params[:year].to_i >0
597 @year_from = params[:year].to_i
580 @year_from = params[:year].to_i
598 if params[:month] and params[:month].to_i >=1 and params[:month].to_i <= 12
581 if params[:month] and params[:month].to_i >=1 and params[:month].to_i <= 12
599 @month_from = params[:month].to_i
582 @month_from = params[:month].to_i
600 else
583 else
601 @month_from = 1
584 @month_from = 1
602 end
585 end
603 else
586 else
604 @month_from ||= (Date.today << 1).month
587 @month_from ||= (Date.today << 1).month
605 @year_from ||= (Date.today << 1).year
588 @year_from ||= (Date.today << 1).year
606 end
589 end
607
590
608 @zoom = (params[:zoom].to_i > 0 and params[:zoom].to_i < 5) ? params[:zoom].to_i : 2
591 @zoom = (params[:zoom].to_i > 0 and params[:zoom].to_i < 5) ? params[:zoom].to_i : 2
609 @months = (params[:months].to_i > 0 and params[:months].to_i < 25) ? params[:months].to_i : 6
592 @months = (params[:months].to_i > 0 and params[:months].to_i < 25) ? params[:months].to_i : 6
610
593
611 @date_from = Date.civil(@year_from, @month_from, 1)
594 @date_from = Date.civil(@year_from, @month_from, 1)
612 @date_to = (@date_from >> @months) - 1
595 @date_to = (@date_from >> @months) - 1
613
596
614 @events = []
597 @events = []
615 @project.issues_with_subprojects(params[:with_subprojects]) do
598 @project.issues_with_subprojects(params[:with_subprojects]) do
616 @events += Issue.find(:all,
599 @events += Issue.find(:all,
617 :order => "start_date, due_date",
600 :order => "start_date, due_date",
618 :include => [:tracker, :status, :assigned_to, :priority, :project],
601 :include => [:tracker, :status, :assigned_to, :priority, :project],
619 :conditions => ["(((start_date>=? and start_date<=?) or (due_date>=? and due_date<=?) or (start_date<? and due_date>?)) and start_date is not null and due_date is not null and #{Issue.table_name}.tracker_id in (#{@selected_tracker_ids.join(',')}))", @date_from, @date_to, @date_from, @date_to, @date_from, @date_to]
602 :conditions => ["(((start_date>=? and start_date<=?) or (due_date>=? and due_date<=?) or (start_date<? and due_date>?)) and start_date is not null and due_date is not null and #{Issue.table_name}.tracker_id in (#{@selected_tracker_ids.join(',')}))", @date_from, @date_to, @date_from, @date_to, @date_from, @date_to]
620 ) unless @selected_tracker_ids.empty?
603 ) unless @selected_tracker_ids.empty?
621 end
604 end
622 @events += @project.versions.find(:all, :conditions => ["effective_date BETWEEN ? AND ?", @date_from, @date_to])
605 @events += @project.versions.find(:all, :conditions => ["effective_date BETWEEN ? AND ?", @date_from, @date_to])
623 @events.sort! {|x,y| x.start_date <=> y.start_date }
606 @events.sort! {|x,y| x.start_date <=> y.start_date }
624
607
625 if params[:output]=='pdf'
608 if params[:output]=='pdf'
626 @options_for_rfpdf ||= {}
609 @options_for_rfpdf ||= {}
627 @options_for_rfpdf[:file_name] = "gantt.pdf"
610 @options_for_rfpdf[:file_name] = "gantt.pdf"
628 render :template => "projects/gantt.rfpdf", :layout => false
611 render :template => "projects/gantt.rfpdf", :layout => false
629 else
612 else
630 render :template => "projects/gantt.rhtml"
613 render :template => "projects/gantt.rhtml"
631 end
614 end
632 end
615 end
633
616
634 def feeds
617 def feeds
635 @queries = @project.queries.find :all, :conditions => ["is_public=? or user_id=?", true, (logged_in_user ? logged_in_user.id : 0)]
618 @queries = @project.queries.find :all, :conditions => ["is_public=? or user_id=?", true, (logged_in_user ? logged_in_user.id : 0)]
636 @key = logged_in_user.get_or_create_rss_key.value if logged_in_user
619 @key = logged_in_user.get_or_create_rss_key.value if logged_in_user
637 end
620 end
638
621
639 private
622 private
640 # Find project of id params[:id]
623 # Find project of id params[:id]
641 # if not found, redirect to project list
624 # if not found, redirect to project list
642 # Used as a before_filter
625 # Used as a before_filter
643 def find_project
626 def find_project
644 @project = Project.find(params[:id])
627 @project = Project.find(params[:id])
645 @html_title = @project.name
628 @html_title = @project.name
646 rescue ActiveRecord::RecordNotFound
629 rescue ActiveRecord::RecordNotFound
647 render_404
630 render_404
648 end
631 end
649
632
650 def retrieve_selected_tracker_ids(selectable_trackers)
633 def retrieve_selected_tracker_ids(selectable_trackers)
651 if ids = params[:tracker_ids]
634 if ids = params[:tracker_ids]
652 @selected_tracker_ids = (ids.is_a? Array) ? ids.collect { |id| id.to_i.to_s } : ids.split('/').collect { |id| id.to_i.to_s }
635 @selected_tracker_ids = (ids.is_a? Array) ? ids.collect { |id| id.to_i.to_s } : ids.split('/').collect { |id| id.to_i.to_s }
653 else
636 else
654 @selected_tracker_ids = selectable_trackers.collect {|t| t.id.to_s }
637 @selected_tracker_ids = selectable_trackers.collect {|t| t.id.to_s }
655 end
638 end
656 end
639 end
657
640
658 # Retrieve query from session or build a new query
641 # Retrieve query from session or build a new query
659 def retrieve_query
642 def retrieve_query
660 if params[:query_id]
643 if params[:query_id]
661 @query = @project.queries.find(params[:query_id])
644 @query = @project.queries.find(params[:query_id])
662 @query.executed_by = logged_in_user
645 @query.executed_by = logged_in_user
663 session[:query] = @query
646 session[:query] = @query
664 else
647 else
665 if params[:set_filter] or !session[:query] or session[:query].project_id != @project.id
648 if params[:set_filter] or !session[:query] or session[:query].project_id != @project.id
666 # Give it a name, required to be valid
649 # Give it a name, required to be valid
667 @query = Query.new(:name => "_", :executed_by => logged_in_user)
650 @query = Query.new(:name => "_", :executed_by => logged_in_user)
668 @query.project = @project
651 @query.project = @project
669 if params[:fields] and params[:fields].is_a? Array
652 if params[:fields] and params[:fields].is_a? Array
670 params[:fields].each do |field|
653 params[:fields].each do |field|
671 @query.add_filter(field, params[:operators][field], params[:values][field])
654 @query.add_filter(field, params[:operators][field], params[:values][field])
672 end
655 end
673 else
656 else
674 @query.available_filters.keys.each do |field|
657 @query.available_filters.keys.each do |field|
675 @query.add_short_filter(field, params[field]) if params[field]
658 @query.add_short_filter(field, params[field]) if params[field]
676 end
659 end
677 end
660 end
678 session[:query] = @query
661 session[:query] = @query
679 else
662 else
680 @query = session[:query]
663 @query = session[:query]
681 end
664 end
682 end
665 end
683 end
666 end
684 end
667 end
@@ -1,52 +1,82
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006 Jean-Philippe Lang
2 # Copyright (C) 2006-2007 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
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
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
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.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 class QueriesController < ApplicationController
18 class QueriesController < ApplicationController
19 layout 'base'
19 layout 'base'
20 before_filter :require_login, :find_query
20 before_filter :require_login, :except => :index
21 before_filter :find_project, :check_project_privacy
22
23 def index
24 @queries = @project.queries.find(:all,
25 :order => "name ASC",
26 :conditions => ["is_public = ? or user_id = ?", true, (logged_in_user ? logged_in_user.id : 0)])
27 end
28
29 def new
30 @query = Query.new(params[:query])
31 @query.project = @project
32 @query.user = logged_in_user
33 @query.executed_by = logged_in_user
34 @query.is_public = false unless logged_in_user.authorized_to(@project, 'projects/add_query')
35
36 params[:fields].each do |field|
37 @query.add_filter(field, params[:operators][field], params[:values][field])
38 end if params[:fields]
39
40 if request.post? and @query.save
41 flash[:notice] = l(:notice_successful_create)
42 redirect_to :controller => 'projects', :action => 'list_issues', :id => @project, :query_id => @query
43 return
44 end
45 render :layout => false if request.xhr?
46 end
21
47
22 def edit
48 def edit
23 if request.post?
49 if request.post?
24 @query.filters = {}
50 @query.filters = {}
25 params[:fields].each do |field|
51 params[:fields].each do |field|
26 @query.add_filter(field, params[:operators][field], params[:values][field])
52 @query.add_filter(field, params[:operators][field], params[:values][field])
27 end if params[:fields]
53 end if params[:fields]
28 @query.attributes = params[:query]
54 @query.attributes = params[:query]
55 @query.is_public = false unless logged_in_user.authorized_to(@project, 'projects/add_query')
29
56
30 if @query.save
57 if @query.save
31 flash[:notice] = l(:notice_successful_update)
58 flash[:notice] = l(:notice_successful_update)
32 redirect_to :controller => 'projects', :action => 'list_issues', :id => @project, :query_id => @query
59 redirect_to :controller => 'projects', :action => 'list_issues', :id => @project, :query_id => @query
33 end
60 end
34 end
61 end
35 end
62 end
36
63
37 def destroy
64 def destroy
38 @query.destroy if request.post?
65 @query.destroy if request.post?
39 redirect_to :controller => 'reports', :action => 'issue_report', :id => @project
66 redirect_to :controller => 'queries', :project_id => @project
40 end
67 end
41
68
42 private
69 private
43 def find_query
70 def find_project
71 if params[:id]
44 @query = Query.find(params[:id])
72 @query = Query.find(params[:id])
45 @query.executed_by = logged_in_user
73 @query.executed_by = logged_in_user
46 @project = @query.project
74 @project = @query.project
47 # check if user is allowed to manage queries (same permission as add_query)
75 render_403 unless @query.editable_by?(logged_in_user)
48 authorize('projects', 'add_query')
76 else
77 @project = Project.find(params[:project_id])
78 end
49 rescue ActiveRecord::RecordNotFound
79 rescue ActiveRecord::RecordNotFound
50 render_404
80 render_404
51 end
81 end
52 end
82 end
@@ -1,214 +1,213
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006 Jean-Philippe Lang
2 # Copyright (C) 2006 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
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
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
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.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 class ReportsController < ApplicationController
18 class ReportsController < ApplicationController
19 layout 'base'
19 layout 'base'
20 before_filter :find_project, :authorize
20 before_filter :find_project, :authorize
21
21
22 def issue_report
22 def issue_report
23 @statuses = IssueStatus.find(:all, :order => 'position')
23 @statuses = IssueStatus.find(:all, :order => 'position')
24
24
25 case params[:detail]
25 case params[:detail]
26 when "tracker"
26 when "tracker"
27 @field = "tracker_id"
27 @field = "tracker_id"
28 @rows = Tracker.find :all, :order => 'position'
28 @rows = Tracker.find :all, :order => 'position'
29 @data = issues_by_tracker
29 @data = issues_by_tracker
30 @report_title = l(:field_tracker)
30 @report_title = l(:field_tracker)
31 render :template => "reports/issue_report_details"
31 render :template => "reports/issue_report_details"
32 when "version"
32 when "version"
33 @field = "fixed_version_id"
33 @field = "fixed_version_id"
34 @rows = @project.versions.sort
34 @rows = @project.versions.sort
35 @data = issues_by_version
35 @data = issues_by_version
36 @report_title = l(:field_version)
36 @report_title = l(:field_version)
37 render :template => "reports/issue_report_details"
37 render :template => "reports/issue_report_details"
38 when "priority"
38 when "priority"
39 @field = "priority_id"
39 @field = "priority_id"
40 @rows = Enumeration::get_values('IPRI')
40 @rows = Enumeration::get_values('IPRI')
41 @data = issues_by_priority
41 @data = issues_by_priority
42 @report_title = l(:field_priority)
42 @report_title = l(:field_priority)
43 render :template => "reports/issue_report_details"
43 render :template => "reports/issue_report_details"
44 when "category"
44 when "category"
45 @field = "category_id"
45 @field = "category_id"
46 @rows = @project.issue_categories
46 @rows = @project.issue_categories
47 @data = issues_by_category
47 @data = issues_by_category
48 @report_title = l(:field_category)
48 @report_title = l(:field_category)
49 render :template => "reports/issue_report_details"
49 render :template => "reports/issue_report_details"
50 when "author"
50 when "author"
51 @field = "author_id"
51 @field = "author_id"
52 @rows = @project.members.collect { |m| m.user }
52 @rows = @project.members.collect { |m| m.user }
53 @data = issues_by_author
53 @data = issues_by_author
54 @report_title = l(:field_author)
54 @report_title = l(:field_author)
55 render :template => "reports/issue_report_details"
55 render :template => "reports/issue_report_details"
56 when "subproject"
56 when "subproject"
57 @field = "project_id"
57 @field = "project_id"
58 @rows = @project.active_children
58 @rows = @project.active_children
59 @data = issues_by_subproject
59 @data = issues_by_subproject
60 @report_title = l(:field_subproject)
60 @report_title = l(:field_subproject)
61 render :template => "reports/issue_report_details"
61 render :template => "reports/issue_report_details"
62 else
62 else
63 @queries = @project.queries.find :all, :conditions => ["is_public=? or user_id=?", true, (logged_in_user ? logged_in_user.id : 0)]
64 @trackers = Tracker.find(:all, :order => 'position')
63 @trackers = Tracker.find(:all, :order => 'position')
65 @versions = @project.versions.sort
64 @versions = @project.versions.sort
66 @priorities = Enumeration::get_values('IPRI')
65 @priorities = Enumeration::get_values('IPRI')
67 @categories = @project.issue_categories
66 @categories = @project.issue_categories
68 @authors = @project.members.collect { |m| m.user }
67 @authors = @project.members.collect { |m| m.user }
69 @subprojects = @project.active_children
68 @subprojects = @project.active_children
70 issues_by_tracker
69 issues_by_tracker
71 issues_by_version
70 issues_by_version
72 issues_by_priority
71 issues_by_priority
73 issues_by_category
72 issues_by_category
74 issues_by_author
73 issues_by_author
75 issues_by_subproject
74 issues_by_subproject
76 @total_hours = @project.time_entries.sum(:hours)
75 @total_hours = @project.time_entries.sum(:hours)
77 render :template => "reports/issue_report"
76 render :template => "reports/issue_report"
78 end
77 end
79 end
78 end
80
79
81 def delays
80 def delays
82 @trackers = Tracker.find(:all)
81 @trackers = Tracker.find(:all)
83 if request.get?
82 if request.get?
84 @selected_tracker_ids = @trackers.collect {|t| t.id.to_s }
83 @selected_tracker_ids = @trackers.collect {|t| t.id.to_s }
85 else
84 else
86 @selected_tracker_ids = params[:tracker_ids].collect { |id| id.to_i.to_s } if params[:tracker_ids] and params[:tracker_ids].is_a? Array
85 @selected_tracker_ids = params[:tracker_ids].collect { |id| id.to_i.to_s } if params[:tracker_ids] and params[:tracker_ids].is_a? Array
87 end
86 end
88 @selected_tracker_ids ||= []
87 @selected_tracker_ids ||= []
89 @raw =
88 @raw =
90 ActiveRecord::Base.connection.select_all("SELECT datediff( a.created_on, b.created_on ) as delay, count(a.id) as total
89 ActiveRecord::Base.connection.select_all("SELECT datediff( a.created_on, b.created_on ) as delay, count(a.id) as total
91 FROM issue_histories a, issue_histories b, issues i
90 FROM issue_histories a, issue_histories b, issues i
92 WHERE a.status_id =5
91 WHERE a.status_id =5
93 AND a.issue_id = b.issue_id
92 AND a.issue_id = b.issue_id
94 AND a.issue_id = i.id
93 AND a.issue_id = i.id
95 AND i.tracker_id in (#{@selected_tracker_ids.join(',')})
94 AND i.tracker_id in (#{@selected_tracker_ids.join(',')})
96 AND b.id = (
95 AND b.id = (
97 SELECT min( c.id )
96 SELECT min( c.id )
98 FROM issue_histories c
97 FROM issue_histories c
99 WHERE b.issue_id = c.issue_id )
98 WHERE b.issue_id = c.issue_id )
100 GROUP BY delay") unless @selected_tracker_ids.empty?
99 GROUP BY delay") unless @selected_tracker_ids.empty?
101 @raw ||=[]
100 @raw ||=[]
102
101
103 @x_from = 0
102 @x_from = 0
104 @x_to = 0
103 @x_to = 0
105 @y_from = 0
104 @y_from = 0
106 @y_to = 0
105 @y_to = 0
107 @sum_total = 0
106 @sum_total = 0
108 @sum_delay = 0
107 @sum_delay = 0
109 @raw.each do |r|
108 @raw.each do |r|
110 @x_to = [r['delay'].to_i, @x_to].max
109 @x_to = [r['delay'].to_i, @x_to].max
111 @y_to = [r['total'].to_i, @y_to].max
110 @y_to = [r['total'].to_i, @y_to].max
112 @sum_total = @sum_total + r['total'].to_i
111 @sum_total = @sum_total + r['total'].to_i
113 @sum_delay = @sum_delay + r['total'].to_i * r['delay'].to_i
112 @sum_delay = @sum_delay + r['total'].to_i * r['delay'].to_i
114 end
113 end
115 end
114 end
116
115
117 private
116 private
118 # Find project of id params[:id]
117 # Find project of id params[:id]
119 def find_project
118 def find_project
120 @project = Project.find(params[:id])
119 @project = Project.find(params[:id])
121 rescue ActiveRecord::RecordNotFound
120 rescue ActiveRecord::RecordNotFound
122 render_404
121 render_404
123 end
122 end
124
123
125 def issues_by_tracker
124 def issues_by_tracker
126 @issues_by_tracker ||=
125 @issues_by_tracker ||=
127 ActiveRecord::Base.connection.select_all("select s.id as status_id,
126 ActiveRecord::Base.connection.select_all("select s.id as status_id,
128 s.is_closed as closed,
127 s.is_closed as closed,
129 t.id as tracker_id,
128 t.id as tracker_id,
130 count(i.id) as total
129 count(i.id) as total
131 from
130 from
132 #{Issue.table_name} i, #{IssueStatus.table_name} s, #{Tracker.table_name} t
131 #{Issue.table_name} i, #{IssueStatus.table_name} s, #{Tracker.table_name} t
133 where
132 where
134 i.status_id=s.id
133 i.status_id=s.id
135 and i.tracker_id=t.id
134 and i.tracker_id=t.id
136 and i.project_id=#{@project.id}
135 and i.project_id=#{@project.id}
137 group by s.id, s.is_closed, t.id")
136 group by s.id, s.is_closed, t.id")
138 end
137 end
139
138
140 def issues_by_version
139 def issues_by_version
141 @issues_by_version ||=
140 @issues_by_version ||=
142 ActiveRecord::Base.connection.select_all("select s.id as status_id,
141 ActiveRecord::Base.connection.select_all("select s.id as status_id,
143 s.is_closed as closed,
142 s.is_closed as closed,
144 v.id as fixed_version_id,
143 v.id as fixed_version_id,
145 count(i.id) as total
144 count(i.id) as total
146 from
145 from
147 #{Issue.table_name} i, #{IssueStatus.table_name} s, #{Version.table_name} v
146 #{Issue.table_name} i, #{IssueStatus.table_name} s, #{Version.table_name} v
148 where
147 where
149 i.status_id=s.id
148 i.status_id=s.id
150 and i.fixed_version_id=v.id
149 and i.fixed_version_id=v.id
151 and i.project_id=#{@project.id}
150 and i.project_id=#{@project.id}
152 group by s.id, s.is_closed, v.id")
151 group by s.id, s.is_closed, v.id")
153 end
152 end
154
153
155 def issues_by_priority
154 def issues_by_priority
156 @issues_by_priority ||=
155 @issues_by_priority ||=
157 ActiveRecord::Base.connection.select_all("select s.id as status_id,
156 ActiveRecord::Base.connection.select_all("select s.id as status_id,
158 s.is_closed as closed,
157 s.is_closed as closed,
159 p.id as priority_id,
158 p.id as priority_id,
160 count(i.id) as total
159 count(i.id) as total
161 from
160 from
162 #{Issue.table_name} i, #{IssueStatus.table_name} s, #{Enumeration.table_name} p
161 #{Issue.table_name} i, #{IssueStatus.table_name} s, #{Enumeration.table_name} p
163 where
162 where
164 i.status_id=s.id
163 i.status_id=s.id
165 and i.priority_id=p.id
164 and i.priority_id=p.id
166 and i.project_id=#{@project.id}
165 and i.project_id=#{@project.id}
167 group by s.id, s.is_closed, p.id")
166 group by s.id, s.is_closed, p.id")
168 end
167 end
169
168
170 def issues_by_category
169 def issues_by_category
171 @issues_by_category ||=
170 @issues_by_category ||=
172 ActiveRecord::Base.connection.select_all("select s.id as status_id,
171 ActiveRecord::Base.connection.select_all("select s.id as status_id,
173 s.is_closed as closed,
172 s.is_closed as closed,
174 c.id as category_id,
173 c.id as category_id,
175 count(i.id) as total
174 count(i.id) as total
176 from
175 from
177 #{Issue.table_name} i, #{IssueStatus.table_name} s, #{IssueCategory.table_name} c
176 #{Issue.table_name} i, #{IssueStatus.table_name} s, #{IssueCategory.table_name} c
178 where
177 where
179 i.status_id=s.id
178 i.status_id=s.id
180 and i.category_id=c.id
179 and i.category_id=c.id
181 and i.project_id=#{@project.id}
180 and i.project_id=#{@project.id}
182 group by s.id, s.is_closed, c.id")
181 group by s.id, s.is_closed, c.id")
183 end
182 end
184
183
185 def issues_by_author
184 def issues_by_author
186 @issues_by_author ||=
185 @issues_by_author ||=
187 ActiveRecord::Base.connection.select_all("select s.id as status_id,
186 ActiveRecord::Base.connection.select_all("select s.id as status_id,
188 s.is_closed as closed,
187 s.is_closed as closed,
189 a.id as author_id,
188 a.id as author_id,
190 count(i.id) as total
189 count(i.id) as total
191 from
190 from
192 #{Issue.table_name} i, #{IssueStatus.table_name} s, #{User.table_name} a
191 #{Issue.table_name} i, #{IssueStatus.table_name} s, #{User.table_name} a
193 where
192 where
194 i.status_id=s.id
193 i.status_id=s.id
195 and i.author_id=a.id
194 and i.author_id=a.id
196 and i.project_id=#{@project.id}
195 and i.project_id=#{@project.id}
197 group by s.id, s.is_closed, a.id")
196 group by s.id, s.is_closed, a.id")
198 end
197 end
199
198
200 def issues_by_subproject
199 def issues_by_subproject
201 @issues_by_subproject ||=
200 @issues_by_subproject ||=
202 ActiveRecord::Base.connection.select_all("select s.id as status_id,
201 ActiveRecord::Base.connection.select_all("select s.id as status_id,
203 s.is_closed as closed,
202 s.is_closed as closed,
204 i.project_id as project_id,
203 i.project_id as project_id,
205 count(i.id) as total
204 count(i.id) as total
206 from
205 from
207 #{Issue.table_name} i, #{IssueStatus.table_name} s
206 #{Issue.table_name} i, #{IssueStatus.table_name} s
208 where
207 where
209 i.status_id=s.id
208 i.status_id=s.id
210 and i.project_id IN (#{@project.active_children.collect{|p| p.id}.join(',')})
209 and i.project_id IN (#{@project.active_children.collect{|p| p.id}.join(',')})
211 group by s.id, s.is_closed, i.project_id") if @project.active_children.any?
210 group by s.id, s.is_closed, i.project_id") if @project.active_children.any?
212 @issues_by_subproject ||= []
211 @issues_by_subproject ||= []
213 end
212 end
214 end
213 end
@@ -1,234 +1,239
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2007 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
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
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
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.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 class Query < ActiveRecord::Base
18 class Query < ActiveRecord::Base
19 belongs_to :project
19 belongs_to :project
20 belongs_to :user
20 belongs_to :user
21 serialize :filters
21 serialize :filters
22
22
23 attr_protected :project, :user
23 attr_protected :project, :user
24 attr_accessor :executed_by
24 attr_accessor :executed_by
25
25
26 validates_presence_of :name, :on => :save
26 validates_presence_of :name, :on => :save
27
27
28 @@operators = { "=" => :label_equals,
28 @@operators = { "=" => :label_equals,
29 "!" => :label_not_equals,
29 "!" => :label_not_equals,
30 "o" => :label_open_issues,
30 "o" => :label_open_issues,
31 "c" => :label_closed_issues,
31 "c" => :label_closed_issues,
32 "!*" => :label_none,
32 "!*" => :label_none,
33 "*" => :label_all,
33 "*" => :label_all,
34 "<t+" => :label_in_less_than,
34 "<t+" => :label_in_less_than,
35 ">t+" => :label_in_more_than,
35 ">t+" => :label_in_more_than,
36 "t+" => :label_in,
36 "t+" => :label_in,
37 "t" => :label_today,
37 "t" => :label_today,
38 ">t-" => :label_less_than_ago,
38 ">t-" => :label_less_than_ago,
39 "<t-" => :label_more_than_ago,
39 "<t-" => :label_more_than_ago,
40 "t-" => :label_ago,
40 "t-" => :label_ago,
41 "~" => :label_contains,
41 "~" => :label_contains,
42 "!~" => :label_not_contains }
42 "!~" => :label_not_contains }
43
43
44 cattr_reader :operators
44 cattr_reader :operators
45
45
46 @@operators_by_filter_type = { :list => [ "=", "!" ],
46 @@operators_by_filter_type = { :list => [ "=", "!" ],
47 :list_status => [ "o", "=", "!", "c", "*" ],
47 :list_status => [ "o", "=", "!", "c", "*" ],
48 :list_optional => [ "=", "!", "!*", "*" ],
48 :list_optional => [ "=", "!", "!*", "*" ],
49 :list_one_or_more => [ "*", "=" ],
49 :list_one_or_more => [ "*", "=" ],
50 :date => [ "<t+", ">t+", "t+", "t", ">t-", "<t-", "t-" ],
50 :date => [ "<t+", ">t+", "t+", "t", ">t-", "<t-", "t-" ],
51 :date_past => [ ">t-", "<t-", "t-", "t" ],
51 :date_past => [ ">t-", "<t-", "t-", "t" ],
52 :string => [ "=", "~", "!", "!~" ],
52 :string => [ "=", "~", "!", "!~" ],
53 :text => [ "~", "!~" ] }
53 :text => [ "~", "!~" ] }
54
54
55 cattr_reader :operators_by_filter_type
55 cattr_reader :operators_by_filter_type
56
56
57 def initialize(attributes = nil)
57 def initialize(attributes = nil)
58 super attributes
58 super attributes
59 self.filters ||= { 'status_id' => {:operator => "o", :values => [""]} }
59 self.filters ||= { 'status_id' => {:operator => "o", :values => [""]} }
60 self.is_public = true
61 end
60 end
62
61
63 def executed_by=(user)
62 def executed_by=(user)
64 @executed_by = user
63 @executed_by = user
65 set_language_if_valid(user.language) if user
64 set_language_if_valid(user.language) if user
66 end
65 end
67
66
68 def validate
67 def validate
69 filters.each_key do |field|
68 filters.each_key do |field|
70 errors.add label_for(field), :activerecord_error_blank unless
69 errors.add label_for(field), :activerecord_error_blank unless
71 # filter requires one or more values
70 # filter requires one or more values
72 (values_for(field) and !values_for(field).first.empty?) or
71 (values_for(field) and !values_for(field).first.empty?) or
73 # filter doesn't require any value
72 # filter doesn't require any value
74 ["o", "c", "!*", "*", "t"].include? operator_for(field)
73 ["o", "c", "!*", "*", "t"].include? operator_for(field)
75 end if filters
74 end if filters
76 end
75 end
77
76
77 def editable_by?(user)
78 return false unless user
79 return true if !is_public && self.user_id == user.id
80 is_public && user.authorized_to(project, "projects/add_query")
81 end
82
78 def available_filters
83 def available_filters
79 return @available_filters if @available_filters
84 return @available_filters if @available_filters
80 @available_filters = { "status_id" => { :type => :list_status, :order => 1, :values => IssueStatus.find(:all, :order => 'position').collect{|s| [s.name, s.id.to_s] } },
85 @available_filters = { "status_id" => { :type => :list_status, :order => 1, :values => IssueStatus.find(:all, :order => 'position').collect{|s| [s.name, s.id.to_s] } },
81 "tracker_id" => { :type => :list, :order => 2, :values => Tracker.find(:all, :order => 'position').collect{|s| [s.name, s.id.to_s] } },
86 "tracker_id" => { :type => :list, :order => 2, :values => Tracker.find(:all, :order => 'position').collect{|s| [s.name, s.id.to_s] } },
82 "priority_id" => { :type => :list, :order => 3, :values => Enumeration.find(:all, :conditions => ['opt=?','IPRI']).collect{|s| [s.name, s.id.to_s] } },
87 "priority_id" => { :type => :list, :order => 3, :values => Enumeration.find(:all, :conditions => ['opt=?','IPRI']).collect{|s| [s.name, s.id.to_s] } },
83 "subject" => { :type => :text, :order => 8 },
88 "subject" => { :type => :text, :order => 8 },
84 "created_on" => { :type => :date_past, :order => 9 },
89 "created_on" => { :type => :date_past, :order => 9 },
85 "updated_on" => { :type => :date_past, :order => 10 },
90 "updated_on" => { :type => :date_past, :order => 10 },
86 "start_date" => { :type => :date, :order => 11 },
91 "start_date" => { :type => :date, :order => 11 },
87 "due_date" => { :type => :date, :order => 12 } }
92 "due_date" => { :type => :date, :order => 12 } }
88 unless project.nil?
93 unless project.nil?
89 # project specific filters
94 # project specific filters
90 user_values = []
95 user_values = []
91 user_values << ["<< #{l(:label_me)} >>", "me"] if executed_by
96 user_values << ["<< #{l(:label_me)} >>", "me"] if executed_by
92 user_values += @project.users.collect{|s| [s.name, s.id.to_s] }
97 user_values += @project.users.collect{|s| [s.name, s.id.to_s] }
93
98
94 @available_filters["assigned_to_id"] = { :type => :list_optional, :order => 4, :values => user_values }
99 @available_filters["assigned_to_id"] = { :type => :list_optional, :order => 4, :values => user_values }
95 @available_filters["author_id"] = { :type => :list, :order => 5, :values => user_values }
100 @available_filters["author_id"] = { :type => :list, :order => 5, :values => user_values }
96 @available_filters["category_id"] = { :type => :list_optional, :order => 6, :values => @project.issue_categories.collect{|s| [s.name, s.id.to_s] } }
101 @available_filters["category_id"] = { :type => :list_optional, :order => 6, :values => @project.issue_categories.collect{|s| [s.name, s.id.to_s] } }
97 @available_filters["fixed_version_id"] = { :type => :list_optional, :order => 7, :values => @project.versions.sort.collect{|s| [s.name, s.id.to_s] } }
102 @available_filters["fixed_version_id"] = { :type => :list_optional, :order => 7, :values => @project.versions.sort.collect{|s| [s.name, s.id.to_s] } }
98 unless @project.active_children.empty?
103 unless @project.active_children.empty?
99 @available_filters["subproject_id"] = { :type => :list_one_or_more, :order => 13, :values => @project.active_children.collect{|s| [s.name, s.id.to_s] } }
104 @available_filters["subproject_id"] = { :type => :list_one_or_more, :order => 13, :values => @project.active_children.collect{|s| [s.name, s.id.to_s] } }
100 end
105 end
101 @project.all_custom_fields.select(&:is_filter?).each do |field|
106 @project.all_custom_fields.select(&:is_filter?).each do |field|
102 case field.field_format
107 case field.field_format
103 when "string", "int"
108 when "string", "int"
104 options = { :type => :string, :order => 20 }
109 options = { :type => :string, :order => 20 }
105 when "text"
110 when "text"
106 options = { :type => :text, :order => 20 }
111 options = { :type => :text, :order => 20 }
107 when "list"
112 when "list"
108 options = { :type => :list_optional, :values => field.possible_values, :order => 20}
113 options = { :type => :list_optional, :values => field.possible_values, :order => 20}
109 when "date"
114 when "date"
110 options = { :type => :date, :order => 20 }
115 options = { :type => :date, :order => 20 }
111 when "bool"
116 when "bool"
112 options = { :type => :list, :values => [[l(:general_text_yes), "1"], [l(:general_text_no), "0"]], :order => 20 }
117 options = { :type => :list, :values => [[l(:general_text_yes), "1"], [l(:general_text_no), "0"]], :order => 20 }
113 end
118 end
114 @available_filters["cf_#{field.id}"] = options.merge({ :name => field.name })
119 @available_filters["cf_#{field.id}"] = options.merge({ :name => field.name })
115 end
120 end
116 # remove category filter if no category defined
121 # remove category filter if no category defined
117 @available_filters.delete "category_id" if @available_filters["category_id"][:values].empty?
122 @available_filters.delete "category_id" if @available_filters["category_id"][:values].empty?
118 end
123 end
119 @available_filters
124 @available_filters
120 end
125 end
121
126
122 def add_filter(field, operator, values)
127 def add_filter(field, operator, values)
123 # values must be an array
128 # values must be an array
124 return unless values and values.is_a? Array # and !values.first.empty?
129 return unless values and values.is_a? Array # and !values.first.empty?
125 # check if field is defined as an available filter
130 # check if field is defined as an available filter
126 if available_filters.has_key? field
131 if available_filters.has_key? field
127 filter_options = available_filters[field]
132 filter_options = available_filters[field]
128 # check if operator is allowed for that filter
133 # check if operator is allowed for that filter
129 #if @@operators_by_filter_type[filter_options[:type]].include? operator
134 #if @@operators_by_filter_type[filter_options[:type]].include? operator
130 # allowed_values = values & ([""] + (filter_options[:values] || []).collect {|val| val[1]})
135 # allowed_values = values & ([""] + (filter_options[:values] || []).collect {|val| val[1]})
131 # filters[field] = {:operator => operator, :values => allowed_values } if (allowed_values.first and !allowed_values.first.empty?) or ["o", "c", "!*", "*", "t"].include? operator
136 # filters[field] = {:operator => operator, :values => allowed_values } if (allowed_values.first and !allowed_values.first.empty?) or ["o", "c", "!*", "*", "t"].include? operator
132 #end
137 #end
133 filters[field] = {:operator => operator, :values => values }
138 filters[field] = {:operator => operator, :values => values }
134 end
139 end
135 end
140 end
136
141
137 def add_short_filter(field, expression)
142 def add_short_filter(field, expression)
138 return unless expression
143 return unless expression
139 parms = expression.scan(/^(o|c|\!|\*)?(.*)$/).first
144 parms = expression.scan(/^(o|c|\!|\*)?(.*)$/).first
140 add_filter field, (parms[0] || "="), [parms[1] || ""]
145 add_filter field, (parms[0] || "="), [parms[1] || ""]
141 end
146 end
142
147
143 def has_filter?(field)
148 def has_filter?(field)
144 filters and filters[field]
149 filters and filters[field]
145 end
150 end
146
151
147 def operator_for(field)
152 def operator_for(field)
148 has_filter?(field) ? filters[field][:operator] : nil
153 has_filter?(field) ? filters[field][:operator] : nil
149 end
154 end
150
155
151 def values_for(field)
156 def values_for(field)
152 has_filter?(field) ? filters[field][:values] : nil
157 has_filter?(field) ? filters[field][:values] : nil
153 end
158 end
154
159
155 def label_for(field)
160 def label_for(field)
156 label = @available_filters[field][:name] if @available_filters.has_key?(field)
161 label = @available_filters[field][:name] if @available_filters.has_key?(field)
157 label ||= field.gsub(/\_id$/, "")
162 label ||= field.gsub(/\_id$/, "")
158 end
163 end
159
164
160 def statement
165 def statement
161 sql = "1=1"
166 sql = "1=1"
162 if has_filter?("subproject_id")
167 if has_filter?("subproject_id")
163 subproject_ids = []
168 subproject_ids = []
164 if operator_for("subproject_id") == "="
169 if operator_for("subproject_id") == "="
165 subproject_ids = values_for("subproject_id").each(&:to_i)
170 subproject_ids = values_for("subproject_id").each(&:to_i)
166 else
171 else
167 subproject_ids = project.active_children.collect{|p| p.id}
172 subproject_ids = project.active_children.collect{|p| p.id}
168 end
173 end
169 sql << " AND #{Issue.table_name}.project_id IN (%d,%s)" % [project.id, subproject_ids.join(",")] if project
174 sql << " AND #{Issue.table_name}.project_id IN (%d,%s)" % [project.id, subproject_ids.join(",")] if project
170 else
175 else
171 sql << " AND #{Issue.table_name}.project_id=%d" % project.id if project
176 sql << " AND #{Issue.table_name}.project_id=%d" % project.id if project
172 end
177 end
173 filters.each_key do |field|
178 filters.each_key do |field|
174 next if field == "subproject_id"
179 next if field == "subproject_id"
175 v = values_for(field).clone
180 v = values_for(field).clone
176 next unless v and !v.empty?
181 next unless v and !v.empty?
177
182
178 sql = sql + " AND " unless sql.empty?
183 sql = sql + " AND " unless sql.empty?
179 sql << "("
184 sql << "("
180
185
181 if field =~ /^cf_(\d+)$/
186 if field =~ /^cf_(\d+)$/
182 # custom field
187 # custom field
183 db_table = CustomValue.table_name
188 db_table = CustomValue.table_name
184 db_field = "value"
189 db_field = "value"
185 sql << "#{db_table}.custom_field_id = #{$1} AND "
190 sql << "#{db_table}.custom_field_id = #{$1} AND "
186 else
191 else
187 # regular field
192 # regular field
188 db_table = Issue.table_name
193 db_table = Issue.table_name
189 db_field = field
194 db_field = field
190 end
195 end
191
196
192 # "me" value subsitution
197 # "me" value subsitution
193 if %w(assigned_to_id author_id).include?(field)
198 if %w(assigned_to_id author_id).include?(field)
194 v.push(executed_by ? executed_by.id.to_s : "0") if v.delete("me")
199 v.push(executed_by ? executed_by.id.to_s : "0") if v.delete("me")
195 end
200 end
196
201
197 case operator_for field
202 case operator_for field
198 when "="
203 when "="
199 sql = sql + "#{db_table}.#{db_field} IN (" + v.collect{|val| "'#{connection.quote_string(val)}'"}.join(",") + ")"
204 sql = sql + "#{db_table}.#{db_field} IN (" + v.collect{|val| "'#{connection.quote_string(val)}'"}.join(",") + ")"
200 when "!"
205 when "!"
201 sql = sql + "#{db_table}.#{db_field} NOT IN (" + v.collect{|val| "'#{connection.quote_string(val)}'"}.join(",") + ")"
206 sql = sql + "#{db_table}.#{db_field} NOT IN (" + v.collect{|val| "'#{connection.quote_string(val)}'"}.join(",") + ")"
202 when "!*"
207 when "!*"
203 sql = sql + "#{db_table}.#{db_field} IS NULL"
208 sql = sql + "#{db_table}.#{db_field} IS NULL"
204 when "*"
209 when "*"
205 sql = sql + "#{db_table}.#{db_field} IS NOT NULL"
210 sql = sql + "#{db_table}.#{db_field} IS NOT NULL"
206 when "o"
211 when "o"
207 sql = sql + "#{IssueStatus.table_name}.is_closed=#{connection.quoted_false}" if field == "status_id"
212 sql = sql + "#{IssueStatus.table_name}.is_closed=#{connection.quoted_false}" if field == "status_id"
208 when "c"
213 when "c"
209 sql = sql + "#{IssueStatus.table_name}.is_closed=#{connection.quoted_true}" if field == "status_id"
214 sql = sql + "#{IssueStatus.table_name}.is_closed=#{connection.quoted_true}" if field == "status_id"
210 when ">t-"
215 when ">t-"
211 sql = sql + "#{db_table}.#{db_field} BETWEEN '%s' AND '%s'" % [connection.quoted_date((Date.today - v.first.to_i).to_time), connection.quoted_date((Date.today + 1).to_time)]
216 sql = sql + "#{db_table}.#{db_field} BETWEEN '%s' AND '%s'" % [connection.quoted_date((Date.today - v.first.to_i).to_time), connection.quoted_date((Date.today + 1).to_time)]
212 when "<t-"
217 when "<t-"
213 sql = sql + "#{db_table}.#{db_field} <= '%s'" % connection.quoted_date((Date.today - v.first.to_i).to_time)
218 sql = sql + "#{db_table}.#{db_field} <= '%s'" % connection.quoted_date((Date.today - v.first.to_i).to_time)
214 when "t-"
219 when "t-"
215 sql = sql + "#{db_table}.#{db_field} BETWEEN '%s' AND '%s'" % [connection.quoted_date((Date.today - v.first.to_i).to_time), connection.quoted_date((Date.today - v.first.to_i + 1).to_time)]
220 sql = sql + "#{db_table}.#{db_field} BETWEEN '%s' AND '%s'" % [connection.quoted_date((Date.today - v.first.to_i).to_time), connection.quoted_date((Date.today - v.first.to_i + 1).to_time)]
216 when ">t+"
221 when ">t+"
217 sql = sql + "#{db_table}.#{db_field} >= '%s'" % connection.quoted_date((Date.today + v.first.to_i).to_time)
222 sql = sql + "#{db_table}.#{db_field} >= '%s'" % connection.quoted_date((Date.today + v.first.to_i).to_time)
218 when "<t+"
223 when "<t+"
219 sql = sql + "#{db_table}.#{db_field} BETWEEN '%s' AND '%s'" % [connection.quoted_date(Date.today.to_time), connection.quoted_date((Date.today + v.first.to_i + 1).to_time)]
224 sql = sql + "#{db_table}.#{db_field} BETWEEN '%s' AND '%s'" % [connection.quoted_date(Date.today.to_time), connection.quoted_date((Date.today + v.first.to_i + 1).to_time)]
220 when "t+"
225 when "t+"
221 sql = sql + "#{db_table}.#{db_field} BETWEEN '%s' AND '%s'" % [connection.quoted_date((Date.today + v.first.to_i).to_time), connection.quoted_date((Date.today + v.first.to_i + 1).to_time)]
226 sql = sql + "#{db_table}.#{db_field} BETWEEN '%s' AND '%s'" % [connection.quoted_date((Date.today + v.first.to_i).to_time), connection.quoted_date((Date.today + v.first.to_i + 1).to_time)]
222 when "t"
227 when "t"
223 sql = sql + "#{db_table}.#{db_field} BETWEEN '%s' AND '%s'" % [connection.quoted_date(Date.today.to_time), connection.quoted_date((Date.today+1).to_time)]
228 sql = sql + "#{db_table}.#{db_field} BETWEEN '%s' AND '%s'" % [connection.quoted_date(Date.today.to_time), connection.quoted_date((Date.today+1).to_time)]
224 when "~"
229 when "~"
225 sql = sql + "#{db_table}.#{db_field} LIKE '%#{connection.quote_string(v.first)}%'"
230 sql = sql + "#{db_table}.#{db_field} LIKE '%#{connection.quote_string(v.first)}%'"
226 when "!~"
231 when "!~"
227 sql = sql + "#{db_table}.#{db_field} NOT LIKE '%#{connection.quote_string(v.first)}%'"
232 sql = sql + "#{db_table}.#{db_field} NOT LIKE '%#{connection.quote_string(v.first)}%'"
228 end
233 end
229 sql << ")"
234 sql << ")"
230
235
231 end if filters and valid?
236 end if filters and valid?
232 sql
237 sql
233 end
238 end
234 end
239 end
@@ -1,159 +1,166
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2007 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
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
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
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.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require "digest/sha1"
18 require "digest/sha1"
19
19
20 class User < ActiveRecord::Base
20 class User < ActiveRecord::Base
21 # Account statuses
21 # Account statuses
22 STATUS_ACTIVE = 1
22 STATUS_ACTIVE = 1
23 STATUS_REGISTERED = 2
23 STATUS_REGISTERED = 2
24 STATUS_LOCKED = 3
24 STATUS_LOCKED = 3
25
25
26 has_many :memberships, :class_name => 'Member', :include => [ :project, :role ], :conditions => "#{Project.table_name}.status=#{Project::STATUS_ACTIVE}", :order => "#{Project.table_name}.name", :dependent => :delete_all
26 has_many :memberships, :class_name => 'Member', :include => [ :project, :role ], :conditions => "#{Project.table_name}.status=#{Project::STATUS_ACTIVE}", :order => "#{Project.table_name}.name", :dependent => :delete_all
27 has_many :projects, :through => :memberships
27 has_many :projects, :through => :memberships
28 has_many :custom_values, :dependent => :delete_all, :as => :customized
28 has_many :custom_values, :dependent => :delete_all, :as => :customized
29 has_one :preference, :dependent => :destroy, :class_name => 'UserPreference'
29 has_one :preference, :dependent => :destroy, :class_name => 'UserPreference'
30 has_one :rss_key, :dependent => :destroy, :class_name => 'Token', :conditions => "action='feeds'"
30 has_one :rss_key, :dependent => :destroy, :class_name => 'Token', :conditions => "action='feeds'"
31 belongs_to :auth_source
31 belongs_to :auth_source
32
32
33 attr_accessor :password, :password_confirmation
33 attr_accessor :password, :password_confirmation
34 attr_accessor :last_before_login_on
34 attr_accessor :last_before_login_on
35 # Prevents unauthorized assignments
35 # Prevents unauthorized assignments
36 attr_protected :login, :admin, :password, :password_confirmation, :hashed_password
36 attr_protected :login, :admin, :password, :password_confirmation, :hashed_password
37
37
38 validates_presence_of :login, :firstname, :lastname, :mail
38 validates_presence_of :login, :firstname, :lastname, :mail
39 validates_uniqueness_of :login, :mail
39 validates_uniqueness_of :login, :mail
40 # Login must contain lettres, numbers, underscores only
40 # Login must contain lettres, numbers, underscores only
41 validates_format_of :login, :with => /^[a-z0-9_\-@\.]+$/i
41 validates_format_of :login, :with => /^[a-z0-9_\-@\.]+$/i
42 validates_length_of :login, :maximum => 30
42 validates_length_of :login, :maximum => 30
43 validates_format_of :firstname, :lastname, :with => /^[\w\s\'\-]*$/i
43 validates_format_of :firstname, :lastname, :with => /^[\w\s\'\-]*$/i
44 validates_length_of :firstname, :lastname, :maximum => 30
44 validates_length_of :firstname, :lastname, :maximum => 30
45 validates_format_of :mail, :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i
45 validates_format_of :mail, :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i
46 validates_length_of :mail, :maximum => 60
46 validates_length_of :mail, :maximum => 60
47 # Password length between 4 and 12
47 # Password length between 4 and 12
48 validates_length_of :password, :in => 4..12, :allow_nil => true
48 validates_length_of :password, :in => 4..12, :allow_nil => true
49 validates_confirmation_of :password, :allow_nil => true
49 validates_confirmation_of :password, :allow_nil => true
50 validates_associated :custom_values, :on => :update
50 validates_associated :custom_values, :on => :update
51
51
52 def before_save
52 def before_save
53 # update hashed_password if password was set
53 # update hashed_password if password was set
54 self.hashed_password = User.hash_password(self.password) if self.password
54 self.hashed_password = User.hash_password(self.password) if self.password
55 end
55 end
56
56
57 def self.active
57 def self.active
58 with_scope :find => { :conditions => [ "status = ?", STATUS_ACTIVE ] } do
58 with_scope :find => { :conditions => [ "status = ?", STATUS_ACTIVE ] } do
59 yield
59 yield
60 end
60 end
61 end
61 end
62
62
63 def self.find_active(*args)
63 def self.find_active(*args)
64 active do
64 active do
65 find(*args)
65 find(*args)
66 end
66 end
67 end
67 end
68
68
69 # Returns the user that matches provided login and password, or nil
69 # Returns the user that matches provided login and password, or nil
70 def self.try_to_login(login, password)
70 def self.try_to_login(login, password)
71 user = find(:first, :conditions => ["login=?", login])
71 user = find(:first, :conditions => ["login=?", login])
72 if user
72 if user
73 # user is already in local database
73 # user is already in local database
74 return nil if !user.active?
74 return nil if !user.active?
75 if user.auth_source
75 if user.auth_source
76 # user has an external authentication method
76 # user has an external authentication method
77 return nil unless user.auth_source.authenticate(login, password)
77 return nil unless user.auth_source.authenticate(login, password)
78 else
78 else
79 # authentication with local password
79 # authentication with local password
80 return nil unless User.hash_password(password) == user.hashed_password
80 return nil unless User.hash_password(password) == user.hashed_password
81 end
81 end
82 else
82 else
83 # user is not yet registered, try to authenticate with available sources
83 # user is not yet registered, try to authenticate with available sources
84 attrs = AuthSource.authenticate(login, password)
84 attrs = AuthSource.authenticate(login, password)
85 if attrs
85 if attrs
86 onthefly = new(*attrs)
86 onthefly = new(*attrs)
87 onthefly.login = login
87 onthefly.login = login
88 onthefly.language = Setting.default_language
88 onthefly.language = Setting.default_language
89 if onthefly.save
89 if onthefly.save
90 user = find(:first, :conditions => ["login=?", login])
90 user = find(:first, :conditions => ["login=?", login])
91 logger.info("User '#{user.login}' created on the fly.") if logger
91 logger.info("User '#{user.login}' created on the fly.") if logger
92 end
92 end
93 end
93 end
94 end
94 end
95 user.update_attribute(:last_login_on, Time.now) if user
95 user.update_attribute(:last_login_on, Time.now) if user
96 user
96 user
97
97
98 rescue => text
98 rescue => text
99 raise text
99 raise text
100 end
100 end
101
101
102 # Return user's full name for display
102 # Return user's full name for display
103 def display_name
103 def display_name
104 firstname + " " + lastname
104 firstname + " " + lastname
105 end
105 end
106
106
107 def name
107 def name
108 display_name
108 display_name
109 end
109 end
110
110
111 def active?
111 def active?
112 self.status == STATUS_ACTIVE
112 self.status == STATUS_ACTIVE
113 end
113 end
114
114
115 def registered?
115 def registered?
116 self.status == STATUS_REGISTERED
116 self.status == STATUS_REGISTERED
117 end
117 end
118
118
119 def locked?
119 def locked?
120 self.status == STATUS_LOCKED
120 self.status == STATUS_LOCKED
121 end
121 end
122
122
123 def check_password?(clear_password)
123 def check_password?(clear_password)
124 User.hash_password(clear_password) == self.hashed_password
124 User.hash_password(clear_password) == self.hashed_password
125 end
125 end
126
126
127 def role_for_project(project)
127 def role_for_project(project)
128 return nil unless project
128 member = memberships.detect {|m| m.project_id == project.id}
129 member = memberships.detect {|m| m.project_id == project.id}
129 member ? member.role : nil
130 member ? member.role : nil
130 end
131 end
131
132
133 def authorized_to(project, action)
134 return true if self.admin?
135 role = role_for_project(project)
136 role && Permission.allowed_to_role(action, role)
137 end
138
132 def pref
139 def pref
133 self.preference ||= UserPreference.new(:user => self)
140 self.preference ||= UserPreference.new(:user => self)
134 end
141 end
135
142
136 def get_or_create_rss_key
143 def get_or_create_rss_key
137 self.rss_key || Token.create(:user => self, :action => 'feeds')
144 self.rss_key || Token.create(:user => self, :action => 'feeds')
138 end
145 end
139
146
140 def self.find_by_rss_key(key)
147 def self.find_by_rss_key(key)
141 token = Token.find_by_value(key)
148 token = Token.find_by_value(key)
142 token && token.user.active? ? token.user : nil
149 token && token.user.active? ? token.user : nil
143 end
150 end
144
151
145 def self.find_by_autologin_key(key)
152 def self.find_by_autologin_key(key)
146 token = Token.find_by_action_and_value('autologin', key)
153 token = Token.find_by_action_and_value('autologin', key)
147 token && (token.created_on > Setting.autologin.to_i.day.ago) && token.user.active? ? token.user : nil
154 token && (token.created_on > Setting.autologin.to_i.day.ago) && token.user.active? ? token.user : nil
148 end
155 end
149
156
150 def <=>(user)
157 def <=>(user)
151 lastname == user.lastname ? firstname <=> user.firstname : lastname <=> user.lastname
158 lastname == user.lastname ? firstname <=> user.firstname : lastname <=> user.lastname
152 end
159 end
153
160
154 private
161 private
155 # Return password digest
162 # Return password digest
156 def self.hash_password(clear_password)
163 def self.hash_password(clear_password)
157 Digest::SHA1.hexdigest(clear_password || "")
164 Digest::SHA1.hexdigest(clear_password || "")
158 end
165 end
159 end
166 end
@@ -1,144 +1,155
1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
2 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
2 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
3 <head>
3 <head>
4 <title><%= Setting.app_title + (@html_title ? ": #{@html_title}" : "") %></title>
4 <title><%= Setting.app_title + (@html_title ? ": #{@html_title}" : "") %></title>
5 <meta http-equiv="content-type" content="text/html; charset=utf-8" />
5 <meta http-equiv="content-type" content="text/html; charset=utf-8" />
6 <meta name="description" content="redMine" />
6 <meta name="description" content="redMine" />
7 <meta name="keywords" content="issue,bug,tracker" />
7 <meta name="keywords" content="issue,bug,tracker" />
8 <!--[if IE]>
8 <!--[if IE]>
9 <style type="text/css">
9 <style type="text/css">
10 body {behavior: url(<%= stylesheet_path "csshover.htc" %>);}
10 body {behavior: url(<%= stylesheet_path "csshover.htc" %>);}
11 </style>
11 </style>
12 <![endif]-->
12 <![endif]-->
13 <%= stylesheet_link_tag "application" %>
13 <%= stylesheet_link_tag "application" %>
14 <%= stylesheet_link_tag "print", :media => "print" %>
14 <%= stylesheet_link_tag "print", :media => "print" %>
15 <%= javascript_include_tag :defaults %>
15 <%= javascript_include_tag :defaults %>
16 <%= javascript_include_tag 'menu' %>
16 <%= javascript_include_tag 'menu' %>
17 <%= stylesheet_link_tag 'jstoolbar' %>
17 <%= stylesheet_link_tag 'jstoolbar' %>
18 <!-- page specific tags --><%= yield :header_tags %>
18 <!-- page specific tags --><%= yield :header_tags %>
19 </head>
19 </head>
20
20
21 <body>
21 <body>
22 <div id="container" >
22 <div id="container" >
23
23
24 <div id="header">
24 <div id="header">
25 <div style="float: left;">
25 <div style="float: left;">
26 <h1><%= Setting.app_title %></h1>
26 <h1><%= Setting.app_title %></h1>
27 <h2><%= Setting.app_subtitle %></h2>
27 <h2><%= Setting.app_subtitle %></h2>
28 </div>
28 </div>
29 <div style="float: right; padding-right: 1em; padding-top: 0.2em;">
29 <div style="float: right; padding-right: 1em; padding-top: 0.2em;">
30 <% if loggedin? %><small><%=l(:label_logged_as)%> <strong><%= @logged_in_user.login %></strong> -</small><% end %>
30 <% if loggedin? %><small><%=l(:label_logged_as)%> <strong><%= @logged_in_user.login %></strong> -</small><% end %>
31 <small><%= toggle_link l(:label_search), 'quick-search-form', :focus => 'quick-search-input' %></small>
31 <small><%= toggle_link l(:label_search), 'quick-search-form', :focus => 'quick-search-input' %></small>
32 <% form_tag({:controller => 'search', :action => 'index', :id => @project}, :method => :get, :id => 'quick-search-form', :style => "display:none;" ) do %>
32 <% form_tag({:controller => 'search', :action => 'index', :id => @project}, :method => :get, :id => 'quick-search-form', :style => "display:none;" ) do %>
33 <%= text_field_tag 'q', @question, :size => 15, :class => 'small', :id => 'quick-search-input' %>
33 <%= text_field_tag 'q', @question, :size => 15, :class => 'small', :id => 'quick-search-input' %>
34 <% end %>
34 <% end %>
35 </div>
35 </div>
36 </div>
36 </div>
37
37
38 <div id="navigation">
38 <div id="navigation">
39 <ul>
39 <ul>
40 <li><%= link_to l(:label_home), { :controller => 'welcome' }, :class => "icon icon-home" %></li>
40 <li><%= link_to l(:label_home), { :controller => 'welcome' }, :class => "icon icon-home" %></li>
41 <li><%= link_to l(:label_my_page), { :controller => 'my', :action => 'page'}, :class => "icon icon-mypage" %></li>
41 <li><%= link_to l(:label_my_page), { :controller => 'my', :action => 'page'}, :class => "icon icon-mypage" %></li>
42
42
43 <% if loggedin? and @logged_in_user.memberships.any? %>
43 <% if loggedin? and @logged_in_user.memberships.any? %>
44 <li class="submenu"><%= link_to l(:label_project_plural), { :controller => 'projects' }, :class => "icon icon-projects", :onmouseover => "buttonMouseover(event, 'menuAllProjects');" %></li>
44 <li class="submenu"><%= link_to l(:label_project_plural), { :controller => 'projects' }, :class => "icon icon-projects", :onmouseover => "buttonMouseover(event, 'menuAllProjects');" %></li>
45 <% else %>
45 <% else %>
46 <li><%= link_to l(:label_project_plural), { :controller => 'projects' }, :class => "icon icon-projects" %></li>
46 <li><%= link_to l(:label_project_plural), { :controller => 'projects' }, :class => "icon icon-projects" %></li>
47 <% end %>
47 <% end %>
48
48
49 <% unless @project.nil? || @project.id.nil? %>
49 <% unless @project.nil? || @project.id.nil? %>
50 <li class="submenu"><%= link_to @project.name, { :controller => 'projects', :action => 'show', :id => @project }, :class => "icon icon-projects", :onmouseover => "buttonMouseover(event, 'menuProject');" %></li>
50 <li class="submenu"><%= link_to @project.name, { :controller => 'projects', :action => 'show', :id => @project }, :class => "icon icon-projects", :onmouseover => "buttonMouseover(event, 'menuProject');" %></li>
51 <% end %>
51 <% end %>
52
52
53 <% if loggedin? %>
53 <% if loggedin? %>
54 <li><%= link_to l(:label_my_account), { :controller => 'my', :action => 'account' }, :class => "icon icon-user" %></li>
54 <li><%= link_to l(:label_my_account), { :controller => 'my', :action => 'account' }, :class => "icon icon-user" %></li>
55 <% end %>
55 <% end %>
56
56
57 <% if admin_loggedin? %>
57 <% if admin_loggedin? %>
58 <li class="submenu"><%= link_to l(:label_administration), { :controller => 'admin' }, :class => "icon icon-admin", :onmouseover => "buttonMouseover(event, 'menuAdmin');" %></li>
58 <li class="submenu"><%= link_to l(:label_administration), { :controller => 'admin' }, :class => "icon icon-admin", :onmouseover => "buttonMouseover(event, 'menuAdmin');" %></li>
59 <% end %>
59 <% end %>
60
60
61 <li class="right"><%= link_to l(:label_help), { :controller => 'help', :ctrl => params[:controller], :page => params[:action] }, :onclick => "window.open(this.href); return false;", :class => "icon icon-help" %></li>
61 <li class="right"><%= link_to l(:label_help), { :controller => 'help', :ctrl => params[:controller], :page => params[:action] }, :onclick => "window.open(this.href); return false;", :class => "icon icon-help" %></li>
62
62
63 <% if loggedin? %>
63 <% if loggedin? %>
64 <li class="right"><%= link_to l(:label_logout), { :controller => 'account', :action => 'logout' }, :class => "icon icon-user" %></li>
64 <li class="right"><%= link_to l(:label_logout), { :controller => 'account', :action => 'logout' }, :class => "icon icon-user" %></li>
65 <% else %>
65 <% else %>
66 <li class="right"><%= link_to l(:label_login), { :controller => 'account', :action => 'login' }, :class => "icon icon-user" %></li>
66 <li class="right"><%= link_to l(:label_login), { :controller => 'account', :action => 'login' }, :class => "icon icon-user" %></li>
67 <% end %>
67 <% end %>
68 </ul>
68 </ul>
69 </div>
69 </div>
70
70
71 <% if admin_loggedin? %>
71 <% if admin_loggedin? %>
72 <%= render :partial => 'admin/menu' %>
72 <%= render :partial => 'admin/menu' %>
73 <% end %>
73 <% end %>
74
74
75 <% unless @project.nil? || @project.id.nil? %>
75 <% unless @project.nil? || @project.id.nil? %>
76 <div id="menuProject" class="menu" onmouseover="menuMouseover(event)">
76 <div id="menuProject" class="menu" onmouseover="menuMouseover(event)">
77 <%= link_to l(:label_calendar), {:controller => 'projects', :action => 'calendar', :id => @project }, :class => "menuItem" %>
77 <%= link_to l(:label_calendar), {:controller => 'projects', :action => 'calendar', :id => @project }, :class => "menuItem" %>
78 <%= link_to l(:label_gantt), {:controller => 'projects', :action => 'gantt', :id => @project }, :class => "menuItem" %>
78 <%= link_to l(:label_gantt), {:controller => 'projects', :action => 'gantt', :id => @project }, :class => "menuItem" %>
79 <%= link_to l(:label_issue_plural), {:controller => 'projects', :action => 'list_issues', :id => @project }, :class => "menuItem" %>
79 <%= link_to l(:label_issue_plural), {:controller => 'projects', :action => 'list_issues', :id => @project }, :class => "menuItem" %>
80 <% if @project && authorize_for('projects', 'add_issue') %>
81 <a class="menuItem" href="#" onmouseover="menuItemMouseover(event,'menuNewIssue');" onclick="this.blur(); return false;"><span class="menuItemText"><%= l(:label_issue_new) %></span><span class="menuItemArrow">&#9654;</span></a>
82 <% end %>
80 <%= link_to l(:label_report_plural), {:controller => 'reports', :action => 'issue_report', :id => @project }, :class => "menuItem" %>
83 <%= link_to l(:label_report_plural), {:controller => 'reports', :action => 'issue_report', :id => @project }, :class => "menuItem" %>
81 <%= link_to l(:label_activity), {:controller => 'projects', :action => 'activity', :id => @project }, :class => "menuItem" %>
84 <%= link_to l(:label_activity), {:controller => 'projects', :action => 'activity', :id => @project }, :class => "menuItem" %>
82 <%= link_to l(:label_news_plural), {:controller => 'projects', :action => 'list_news', :id => @project }, :class => "menuItem" %>
85 <%= link_to l(:label_news_plural), {:controller => 'projects', :action => 'list_news', :id => @project }, :class => "menuItem" %>
83 <%= link_to l(:label_change_log), {:controller => 'projects', :action => 'changelog', :id => @project }, :class => "menuItem" %>
86 <%= link_to l(:label_change_log), {:controller => 'projects', :action => 'changelog', :id => @project }, :class => "menuItem" %>
84 <%= link_to l(:label_roadmap), {:controller => 'projects', :action => 'roadmap', :id => @project }, :class => "menuItem" %>
87 <%= link_to l(:label_roadmap), {:controller => 'projects', :action => 'roadmap', :id => @project }, :class => "menuItem" %>
85 <%= link_to l(:label_document_plural), {:controller => 'projects', :action => 'list_documents', :id => @project }, :class => "menuItem" %>
88 <%= link_to l(:label_document_plural), {:controller => 'projects', :action => 'list_documents', :id => @project }, :class => "menuItem" %>
86 <%= link_to l(:label_wiki), {:controller => 'wiki', :id => @project, :page => nil }, :class => "menuItem" if @project.wiki and !@project.wiki.new_record? %>
89 <%= link_to l(:label_wiki), {:controller => 'wiki', :id => @project, :page => nil }, :class => "menuItem" if @project.wiki and !@project.wiki.new_record? %>
87 <%= link_to l(:label_board_plural), {:controller => 'boards', :project_id => @project }, :class => "menuItem" unless @project.boards.empty? %>
90 <%= link_to l(:label_board_plural), {:controller => 'boards', :project_id => @project }, :class => "menuItem" unless @project.boards.empty? %>
88 <%= link_to l(:label_attachment_plural), {:controller => 'projects', :action => 'list_files', :id => @project }, :class => "menuItem" %>
91 <%= link_to l(:label_attachment_plural), {:controller => 'projects', :action => 'list_files', :id => @project }, :class => "menuItem" %>
89 <%= link_to l(:label_search), {:controller => 'search', :action => 'index', :id => @project }, :class => "menuItem" %>
92 <%= link_to l(:label_search), {:controller => 'search', :action => 'index', :id => @project }, :class => "menuItem" %>
90 <%= link_to l(:label_repository), {:controller => 'repositories', :action => 'show', :id => @project}, :class => "menuItem" if @project.repository and !@project.repository.new_record? %>
93 <%= link_to l(:label_repository), {:controller => 'repositories', :action => 'show', :id => @project}, :class => "menuItem" if @project.repository and !@project.repository.new_record? %>
91 <%= link_to_if_authorized l(:label_settings), {:controller => 'projects', :action => 'settings', :id => @project }, :class => "menuItem" %>
94 <%= link_to_if_authorized l(:label_settings), {:controller => 'projects', :action => 'settings', :id => @project }, :class => "menuItem" %>
92 </div>
95 </div>
93 <% end %>
96 <% end %>
94
97
98 <% if @project && authorize_for('projects', 'add_issue') %>
99 <div id="menuNewIssue" class="menu" onmouseover="menuMouseover(event)">
100 <% Tracker.find(:all, :order => 'position').each do |tracker| %>
101 <%= link_to tracker.name, {:controller => 'projects', :action => 'add_issue', :id => @project, :tracker_id => tracker}, :class => "menuItem" %>
102 <% end %>
103 </div>
104 <% end %>
105
95 <% if loggedin? and @logged_in_user.memberships.any? %>
106 <% if loggedin? and @logged_in_user.memberships.any? %>
96 <div id="menuAllProjects" class="menu" onmouseover="menuMouseover(event)">
107 <div id="menuAllProjects" class="menu" onmouseover="menuMouseover(event)">
97 <%= link_to l(:label_project_all), {:controller => 'projects' }, :class => "menuItem" %>
108 <%= link_to l(:label_project_all), {:controller => 'projects' }, :class => "menuItem" %>
98 <% @logged_in_user.memberships.find(:all, :limit => 20).each do |membership| %>
109 <% @logged_in_user.memberships.find(:all, :limit => 20).each do |membership| %>
99 <%= link_to membership.project.name, {:controller => 'projects',:action => 'show', :id => membership.project }, :class => "menuItem" %>
110 <%= link_to membership.project.name, {:controller => 'projects',:action => 'show', :id => membership.project }, :class => "menuItem" %>
100 <% end %>
111 <% end %>
101 </div>
112 </div>
102 <% end %>
113 <% end %>
103
114
104 <div id="subcontent">
115 <div id="subcontent">
105
116
106 <% unless @project.nil? || @project.id.nil? %>
117 <% unless @project.nil? || @project.id.nil? %>
107 <h2><%= @project.name %></h2>
118 <h2><%= @project.name %></h2>
108 <ul class="menublock">
119 <ul class="menublock">
109 <li><%= link_to l(:label_overview), :controller => 'projects', :action => 'show', :id => @project %></li>
120 <li><%= link_to l(:label_overview), :controller => 'projects', :action => 'show', :id => @project %></li>
110 <li><%= link_to l(:label_calendar), :controller => 'projects', :action => 'calendar', :id => @project %></li>
121 <li><%= link_to l(:label_calendar), :controller => 'projects', :action => 'calendar', :id => @project %></li>
111 <li><%= link_to l(:label_gantt), :controller => 'projects', :action => 'gantt', :id => @project %></li>
122 <li><%= link_to l(:label_gantt), :controller => 'projects', :action => 'gantt', :id => @project %></li>
112 <li><%= link_to l(:label_issue_plural), :controller => 'projects', :action => 'list_issues', :id => @project %></li>
123 <li><%= link_to l(:label_issue_plural), :controller => 'projects', :action => 'list_issues', :id => @project %></li>
113 <li><%= link_to l(:label_report_plural), :controller => 'reports', :action => 'issue_report', :id => @project %></li>
124 <li><%= link_to l(:label_report_plural), :controller => 'reports', :action => 'issue_report', :id => @project %></li>
114 <li><%= link_to l(:label_activity), :controller => 'projects', :action => 'activity', :id => @project %></li>
125 <li><%= link_to l(:label_activity), :controller => 'projects', :action => 'activity', :id => @project %></li>
115 <li><%= link_to l(:label_news_plural), :controller => 'projects', :action => 'list_news', :id => @project %></li>
126 <li><%= link_to l(:label_news_plural), :controller => 'projects', :action => 'list_news', :id => @project %></li>
116 <li><%= link_to l(:label_change_log), :controller => 'projects', :action => 'changelog', :id => @project %></li>
127 <li><%= link_to l(:label_change_log), :controller => 'projects', :action => 'changelog', :id => @project %></li>
117 <li><%= link_to l(:label_roadmap), :controller => 'projects', :action => 'roadmap', :id => @project %></li>
128 <li><%= link_to l(:label_roadmap), :controller => 'projects', :action => 'roadmap', :id => @project %></li>
118 <li><%= link_to l(:label_document_plural), :controller => 'projects', :action => 'list_documents', :id => @project %></li>
129 <li><%= link_to l(:label_document_plural), :controller => 'projects', :action => 'list_documents', :id => @project %></li>
119 <%= content_tag("li", link_to(l(:label_wiki), :controller => 'wiki', :id => @project, :page => nil)) if @project.wiki and !@project.wiki.new_record? %>
130 <%= content_tag("li", link_to(l(:label_wiki), :controller => 'wiki', :id => @project, :page => nil)) if @project.wiki and !@project.wiki.new_record? %>
120 <%= content_tag("li", link_to(l(:label_board_plural), :controller => 'boards', :project_id => @project)) unless @project.boards.empty? %>
131 <%= content_tag("li", link_to(l(:label_board_plural), :controller => 'boards', :project_id => @project)) unless @project.boards.empty? %>
121 <li><%= link_to l(:label_attachment_plural), :controller => 'projects', :action => 'list_files', :id => @project %></li>
132 <li><%= link_to l(:label_attachment_plural), :controller => 'projects', :action => 'list_files', :id => @project %></li>
122 <li><%= link_to l(:label_search), :controller => 'search', :action => 'index', :id => @project %></li>
133 <li><%= link_to l(:label_search), :controller => 'search', :action => 'index', :id => @project %></li>
123 <%= content_tag("li", link_to(l(:label_repository), :controller => 'repositories', :action => 'show', :id => @project)) if @project.repository and !@project.repository.new_record? %>
134 <%= content_tag("li", link_to(l(:label_repository), :controller => 'repositories', :action => 'show', :id => @project)) if @project.repository and !@project.repository.new_record? %>
124 <li><%= link_to_if_authorized l(:label_settings), :controller => 'projects', :action => 'settings', :id => @project %></li>
135 <li><%= link_to_if_authorized l(:label_settings), :controller => 'projects', :action => 'settings', :id => @project %></li>
125 </ul>
136 </ul>
126 <% end %>
137 <% end %>
127 </div>
138 </div>
128
139
129 <div id="content">
140 <div id="content">
130 <% if flash[:notice] %><p style="color: green"><%= flash[:notice] %></p><% end %>
141 <% if flash[:notice] %><p style="color: green"><%= flash[:notice] %></p><% end %>
131 <%= yield %>
142 <%= yield %>
132 </div>
143 </div>
133
144
134 <div id="ajax-indicator" style="display:none;">
145 <div id="ajax-indicator" style="display:none;">
135 <span><%= l(:label_loading) %></span>
146 <span><%= l(:label_loading) %></span>
136 </div>
147 </div>
137
148
138 <div id="footer">
149 <div id="footer">
139 <p><a href="http://redmine.rubyforge.org/">redMine</a> <small><%= Redmine::VERSION %> &copy 2006-2007 Jean-Philippe Lang</small></p>
150 <p><a href="http://redmine.rubyforge.org/">redMine</a> <small><%= Redmine::VERSION %> &copy 2006-2007 Jean-Philippe Lang</small></p>
140 </div>
151 </div>
141
152
142 </div>
153 </div>
143 </body>
154 </body>
144 </html> No newline at end of file
155 </html>
@@ -1,88 +1,84
1 <% if @query.new_record? %>
1 <% if @query.new_record? %>
2 <div class="contextual">
2 <div class="contextual">
3 <%= render :partial => 'issues/add_shortcut', :locals => {:trackers => @trackers } %>
3 <%= link_to l(:label_query_plural), :controller => 'queries', :project_id => @project %>
4 </div>
4 </div>
5 <h2><%=l(:label_issue_plural)%></h2>
5 <h2><%=l(:label_issue_plural)%></h2>
6
6
7 <% form_tag({:action => 'list_issues'}, :id => 'query_form') do %>
7 <% form_tag({:action => 'list_issues'}, :id => 'query_form') do %>
8 <%= render :partial => 'queries/filters', :locals => {:query => @query} %>
8 <%= render :partial => 'queries/filters', :locals => {:query => @query} %>
9 <% end %>
9 <% end %>
10 <div class="contextual">
10 <div class="contextual">
11 <%= link_to_remote l(:button_apply),
11 <%= link_to_remote l(:button_apply),
12 { :url => { :controller => 'projects', :action => 'list_issues', :id => @project, :set_filter => 1 },
12 { :url => { :controller => 'projects', :action => 'list_issues', :id => @project, :set_filter => 1 },
13 :update => "content",
13 :update => "content",
14 :with => "Form.serialize('query_form')"
14 :with => "Form.serialize('query_form')"
15 }, :class => 'icon icon-edit' %>
15 }, :class => 'icon icon-edit' %>
16
16
17 <%= link_to_remote l(:button_clear),
17 <%= link_to_remote l(:button_clear),
18 { :url => {:controller => 'projects', :action => 'list_issues', :id => @project, :set_filter => 1},
18 { :url => {:controller => 'projects', :action => 'list_issues', :id => @project, :set_filter => 1},
19 :update => "content",
19 :update => "content",
20 }, :class => 'icon icon-reload' %>
20 }, :class => 'icon icon-reload' %>
21
21
22 <% if authorize_for('projects', 'add_query') %>
22 <% if loggedin? %>
23 <%= link_to_remote l(:button_save),
23 <%= link_to_remote l(:button_save),
24 { :url => { :controller => 'projects', :action => "add_query", :id => @project },
24 { :url => { :controller => 'queries', :action => 'new', :project_id => @project },
25 :method => 'get',
25 :method => 'get',
26 :update => "content",
26 :update => "content",
27 :with => "Form.serialize('query_form')"
27 :with => "Form.serialize('query_form')"
28 }, :class => 'icon icon-save' %>
28 }, :class => 'icon icon-save' %>
29 <% end %>
29 <% end %>
30 </div>
30 </div>
31 <br />
31 <br />
32 <% else %>
32 <% else %>
33 <div class="contextual">
33 <div class="contextual">
34 <%= render :partial => 'issues/add_shortcut', :locals => {:trackers => @trackers } %>
34 <%= link_to l(:label_query_plural), {:controller => 'queries', :project_id => @project} %> |
35 <%= link_to l(:button_clear), {:controller => 'projects', :action => 'list_issues', :id => @project, :set_filter => 1}, :class => 'icon icon-reload' %>
35 <%= link_to l(:label_issue_view_all), {:controller => 'projects', :action => 'list_issues', :id => @project, :set_filter => 1} %>
36 <% if authorize_for('projects', 'add_query') %>
37 <%= link_to l(:button_edit), {:controller => 'queries', :action => 'edit', :id => @query}, :class => 'icon icon-edit' %>
38 <%= link_to l(:button_delete), {:controller => 'queries', :action => 'destroy', :id => @query}, :confirm => l(:text_are_you_sure), :method => :post, :class => 'icon icon-del' %>
39 <% end %>
40 </div>
36 </div>
41 <h2><%= @query.name %></h2>
37 <h2><%= @query.name %></h2>
42 <% end %>
38 <% end %>
43 <%= error_messages_for 'query' %>
39 <%= error_messages_for 'query' %>
44 <% if @query.valid? %>
40 <% if @query.valid? %>
45 <% if @issues.empty? %>
41 <% if @issues.empty? %>
46 <p><i><%= l(:label_no_data) %></i></p>
42 <p><i><%= l(:label_no_data) %></i></p>
47 <% else %>
43 <% else %>
48 &nbsp;
44 &nbsp;
49 <% form_tag({:controller => 'projects', :action => 'move_issues', :id => @project}, :id => 'issues_form' ) do %>
45 <% form_tag({:controller => 'projects', :action => 'move_issues', :id => @project}, :id => 'issues_form' ) do %>
50 <table class="list">
46 <table class="list">
51 <thead><tr>
47 <thead><tr>
52 <th></th>
48 <th></th>
53 <%= sort_header_tag("#{Issue.table_name}.id", :caption => '#') %>
49 <%= sort_header_tag("#{Issue.table_name}.id", :caption => '#') %>
54 <%= sort_header_tag("#{Issue.table_name}.tracker_id", :caption => l(:field_tracker)) %>
50 <%= sort_header_tag("#{Issue.table_name}.tracker_id", :caption => l(:field_tracker)) %>
55 <%= sort_header_tag("#{IssueStatus.table_name}.name", :caption => l(:field_status)) %>
51 <%= sort_header_tag("#{IssueStatus.table_name}.name", :caption => l(:field_status)) %>
56 <%= sort_header_tag("#{Issue.table_name}.priority_id", :caption => l(:field_priority)) %>
52 <%= sort_header_tag("#{Issue.table_name}.priority_id", :caption => l(:field_priority)) %>
57 <th><%=l(:field_subject)%></th>
53 <th><%=l(:field_subject)%></th>
58 <%= sort_header_tag("#{User.table_name}.lastname", :caption => l(:field_assigned_to)) %>
54 <%= sort_header_tag("#{User.table_name}.lastname", :caption => l(:field_assigned_to)) %>
59 <%= sort_header_tag("#{Issue.table_name}.updated_on", :caption => l(:field_updated_on)) %>
55 <%= sort_header_tag("#{Issue.table_name}.updated_on", :caption => l(:field_updated_on)) %>
60 </tr></thead>
56 </tr></thead>
61 <tbody>
57 <tbody>
62 <% for issue in @issues %>
58 <% for issue in @issues %>
63 <tr class="<%= cycle("odd", "even") %>">
59 <tr class="<%= cycle("odd", "even") %>">
64 <th style="width:15px;"><%= check_box_tag "issue_ids[]", issue.id, false, :id => "issue_#{issue.id}" %></th>
60 <th style="width:15px;"><%= check_box_tag "issue_ids[]", issue.id, false, :id => "issue_#{issue.id}" %></th>
65 <td align="center"><%= link_to issue.id, :controller => 'issues', :action => 'show', :id => issue %></td>
61 <td align="center"><%= link_to issue.id, :controller => 'issues', :action => 'show', :id => issue %></td>
66 <td align="center"><%= issue.tracker.name %></td>
62 <td align="center"><%= issue.tracker.name %></td>
67 <td><div class="square" style="background:#<%= issue.status.html_color %>;"></div> <%= issue.status.name %></td>
63 <td><div class="square" style="background:#<%= issue.status.html_color %>;"></div> <%= issue.status.name %></td>
68 <td align="center"><%= issue.priority.name %></td>
64 <td align="center"><%= issue.priority.name %></td>
69 <td><%= "#{issue.project.name} - " unless @project && @project == issue.project %><%= link_to h(issue.subject), :controller => 'issues', :action => 'show', :id => issue %></td>
65 <td><%= "#{issue.project.name} - " unless @project && @project == issue.project %><%= link_to h(issue.subject), :controller => 'issues', :action => 'show', :id => issue %></td>
70 <td align="center"><%= issue.assigned_to.name if issue.assigned_to %></td>
66 <td align="center"><%= issue.assigned_to.name if issue.assigned_to %></td>
71 <td align="center"><%= format_time(issue.updated_on) %></td>
67 <td align="center"><%= format_time(issue.updated_on) %></td>
72 </tr>
68 </tr>
73 <% end %>
69 <% end %>
74 </tbody>
70 </tbody>
75 </table>
71 </table>
76 <div class="contextual">
72 <div class="contextual">
77 <%= l(:label_export_to) %>
73 <%= l(:label_export_to) %>
78 <%= link_to 'CSV', {:action => 'export_issues_csv', :id => @project}, :class => 'icon icon-csv' %>,
74 <%= link_to 'CSV', {:action => 'export_issues_csv', :id => @project}, :class => 'icon icon-csv' %>,
79 <%= link_to 'PDF', {:action => 'export_issues_pdf', :id => @project}, :class => 'icon icon-pdf' %>
75 <%= link_to 'PDF', {:action => 'export_issues_pdf', :id => @project}, :class => 'icon icon-pdf' %>
80 </div>
76 </div>
81 <p><%= submit_tag l(:button_move), :class => "button-small" %>
77 <p><%= submit_tag l(:button_move), :class => "button-small" %>
82 &nbsp;
78 &nbsp;
83 <%= pagination_links_full @issue_pages %>
79 <%= pagination_links_full @issue_pages %>
84 [ <%= @issue_pages.current.first_item %> - <%= @issue_pages.current.last_item %> / <%= @issue_count %> ]
80 [ <%= @issue_pages.current.first_item %> - <%= @issue_pages.current.last_item %> / <%= @issue_count %> ]
85 </p>
81 </p>
86 <% end %>
82 <% end %>
87 <% end %>
83 <% end %>
88 <% end %> No newline at end of file
84 <% end %>
@@ -1,62 +1,59
1 <div class="contextual">
1 <div class="contextual">
2 <%= link_to l(:label_feed_plural), {:action => 'feeds', :id => @project}, :class => 'icon icon-feed' %>
2 <%= link_to l(:label_feed_plural), {:action => 'feeds', :id => @project}, :class => 'icon icon-feed' %>
3 </div>
3 </div>
4
4
5 <h2><%=l(:label_overview)%></h2>
5 <h2><%=l(:label_overview)%></h2>
6
6
7 <div class="splitcontentleft">
7 <div class="splitcontentleft">
8 <%= textilizable @project.description %>
8 <%= textilizable @project.description %>
9 <ul>
9 <ul>
10 <% unless @project.homepage.blank? %><li><%=l(:field_homepage)%>: <%= auto_link @project.homepage %></li><% end %>
10 <% unless @project.homepage.blank? %><li><%=l(:field_homepage)%>: <%= auto_link @project.homepage %></li><% end %>
11 <li><%=l(:field_created_on)%>: <%= format_date(@project.created_on) %></li>
11 <li><%=l(:field_created_on)%>: <%= format_date(@project.created_on) %></li>
12 <% unless @project.parent.nil? %>
12 <% unless @project.parent.nil? %>
13 <li><%=l(:field_parent)%>: <%= link_to @project.parent.name, :controller => 'projects', :action => 'show', :id => @project.parent %></li>
13 <li><%=l(:field_parent)%>: <%= link_to @project.parent.name, :controller => 'projects', :action => 'show', :id => @project.parent %></li>
14 <% end %>
14 <% end %>
15 <% for custom_value in @custom_values %>
15 <% for custom_value in @custom_values %>
16 <% if !custom_value.value.empty? %>
16 <% if !custom_value.value.empty? %>
17 <li><%= custom_value.custom_field.name%>: <%=h show_value(custom_value) %></li>
17 <li><%= custom_value.custom_field.name%>: <%=h show_value(custom_value) %></li>
18 <% end %>
18 <% end %>
19 <% end %>
19 <% end %>
20 </ul>
20 </ul>
21
21
22 <div class="box">
22 <div class="box">
23 <div class="contextual">
24 <%= render :partial => 'issues/add_shortcut', :locals => {:trackers => @trackers } %>
25 </div>
26 <h3 class="icon22 icon22-tracker"><%=l(:label_issue_tracking)%></h3>
23 <h3 class="icon22 icon22-tracker"><%=l(:label_issue_tracking)%></h3>
27 <ul>
24 <ul>
28 <% for tracker in @trackers %>
25 <% for tracker in @trackers %>
29 <li><%= link_to tracker.name, :controller => 'projects', :action => 'list_issues', :id => @project,
26 <li><%= link_to tracker.name, :controller => 'projects', :action => 'list_issues', :id => @project,
30 :set_filter => 1,
27 :set_filter => 1,
31 "tracker_id" => tracker.id %>:
28 "tracker_id" => tracker.id %>:
32 <%= @open_issues_by_tracker[tracker] || 0 %> <%= lwr(:label_open_issues, @open_issues_by_tracker[tracker] || 0) %>
29 <%= @open_issues_by_tracker[tracker] || 0 %> <%= lwr(:label_open_issues, @open_issues_by_tracker[tracker] || 0) %>
33 <%= l(:label_on) %> <%= @total_issues_by_tracker[tracker] || 0 %></li>
30 <%= l(:label_on) %> <%= @total_issues_by_tracker[tracker] || 0 %></li>
34 <% end %>
31 <% end %>
35 </ul>
32 </ul>
36 <p class="textcenter"><small><%= link_to l(:label_issue_view_all), :controller => 'projects', :action => 'list_issues', :id => @project, :set_filter => 1 %></small></p>
33 <p class="textcenter"><small><%= link_to l(:label_issue_view_all), :controller => 'projects', :action => 'list_issues', :id => @project, :set_filter => 1 %></small></p>
37 </div>
34 </div>
38 </div>
35 </div>
39
36
40 <div class="splitcontentright">
37 <div class="splitcontentright">
41 <div class="box">
38 <div class="box">
42 <h3 class="icon22 icon22-users"><%=l(:label_member_plural)%></h3>
39 <h3 class="icon22 icon22-users"><%=l(:label_member_plural)%></h3>
43 <% @members_by_role.keys.sort.each do |role| %>
40 <% @members_by_role.keys.sort.each do |role| %>
44 <%= role.name %>: <%= @members_by_role[role].collect(&:user).sort.collect{|u| link_to_user u}.join(", ") %><br />
41 <%= role.name %>: <%= @members_by_role[role].collect(&:user).sort.collect{|u| link_to_user u}.join(", ") %><br />
45 <% end %>
42 <% end %>
46 </div>
43 </div>
47
44
48 <% if @subprojects.any? %>
45 <% if @subprojects.any? %>
49 <div class="box">
46 <div class="box">
50 <h3 class="icon22 icon22-projects"><%=l(:label_subproject_plural)%></h3>
47 <h3 class="icon22 icon22-projects"><%=l(:label_subproject_plural)%></h3>
51 <%= @subprojects.collect{|p| link_to(p.name, :action => 'show', :id => p)}.join(", ") %>
48 <%= @subprojects.collect{|p| link_to(p.name, :action => 'show', :id => p)}.join(", ") %>
52 </div>
49 </div>
53 <% end %>
50 <% end %>
54
51
55 <% if @news.any? %>
52 <% if @news.any? %>
56 <div class="box">
53 <div class="box">
57 <h3><%=l(:label_news_latest)%></h3>
54 <h3><%=l(:label_news_latest)%></h3>
58 <%= render :partial => 'news/news', :collection => @news %>
55 <%= render :partial => 'news/news', :collection => @news %>
59 <p class="textcenter"><small><%= link_to l(:label_news_view_all), :controller => 'projects', :action => 'list_news', :id => @project %></small></p>
56 <p class="textcenter"><small><%= link_to l(:label_news_view_all), :controller => 'projects', :action => 'list_news', :id => @project %></small></p>
60 </div>
57 </div>
61 <% end %>
58 <% end %>
62 </div> No newline at end of file
59 </div>
@@ -1,100 +1,100
1 <script type="text/javascript">
1 <script type="text/javascript">
2 //<![CDATA[
2 //<![CDATA[
3 function add_filter() {
3 function add_filter() {
4 select = $('add_filter_select');
4 select = $('add_filter_select');
5 field = select.value
5 field = select.value
6 Element.show('tr_' + field);
6 Element.show('tr_' + field);
7 check_box = $('cb_' + field);
7 check_box = $('cb_' + field);
8 check_box.checked = true;
8 check_box.checked = true;
9 toggle_filter(field);
9 toggle_filter(field);
10 select.selectedIndex = 0;
10 select.selectedIndex = 0;
11
11
12 for (i=0; i<select.options.length; i++) {
12 for (i=0; i<select.options.length; i++) {
13 if (select.options[i].value == field) {
13 if (select.options[i].value == field) {
14 select.options[i].disabled = true;
14 select.options[i].disabled = true;
15 }
15 }
16 }
16 }
17 }
17 }
18
18
19 function toggle_filter(field) {
19 function toggle_filter(field) {
20 check_box = $('cb_' + field);
20 check_box = $('cb_' + field);
21
21
22 if (check_box.checked) {
22 if (check_box.checked) {
23 Element.show("operators_" + field);
23 Element.show("operators_" + field);
24 toggle_operator(field);
24 toggle_operator(field);
25 } else {
25 } else {
26 Element.hide("operators_" + field);
26 Element.hide("operators_" + field);
27 Element.hide("div_values_" + field);
27 Element.hide("div_values_" + field);
28 }
28 }
29 }
29 }
30
30
31 function toggle_operator(field) {
31 function toggle_operator(field) {
32 operator = $("operators_" + field);
32 operator = $("operators_" + field);
33 switch (operator.value) {
33 switch (operator.value) {
34 case "!*":
34 case "!*":
35 case "*":
35 case "*":
36 case "t":
36 case "t":
37 case "o":
37 case "o":
38 case "c":
38 case "c":
39 Element.hide("div_values_" + field);
39 Element.hide("div_values_" + field);
40 break;
40 break;
41 default:
41 default:
42 Element.show("div_values_" + field);
42 Element.show("div_values_" + field);
43 break;
43 break;
44 }
44 }
45 }
45 }
46
46
47 function toggle_multi_select(field) {
47 function toggle_multi_select(field) {
48 select = $('values_' + field);
48 select = $('values_' + field);
49 if (select.multiple == true) {
49 if (select.multiple == true) {
50 select.multiple = false;
50 select.multiple = false;
51 } else {
51 } else {
52 select.multiple = true;
52 select.multiple = true;
53 }
53 }
54 }
54 }
55 //]]>
55 //]]>
56 </script>
56 </script>
57
57
58 <fieldset style="margin:0;"><legend><%= l(:label_filter_plural) %></legend>
58 <fieldset><legend><%= l(:label_filter_plural) %></legend>
59 <table width="100%">
59 <table width="100%">
60 <tr>
60 <tr>
61 <td>
61 <td>
62 <table style="padding:0;">
62 <table style="padding:0;">
63 <% query.available_filters.sort{|a,b| a[1][:order]<=>b[1][:order]}.each do |filter| %>
63 <% query.available_filters.sort{|a,b| a[1][:order]<=>b[1][:order]}.each do |filter| %>
64 <% field = filter[0]
64 <% field = filter[0]
65 options = filter[1] %>
65 options = filter[1] %>
66 <tr <%= 'style="display:none;"' unless query.has_filter?(field) %> id="tr_<%= field %>">
66 <tr <%= 'style="display:none;"' unless query.has_filter?(field) %> id="tr_<%= field %>">
67 <td valign="top" style="width:200px;">
67 <td valign="top" style="width:200px;">
68 <%= check_box_tag 'fields[]', field, query.has_filter?(field), :onclick => "toggle_filter('#{field}');", :id => "cb_#{field}" %>
68 <%= check_box_tag 'fields[]', field, query.has_filter?(field), :onclick => "toggle_filter('#{field}');", :id => "cb_#{field}" %>
69 <label for="cb_<%= field %>"><%= filter[1][:name] || l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) %></label>
69 <label for="cb_<%= field %>"><%= filter[1][:name] || l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) %></label>
70 </td>
70 </td>
71 <td valign="top" style="width:150px;">
71 <td valign="top" style="width:150px;">
72 <%= select_tag "operators[#{field}]", options_for_select(operators_for_select(options[:type]), query.operator_for(field)), :id => "operators_#{field}", :onchange => "toggle_operator('#{field}');", :class => "select-small", :style => "vertical-align: top;" %>
72 <%= select_tag "operators[#{field}]", options_for_select(operators_for_select(options[:type]), query.operator_for(field)), :id => "operators_#{field}", :onchange => "toggle_operator('#{field}');", :class => "select-small", :style => "vertical-align: top;" %>
73 </td>
73 </td>
74 <td valign="top">
74 <td valign="top">
75 <div id="div_values_<%= field %>" style="display:none;">
75 <div id="div_values_<%= field %>" style="display:none;">
76 <% case options[:type]
76 <% case options[:type]
77 when :list, :list_optional, :list_status, :list_one_or_more %>
77 when :list, :list_optional, :list_status, :list_one_or_more %>
78 <select <%= "multiple=true" if query.values_for(field) and query.values_for(field).length > 1 %> name="values[<%= field %>][]" id="values_<%= field %>" class="select-small" style="vertical-align: top;">
78 <select <%= "multiple=true" if query.values_for(field) and query.values_for(field).length > 1 %> name="values[<%= field %>][]" id="values_<%= field %>" class="select-small" style="vertical-align: top;">
79 <%= options_for_select options[:values], query.values_for(field) %>
79 <%= options_for_select options[:values], query.values_for(field) %>
80 </select>
80 </select>
81 <%= link_to_function image_tag('expand.png'), "toggle_multi_select('#{field}');" %>
81 <%= link_to_function image_tag('expand.png'), "toggle_multi_select('#{field}');" %>
82 <% when :date, :date_past %>
82 <% when :date, :date_past %>
83 <%= text_field_tag "values[#{field}][]", query.values_for(field), :id => "values_#{field}", :size => 3, :class => "select-small" %> <%= l(:label_day_plural) %>
83 <%= text_field_tag "values[#{field}][]", query.values_for(field), :id => "values_#{field}", :size => 3, :class => "select-small" %> <%= l(:label_day_plural) %>
84 <% when :string, :text %>
84 <% when :string, :text %>
85 <%= text_field_tag "values[#{field}][]", query.values_for(field), :id => "values_#{field}", :size => 30, :class => "select-small" %>
85 <%= text_field_tag "values[#{field}][]", query.values_for(field), :id => "values_#{field}", :size => 30, :class => "select-small" %>
86 <% end %>
86 <% end %>
87 </div>
87 </div>
88 <script type="text/javascript">toggle_filter('<%= field %>');</script>
88 <script type="text/javascript">toggle_filter('<%= field %>');</script>
89 </td>
89 </td>
90 </tr>
90 </tr>
91 <% end %>
91 <% end %>
92 </table>
92 </table>
93 </td>
93 </td>
94 <td align="right" valign="top">
94 <td align="right" valign="top">
95 <%= l(:label_filter_add) %>:
95 <%= l(:label_filter_add) %>:
96 <%= select_tag 'add_filter_select', options_for_select([["",""]] + query.available_filters.sort{|a,b| a[1][:order]<=>b[1][:order]}.collect{|field| [ field[1][:name] || l(("field_"+field[0].to_s.gsub(/\_id$/, "")).to_sym), field[0]] unless query.has_filter?(field[0])}.compact), :onchange => "add_filter();", :class => "select-small" %>
96 <%= select_tag 'add_filter_select', options_for_select([["",""]] + query.available_filters.sort{|a,b| a[1][:order]<=>b[1][:order]}.collect{|field| [ field[1][:name] || l(("field_"+field[0].to_s.gsub(/\_id$/, "")).to_sym), field[0]] unless query.has_filter?(field[0])}.compact), :onchange => "add_filter();", :class => "select-small" %>
97 </td>
97 </td>
98 </tr>
98 </tr>
99 </table>
99 </table>
100 </fieldset> No newline at end of file
100 </fieldset>
@@ -1,12 +1,15
1 <%= error_messages_for 'query' %>
1 <%= error_messages_for 'query' %>
2
2
3 <!--[form:query]-->
4 <div class="box">
3 <div class="box">
5 <div class="tabular">
4 <div class="tabular">
6 <p><label for="query_name"><%=l(:field_name)%></label>
5 <p><label for="query_name"><%=l(:field_name)%></label>
7 <%= text_field 'query', 'name', :size => 80 %></p>
6 <%= text_field 'query', 'name', :size => 80 %></p>
7
8 <% if authorize_for('projects', 'add_query') %>
9 <p><label for="query_is_public"><%=l(:field_is_public)%></label>
10 <%= check_box 'query', 'is_public' %></p>
11 <% end %>
8 </div>
12 </div>
9
13
10 <%= render :partial => 'queries/filters', :locals => {:query => query}%>
14 <%= render :partial => 'queries/filters', :locals => {:query => query}%>
11 </div>
15 </div>
12 <!--[eoform:query]--> No newline at end of file
1 NO CONTENT: modified file
NO CONTENT: modified file
@@ -1,51 +1,34
1 <h2><%=l(:label_report_plural)%></h2>
1 <h2><%=l(:label_report_plural)%></h2>
2
2
3 <div class="splitcontentleft">
4 <div class="contextual">
5 <%= link_to_if_authorized l(:label_query_new), {:controller => 'projects', :action => 'add_query', :id => @project}, :class => 'icon icon-add' %>
6 </div>
7 <h3><%= l(:label_query_plural) %></h3>
8
9 <% if @queries.empty? %><p><i><%=l(:label_no_data)%></i></p><% end %>
10 <ul>
11 <% @queries.each do |query| %>
12 <li><%= link_to query.name, :controller => 'projects', :action => 'list_issues', :id => @project, :query_id => query %></li>
13 <% end %>
14 </ul>
15 </div>
16 <div class="splitcontentright">
17 <% if @total_hours %>
3 <% if @total_hours %>
18 <h3 class="textright"><%= l(:label_spent_time) %>:
4 <h3 class="textright"><%= l(:label_spent_time) %>:
19 <%= link_to(lwr(:label_f_hour, @total_hours), {:controller => 'timelog', :action => 'details', :project_id => @project}, :class => 'icon icon-time') %>
5 <%= link_to(lwr(:label_f_hour, @total_hours), {:controller => 'timelog', :action => 'details', :project_id => @project}, :class => 'icon icon-time') %>
20 </h3>
6 </h3>
21 <% end %>
7 <% end %>
22 </div>
23
24 <div class="clear"></div>
25
8
26 <div class="splitcontentleft">
9 <div class="splitcontentleft">
27 <h3><%=l(:field_tracker)%>&nbsp;&nbsp;<%= link_to image_tag('zoom_in.png'), :detail => 'tracker' %></h3>
10 <h3><%=l(:field_tracker)%>&nbsp;&nbsp;<%= link_to image_tag('zoom_in.png'), :detail => 'tracker' %></h3>
28 <%= render :partial => 'simple', :locals => { :data => @issues_by_tracker, :field_name => "tracker_id", :rows => @trackers } %>
11 <%= render :partial => 'simple', :locals => { :data => @issues_by_tracker, :field_name => "tracker_id", :rows => @trackers } %>
29 <br />
12 <br />
30 <h3><%=l(:field_version)%>&nbsp;&nbsp;<%= link_to image_tag('zoom_in.png'), :detail => 'version' %></h3>
13 <h3><%=l(:field_version)%>&nbsp;&nbsp;<%= link_to image_tag('zoom_in.png'), :detail => 'version' %></h3>
31 <%= render :partial => 'simple', :locals => { :data => @issues_by_version, :field_name => "fixed_version_id", :rows => @versions } %>
14 <%= render :partial => 'simple', :locals => { :data => @issues_by_version, :field_name => "fixed_version_id", :rows => @versions } %>
32 <br />
15 <br />
33 <h3><%=l(:field_author)%>&nbsp;&nbsp;<%= link_to image_tag('zoom_in.png'), :detail => 'author' %></h3>
16 <h3><%=l(:field_author)%>&nbsp;&nbsp;<%= link_to image_tag('zoom_in.png'), :detail => 'author' %></h3>
34 <%= render :partial => 'simple', :locals => { :data => @issues_by_author, :field_name => "author_id", :rows => @authors } %>
17 <%= render :partial => 'simple', :locals => { :data => @issues_by_author, :field_name => "author_id", :rows => @authors } %>
35 <br />
18 <br />
36 </div>
19 </div>
37
20
38 <div class="splitcontentright">
21 <div class="splitcontentright">
39 <h3><%=l(:field_priority)%>&nbsp;&nbsp;<%= link_to image_tag('zoom_in.png'), :detail => 'priority' %></h3>
22 <h3><%=l(:field_priority)%>&nbsp;&nbsp;<%= link_to image_tag('zoom_in.png'), :detail => 'priority' %></h3>
40 <%= render :partial => 'simple', :locals => { :data => @issues_by_priority, :field_name => "priority_id", :rows => @priorities } %>
23 <%= render :partial => 'simple', :locals => { :data => @issues_by_priority, :field_name => "priority_id", :rows => @priorities } %>
41 <br />
24 <br />
42 <% if @project.children.any? %>
25 <% if @project.children.any? %>
43 <h3><%=l(:field_subproject)%>&nbsp;&nbsp;<%= link_to image_tag('zoom_in.png'), :detail => 'subproject' %></h3>
26 <h3><%=l(:field_subproject)%>&nbsp;&nbsp;<%= link_to image_tag('zoom_in.png'), :detail => 'subproject' %></h3>
44 <%= render :partial => 'simple', :locals => { :data => @issues_by_subproject, :field_name => "project_id", :rows => @subprojects } %>
27 <%= render :partial => 'simple', :locals => { :data => @issues_by_subproject, :field_name => "project_id", :rows => @subprojects } %>
45 <br />
28 <br />
46 <% end %>
29 <% end %>
47 <h3><%=l(:field_category)%>&nbsp;&nbsp;<%= link_to image_tag('zoom_in.png'), :detail => 'category' %></h3>
30 <h3><%=l(:field_category)%>&nbsp;&nbsp;<%= link_to image_tag('zoom_in.png'), :detail => 'category' %></h3>
48 <%= render :partial => 'simple', :locals => { :data => @issues_by_category, :field_name => "category_id", :rows => @categories } %>
31 <%= render :partial => 'simple', :locals => { :data => @issues_by_category, :field_name => "category_id", :rows => @categories } %>
49 <br />
32 <br />
50 </div>
33 </div>
51
34
1 NO CONTENT: modified file, binary diff hidden
NO CONTENT: modified file, binary diff hidden
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now