##// END OF EJS Templates
Added a link to add a new category when creating or editing an issue....
Jean-Philippe Lang -
r642:4cedecad4d33
parent child
Show More
@@ -1,674 +1,684
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
98
99 @key = logged_in_user.get_or_create_rss_key.value if logged_in_user
99 @key = logged_in_user.get_or_create_rss_key.value if logged_in_user
100 end
100 end
101
101
102 def settings
102 def settings
103 @root_projects = Project::find(:all, :conditions => ["parent_id is null and id <> ?", @project.id])
103 @root_projects = Project::find(:all, :conditions => ["parent_id is null and id <> ?", @project.id])
104 @custom_fields = IssueCustomField.find(:all)
104 @custom_fields = IssueCustomField.find(:all)
105 @issue_category ||= IssueCategory.new
105 @issue_category ||= IssueCategory.new
106 @member ||= @project.members.new
106 @member ||= @project.members.new
107 @custom_values ||= ProjectCustomField.find(:all).collect { |x| @project.custom_values.find_by_custom_field_id(x.id) || CustomValue.new(:custom_field => x) }
107 @custom_values ||= ProjectCustomField.find(:all).collect { |x| @project.custom_values.find_by_custom_field_id(x.id) || CustomValue.new(:custom_field => x) }
108 end
108 end
109
109
110 # Edit @project
110 # Edit @project
111 def edit
111 def edit
112 if request.post?
112 if request.post?
113 @project.custom_fields = IssueCustomField.find(params[:custom_field_ids]) if params[:custom_field_ids]
113 @project.custom_fields = IssueCustomField.find(params[:custom_field_ids]) if params[:custom_field_ids]
114 if params[:custom_fields]
114 if params[:custom_fields]
115 @custom_values = ProjectCustomField.find(:all).collect { |x| CustomValue.new(:custom_field => x, :customized => @project, :value => params["custom_fields"][x.id.to_s]) }
115 @custom_values = ProjectCustomField.find(:all).collect { |x| CustomValue.new(:custom_field => x, :customized => @project, :value => params["custom_fields"][x.id.to_s]) }
116 @project.custom_values = @custom_values
116 @project.custom_values = @custom_values
117 end
117 end
118 if params[:repository_enabled]
118 if params[:repository_enabled]
119 case params[:repository_enabled]
119 case params[:repository_enabled]
120 when "0"
120 when "0"
121 @project.repository = nil
121 @project.repository = nil
122 when "1"
122 when "1"
123 @project.repository ||= Repository.factory(params[:repository_scm])
123 @project.repository ||= Repository.factory(params[:repository_scm])
124 @project.repository.update_attributes params[:repository] if @project.repository
124 @project.repository.update_attributes params[:repository] if @project.repository
125 end
125 end
126 end
126 end
127 if params[:wiki_enabled]
127 if params[:wiki_enabled]
128 case params[:wiki_enabled]
128 case params[:wiki_enabled]
129 when "0"
129 when "0"
130 @project.wiki.destroy if @project.wiki
130 @project.wiki.destroy if @project.wiki
131 when "1"
131 when "1"
132 @project.wiki ||= Wiki.new
132 @project.wiki ||= Wiki.new
133 @project.wiki.update_attributes params[:wiki]
133 @project.wiki.update_attributes params[:wiki]
134 end
134 end
135 end
135 end
136 @project.attributes = params[:project]
136 @project.attributes = params[:project]
137 if @project.save
137 if @project.save
138 flash[:notice] = l(:notice_successful_update)
138 flash[:notice] = l(:notice_successful_update)
139 redirect_to :action => 'settings', :id => @project
139 redirect_to :action => 'settings', :id => @project
140 else
140 else
141 settings
141 settings
142 render :action => 'settings'
142 render :action => 'settings'
143 end
143 end
144 end
144 end
145 end
145 end
146
146
147 def archive
147 def archive
148 @project.archive if request.post? && @project.active?
148 @project.archive if request.post? && @project.active?
149 redirect_to :controller => 'admin', :action => 'projects'
149 redirect_to :controller => 'admin', :action => 'projects'
150 end
150 end
151
151
152 def unarchive
152 def unarchive
153 @project.unarchive if request.post? && !@project.active?
153 @project.unarchive if request.post? && !@project.active?
154 redirect_to :controller => 'admin', :action => 'projects'
154 redirect_to :controller => 'admin', :action => 'projects'
155 end
155 end
156
156
157 # Delete @project
157 # Delete @project
158 def destroy
158 def destroy
159 @project_to_destroy = @project
159 @project_to_destroy = @project
160 if request.post? and params[:confirm]
160 if request.post? and params[:confirm]
161 @project_to_destroy.destroy
161 @project_to_destroy.destroy
162 redirect_to :controller => 'admin', :action => 'projects'
162 redirect_to :controller => 'admin', :action => 'projects'
163 end
163 end
164 # hide project in layout
164 # hide project in layout
165 @project = nil
165 @project = nil
166 end
166 end
167
167
168 # Add a new issue category to @project
168 # Add a new issue category to @project
169 def add_issue_category
169 def add_issue_category
170 @category = @project.issue_categories.build(params[:category])
170 @category = @project.issue_categories.build(params[:category])
171 if request.post? and @category.save
171 if request.post? and @category.save
172 respond_to do |format|
173 format.html do
172 flash[:notice] = l(:notice_successful_create)
174 flash[:notice] = l(:notice_successful_create)
173 redirect_to :action => 'settings', :tab => 'categories', :id => @project
175 redirect_to :action => 'settings', :tab => 'categories', :id => @project
174 end
176 end
177 format.js do
178 # IE doesn't support the replace_html rjs method for select box options
179 render(:update) {|page| page.replace "issue_category_id",
180 content_tag('select', '<option></option>' + options_from_collection_for_select(@project.issue_categories, 'id', 'name', @category.id), :id => 'issue_category_id', :name => 'issue[category_id]')
181 }
182 end
183 end
184 end
175 end
185 end
176
186
177 # Add a new version to @project
187 # Add a new version to @project
178 def add_version
188 def add_version
179 @version = @project.versions.build(params[:version])
189 @version = @project.versions.build(params[:version])
180 if request.post? and @version.save
190 if request.post? and @version.save
181 flash[:notice] = l(:notice_successful_create)
191 flash[:notice] = l(:notice_successful_create)
182 redirect_to :action => 'settings', :tab => 'versions', :id => @project
192 redirect_to :action => 'settings', :tab => 'versions', :id => @project
183 end
193 end
184 end
194 end
185
195
186 # Add a new member to @project
196 # Add a new member to @project
187 def add_member
197 def add_member
188 @member = @project.members.build(params[:member])
198 @member = @project.members.build(params[:member])
189 if request.post? && @member.save
199 if request.post? && @member.save
190 respond_to do |format|
200 respond_to do |format|
191 format.html { redirect_to :action => 'settings', :tab => 'members', :id => @project }
201 format.html { redirect_to :action => 'settings', :tab => 'members', :id => @project }
192 format.js { render(:update) {|page| page.replace_html "tab-content-members", :partial => 'members'} }
202 format.js { render(:update) {|page| page.replace_html "tab-content-members", :partial => 'members'} }
193 end
203 end
194 else
204 else
195 settings
205 settings
196 render :action => 'settings'
206 render :action => 'settings'
197 end
207 end
198 end
208 end
199
209
200 # Show members list of @project
210 # Show members list of @project
201 def list_members
211 def list_members
202 @members = @project.members.find(:all)
212 @members = @project.members.find(:all)
203 end
213 end
204
214
205 # Add a new document to @project
215 # Add a new document to @project
206 def add_document
216 def add_document
207 @categories = Enumeration::get_values('DCAT')
217 @categories = Enumeration::get_values('DCAT')
208 @document = @project.documents.build(params[:document])
218 @document = @project.documents.build(params[:document])
209 if request.post? and @document.save
219 if request.post? and @document.save
210 # Save the attachments
220 # Save the attachments
211 params[:attachments].each { |a|
221 params[:attachments].each { |a|
212 Attachment.create(:container => @document, :file => a, :author => logged_in_user) unless a.size == 0
222 Attachment.create(:container => @document, :file => a, :author => logged_in_user) unless a.size == 0
213 } if params[:attachments] and params[:attachments].is_a? Array
223 } if params[:attachments] and params[:attachments].is_a? Array
214 flash[:notice] = l(:notice_successful_create)
224 flash[:notice] = l(:notice_successful_create)
215 Mailer.deliver_document_add(@document) if Permission.find_by_controller_and_action(params[:controller], params[:action]).mail_enabled?
225 Mailer.deliver_document_add(@document) if Permission.find_by_controller_and_action(params[:controller], params[:action]).mail_enabled?
216 redirect_to :action => 'list_documents', :id => @project
226 redirect_to :action => 'list_documents', :id => @project
217 end
227 end
218 end
228 end
219
229
220 # Show documents list of @project
230 # Show documents list of @project
221 def list_documents
231 def list_documents
222 @documents = @project.documents.find :all, :include => :category
232 @documents = @project.documents.find :all, :include => :category
223 end
233 end
224
234
225 # Add a new issue to @project
235 # Add a new issue to @project
226 def add_issue
236 def add_issue
227 @tracker = Tracker.find(params[:tracker_id])
237 @tracker = Tracker.find(params[:tracker_id])
228 @priorities = Enumeration::get_values('IPRI')
238 @priorities = Enumeration::get_values('IPRI')
229
239
230 default_status = IssueStatus.default
240 default_status = IssueStatus.default
231 unless default_status
241 unless default_status
232 flash.now[:error] = 'No default issue status defined. Please check your configuration.'
242 flash.now[:error] = 'No default issue status defined. Please check your configuration.'
233 render :nothing => true, :layout => true
243 render :nothing => true, :layout => true
234 return
244 return
235 end
245 end
236 @issue = Issue.new(:project => @project, :tracker => @tracker)
246 @issue = Issue.new(:project => @project, :tracker => @tracker)
237 @issue.status = default_status
247 @issue.status = default_status
238 @allowed_statuses = ([default_status] + default_status.find_new_statuses_allowed_to(logged_in_user.role_for_project(@project), @issue.tracker))if logged_in_user
248 @allowed_statuses = ([default_status] + default_status.find_new_statuses_allowed_to(logged_in_user.role_for_project(@project), @issue.tracker))if logged_in_user
239 if request.get?
249 if request.get?
240 @issue.start_date = Date.today
250 @issue.start_date = Date.today
241 @custom_values = @project.custom_fields_for_issues(@tracker).collect { |x| CustomValue.new(:custom_field => x, :customized => @issue) }
251 @custom_values = @project.custom_fields_for_issues(@tracker).collect { |x| CustomValue.new(:custom_field => x, :customized => @issue) }
242 else
252 else
243 @issue.attributes = params[:issue]
253 @issue.attributes = params[:issue]
244
254
245 requested_status = IssueStatus.find_by_id(params[:issue][:status_id])
255 requested_status = IssueStatus.find_by_id(params[:issue][:status_id])
246 @issue.status = (@allowed_statuses.include? requested_status) ? requested_status : default_status
256 @issue.status = (@allowed_statuses.include? requested_status) ? requested_status : default_status
247
257
248 @issue.author_id = self.logged_in_user.id if self.logged_in_user
258 @issue.author_id = self.logged_in_user.id if self.logged_in_user
249 # Multiple file upload
259 # Multiple file upload
250 @attachments = []
260 @attachments = []
251 params[:attachments].each { |a|
261 params[:attachments].each { |a|
252 @attachments << Attachment.new(:container => @issue, :file => a, :author => logged_in_user) unless a.size == 0
262 @attachments << Attachment.new(:container => @issue, :file => a, :author => logged_in_user) unless a.size == 0
253 } if params[:attachments] and params[:attachments].is_a? Array
263 } if params[:attachments] and params[:attachments].is_a? Array
254 @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]) }
264 @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]) }
255 @issue.custom_values = @custom_values
265 @issue.custom_values = @custom_values
256 if @issue.save
266 if @issue.save
257 @attachments.each(&:save)
267 @attachments.each(&:save)
258 flash[:notice] = l(:notice_successful_create)
268 flash[:notice] = l(:notice_successful_create)
259 Mailer.deliver_issue_add(@issue) if Permission.find_by_controller_and_action(params[:controller], params[:action]).mail_enabled?
269 Mailer.deliver_issue_add(@issue) if Permission.find_by_controller_and_action(params[:controller], params[:action]).mail_enabled?
260 redirect_to :action => 'list_issues', :id => @project
270 redirect_to :action => 'list_issues', :id => @project
261 end
271 end
262 end
272 end
263 end
273 end
264
274
265 # Show filtered/sorted issues list of @project
275 # Show filtered/sorted issues list of @project
266 def list_issues
276 def list_issues
267 sort_init "#{Issue.table_name}.id", "desc"
277 sort_init "#{Issue.table_name}.id", "desc"
268 sort_update
278 sort_update
269
279
270 retrieve_query
280 retrieve_query
271
281
272 @results_per_page_options = [ 15, 25, 50, 100 ]
282 @results_per_page_options = [ 15, 25, 50, 100 ]
273 if params[:per_page] and @results_per_page_options.include? params[:per_page].to_i
283 if params[:per_page] and @results_per_page_options.include? params[:per_page].to_i
274 @results_per_page = params[:per_page].to_i
284 @results_per_page = params[:per_page].to_i
275 session[:results_per_page] = @results_per_page
285 session[:results_per_page] = @results_per_page
276 else
286 else
277 @results_per_page = session[:results_per_page] || 25
287 @results_per_page = session[:results_per_page] || 25
278 end
288 end
279
289
280 if @query.valid?
290 if @query.valid?
281 @issue_count = Issue.count(:include => [:status, :project, :custom_values], :conditions => @query.statement)
291 @issue_count = Issue.count(:include => [:status, :project, :custom_values], :conditions => @query.statement)
282 @issue_pages = Paginator.new self, @issue_count, @results_per_page, params['page']
292 @issue_pages = Paginator.new self, @issue_count, @results_per_page, params['page']
283 @issues = Issue.find :all, :order => sort_clause,
293 @issues = Issue.find :all, :order => sort_clause,
284 :include => [ :assigned_to, :status, :tracker, :project, :priority, :custom_values ],
294 :include => [ :assigned_to, :status, :tracker, :project, :priority, :custom_values ],
285 :conditions => @query.statement,
295 :conditions => @query.statement,
286 :limit => @issue_pages.items_per_page,
296 :limit => @issue_pages.items_per_page,
287 :offset => @issue_pages.current.offset
297 :offset => @issue_pages.current.offset
288 end
298 end
289 render :layout => false if request.xhr?
299 render :layout => false if request.xhr?
290 end
300 end
291
301
292 # Export filtered/sorted issues list to CSV
302 # Export filtered/sorted issues list to CSV
293 def export_issues_csv
303 def export_issues_csv
294 sort_init "#{Issue.table_name}.id", "desc"
304 sort_init "#{Issue.table_name}.id", "desc"
295 sort_update
305 sort_update
296
306
297 retrieve_query
307 retrieve_query
298 render :action => 'list_issues' and return unless @query.valid?
308 render :action => 'list_issues' and return unless @query.valid?
299
309
300 @issues = Issue.find :all, :order => sort_clause,
310 @issues = Issue.find :all, :order => sort_clause,
301 :include => [ :assigned_to, :author, :status, :tracker, :priority, :project, {:custom_values => :custom_field} ],
311 :include => [ :assigned_to, :author, :status, :tracker, :priority, :project, {:custom_values => :custom_field} ],
302 :conditions => @query.statement,
312 :conditions => @query.statement,
303 :limit => Setting.issues_export_limit.to_i
313 :limit => Setting.issues_export_limit.to_i
304
314
305 ic = Iconv.new(l(:general_csv_encoding), 'UTF-8')
315 ic = Iconv.new(l(:general_csv_encoding), 'UTF-8')
306 export = StringIO.new
316 export = StringIO.new
307 CSV::Writer.generate(export, l(:general_csv_separator)) do |csv|
317 CSV::Writer.generate(export, l(:general_csv_separator)) do |csv|
308 # csv header fields
318 # csv header fields
309 headers = [ "#", l(:field_status),
319 headers = [ "#", l(:field_status),
310 l(:field_project),
320 l(:field_project),
311 l(:field_tracker),
321 l(:field_tracker),
312 l(:field_priority),
322 l(:field_priority),
313 l(:field_subject),
323 l(:field_subject),
314 l(:field_assigned_to),
324 l(:field_assigned_to),
315 l(:field_author),
325 l(:field_author),
316 l(:field_start_date),
326 l(:field_start_date),
317 l(:field_due_date),
327 l(:field_due_date),
318 l(:field_done_ratio),
328 l(:field_done_ratio),
319 l(:field_created_on),
329 l(:field_created_on),
320 l(:field_updated_on)
330 l(:field_updated_on)
321 ]
331 ]
322 for custom_field in @project.all_custom_fields
332 for custom_field in @project.all_custom_fields
323 headers << custom_field.name
333 headers << custom_field.name
324 end
334 end
325 csv << headers.collect {|c| begin; ic.iconv(c.to_s); rescue; c.to_s; end }
335 csv << headers.collect {|c| begin; ic.iconv(c.to_s); rescue; c.to_s; end }
326 # csv lines
336 # csv lines
327 @issues.each do |issue|
337 @issues.each do |issue|
328 fields = [issue.id, issue.status.name,
338 fields = [issue.id, issue.status.name,
329 issue.project.name,
339 issue.project.name,
330 issue.tracker.name,
340 issue.tracker.name,
331 issue.priority.name,
341 issue.priority.name,
332 issue.subject,
342 issue.subject,
333 (issue.assigned_to ? issue.assigned_to.name : ""),
343 (issue.assigned_to ? issue.assigned_to.name : ""),
334 issue.author.name,
344 issue.author.name,
335 issue.start_date ? l_date(issue.start_date) : nil,
345 issue.start_date ? l_date(issue.start_date) : nil,
336 issue.due_date ? l_date(issue.due_date) : nil,
346 issue.due_date ? l_date(issue.due_date) : nil,
337 issue.done_ratio,
347 issue.done_ratio,
338 l_datetime(issue.created_on),
348 l_datetime(issue.created_on),
339 l_datetime(issue.updated_on)
349 l_datetime(issue.updated_on)
340 ]
350 ]
341 for custom_field in @project.all_custom_fields
351 for custom_field in @project.all_custom_fields
342 fields << (show_value issue.custom_value_for(custom_field))
352 fields << (show_value issue.custom_value_for(custom_field))
343 end
353 end
344 csv << fields.collect {|c| begin; ic.iconv(c.to_s); rescue; c.to_s; end }
354 csv << fields.collect {|c| begin; ic.iconv(c.to_s); rescue; c.to_s; end }
345 end
355 end
346 end
356 end
347 export.rewind
357 export.rewind
348 send_data(export.read, :type => 'text/csv; header=present', :filename => 'export.csv')
358 send_data(export.read, :type => 'text/csv; header=present', :filename => 'export.csv')
349 end
359 end
350
360
351 # Export filtered/sorted issues to PDF
361 # Export filtered/sorted issues to PDF
352 def export_issues_pdf
362 def export_issues_pdf
353 sort_init "#{Issue.table_name}.id", "desc"
363 sort_init "#{Issue.table_name}.id", "desc"
354 sort_update
364 sort_update
355
365
356 retrieve_query
366 retrieve_query
357 render :action => 'list_issues' and return unless @query.valid?
367 render :action => 'list_issues' and return unless @query.valid?
358
368
359 @issues = Issue.find :all, :order => sort_clause,
369 @issues = Issue.find :all, :order => sort_clause,
360 :include => [ :author, :status, :tracker, :priority, :project, :custom_values ],
370 :include => [ :author, :status, :tracker, :priority, :project, :custom_values ],
361 :conditions => @query.statement,
371 :conditions => @query.statement,
362 :limit => Setting.issues_export_limit.to_i
372 :limit => Setting.issues_export_limit.to_i
363
373
364 @options_for_rfpdf ||= {}
374 @options_for_rfpdf ||= {}
365 @options_for_rfpdf[:file_name] = "export.pdf"
375 @options_for_rfpdf[:file_name] = "export.pdf"
366 render :layout => false
376 render :layout => false
367 end
377 end
368
378
369 def move_issues
379 def move_issues
370 @issues = @project.issues.find(params[:issue_ids]) if params[:issue_ids]
380 @issues = @project.issues.find(params[:issue_ids]) if params[:issue_ids]
371 redirect_to :action => 'list_issues', :id => @project and return unless @issues
381 redirect_to :action => 'list_issues', :id => @project and return unless @issues
372 @projects = []
382 @projects = []
373 # find projects to which the user is allowed to move the issue
383 # find projects to which the user is allowed to move the issue
374 @logged_in_user.memberships.each {|m| @projects << m.project if Permission.allowed_to_role("projects/move_issues", m.role)}
384 @logged_in_user.memberships.each {|m| @projects << m.project if Permission.allowed_to_role("projects/move_issues", m.role)}
375 # issue can be moved to any tracker
385 # issue can be moved to any tracker
376 @trackers = Tracker.find(:all)
386 @trackers = Tracker.find(:all)
377 if request.post? and params[:new_project_id] and params[:new_tracker_id]
387 if request.post? and params[:new_project_id] and params[:new_tracker_id]
378 new_project = Project.find(params[:new_project_id])
388 new_project = Project.find(params[:new_project_id])
379 new_tracker = Tracker.find(params[:new_tracker_id])
389 new_tracker = Tracker.find(params[:new_tracker_id])
380 @issues.each { |i|
390 @issues.each { |i|
381 # project dependent properties
391 # project dependent properties
382 unless i.project_id == new_project.id
392 unless i.project_id == new_project.id
383 i.category = nil
393 i.category = nil
384 i.fixed_version = nil
394 i.fixed_version = nil
385 # delete issue relations
395 # delete issue relations
386 i.relations_from.clear
396 i.relations_from.clear
387 i.relations_to.clear
397 i.relations_to.clear
388 end
398 end
389 # move the issue
399 # move the issue
390 i.project = new_project
400 i.project = new_project
391 i.tracker = new_tracker
401 i.tracker = new_tracker
392 i.save
402 i.save
393 }
403 }
394 flash[:notice] = l(:notice_successful_update)
404 flash[:notice] = l(:notice_successful_update)
395 redirect_to :action => 'list_issues', :id => @project
405 redirect_to :action => 'list_issues', :id => @project
396 end
406 end
397 end
407 end
398
408
399 # Add a news to @project
409 # Add a news to @project
400 def add_news
410 def add_news
401 @news = News.new(:project => @project)
411 @news = News.new(:project => @project)
402 if request.post?
412 if request.post?
403 @news.attributes = params[:news]
413 @news.attributes = params[:news]
404 @news.author_id = self.logged_in_user.id if self.logged_in_user
414 @news.author_id = self.logged_in_user.id if self.logged_in_user
405 if @news.save
415 if @news.save
406 flash[:notice] = l(:notice_successful_create)
416 flash[:notice] = l(:notice_successful_create)
407 redirect_to :action => 'list_news', :id => @project
417 redirect_to :action => 'list_news', :id => @project
408 end
418 end
409 end
419 end
410 end
420 end
411
421
412 # Show news list of @project
422 # Show news list of @project
413 def list_news
423 def list_news
414 @news_pages, @news = paginate :news, :per_page => 10, :conditions => ["project_id=?", @project.id], :include => :author, :order => "#{News.table_name}.created_on DESC"
424 @news_pages, @news = paginate :news, :per_page => 10, :conditions => ["project_id=?", @project.id], :include => :author, :order => "#{News.table_name}.created_on DESC"
415 render :action => "list_news", :layout => false if request.xhr?
425 render :action => "list_news", :layout => false if request.xhr?
416 end
426 end
417
427
418 def add_file
428 def add_file
419 if request.post?
429 if request.post?
420 @version = @project.versions.find_by_id(params[:version_id])
430 @version = @project.versions.find_by_id(params[:version_id])
421 # Save the attachments
431 # Save the attachments
422 @attachments = []
432 @attachments = []
423 params[:attachments].each { |file|
433 params[:attachments].each { |file|
424 next unless file.size > 0
434 next unless file.size > 0
425 a = Attachment.create(:container => @version, :file => file, :author => logged_in_user)
435 a = Attachment.create(:container => @version, :file => file, :author => logged_in_user)
426 @attachments << a unless a.new_record?
436 @attachments << a unless a.new_record?
427 } if params[:attachments] and params[:attachments].is_a? Array
437 } if params[:attachments] and params[:attachments].is_a? Array
428 Mailer.deliver_attachments_add(@attachments) if !@attachments.empty? and Permission.find_by_controller_and_action(params[:controller], params[:action]).mail_enabled?
438 Mailer.deliver_attachments_add(@attachments) if !@attachments.empty? and Permission.find_by_controller_and_action(params[:controller], params[:action]).mail_enabled?
429 redirect_to :controller => 'projects', :action => 'list_files', :id => @project
439 redirect_to :controller => 'projects', :action => 'list_files', :id => @project
430 end
440 end
431 @versions = @project.versions.sort
441 @versions = @project.versions.sort
432 end
442 end
433
443
434 def list_files
444 def list_files
435 @versions = @project.versions.sort
445 @versions = @project.versions.sort
436 end
446 end
437
447
438 # Show changelog for @project
448 # Show changelog for @project
439 def changelog
449 def changelog
440 @trackers = Tracker.find(:all, :conditions => ["is_in_chlog=?", true], :order => 'position')
450 @trackers = Tracker.find(:all, :conditions => ["is_in_chlog=?", true], :order => 'position')
441 retrieve_selected_tracker_ids(@trackers)
451 retrieve_selected_tracker_ids(@trackers)
442 @versions = @project.versions.sort
452 @versions = @project.versions.sort
443 end
453 end
444
454
445 def roadmap
455 def roadmap
446 @trackers = Tracker.find(:all, :conditions => ["is_in_roadmap=?", true], :order => 'position')
456 @trackers = Tracker.find(:all, :conditions => ["is_in_roadmap=?", true], :order => 'position')
447 retrieve_selected_tracker_ids(@trackers)
457 retrieve_selected_tracker_ids(@trackers)
448 @versions = @project.versions.sort
458 @versions = @project.versions.sort
449 @versions = @versions.select {|v| !v.completed? } unless params[:completed]
459 @versions = @versions.select {|v| !v.completed? } unless params[:completed]
450 end
460 end
451
461
452 def activity
462 def activity
453 if params[:year] and params[:year].to_i > 1900
463 if params[:year] and params[:year].to_i > 1900
454 @year = params[:year].to_i
464 @year = params[:year].to_i
455 if params[:month] and params[:month].to_i > 0 and params[:month].to_i < 13
465 if params[:month] and params[:month].to_i > 0 and params[:month].to_i < 13
456 @month = params[:month].to_i
466 @month = params[:month].to_i
457 end
467 end
458 end
468 end
459 @year ||= Date.today.year
469 @year ||= Date.today.year
460 @month ||= Date.today.month
470 @month ||= Date.today.month
461
471
462 @date_from = Date.civil(@year, @month, 1)
472 @date_from = Date.civil(@year, @month, 1)
463 @date_to = @date_from >> 1
473 @date_to = @date_from >> 1
464
474
465 @events_by_day = {}
475 @events_by_day = {}
466
476
467 unless params[:show_issues] == "0"
477 unless params[:show_issues] == "0"
468 @project.issues.find(:all, :include => [:author], :conditions => ["#{Issue.table_name}.created_on BETWEEN ? AND ?", @date_from, @date_to] ).each { |i|
478 @project.issues.find(:all, :include => [:author], :conditions => ["#{Issue.table_name}.created_on BETWEEN ? AND ?", @date_from, @date_to] ).each { |i|
469 @events_by_day[i.created_on.to_date] ||= []
479 @events_by_day[i.created_on.to_date] ||= []
470 @events_by_day[i.created_on.to_date] << i
480 @events_by_day[i.created_on.to_date] << i
471 }
481 }
472 @project.issue_changes.find(:all, :include => :details, :conditions => ["(#{Journal.table_name}.created_on BETWEEN ? AND ?) AND (#{JournalDetail.table_name}.prop_key = 'status_id')", @date_from, @date_to] ).each { |i|
482 @project.issue_changes.find(:all, :include => :details, :conditions => ["(#{Journal.table_name}.created_on BETWEEN ? AND ?) AND (#{JournalDetail.table_name}.prop_key = 'status_id')", @date_from, @date_to] ).each { |i|
473 @events_by_day[i.created_on.to_date] ||= []
483 @events_by_day[i.created_on.to_date] ||= []
474 @events_by_day[i.created_on.to_date] << i
484 @events_by_day[i.created_on.to_date] << i
475 }
485 }
476 @show_issues = 1
486 @show_issues = 1
477 end
487 end
478
488
479 unless params[:show_news] == "0"
489 unless params[:show_news] == "0"
480 @project.news.find(:all, :conditions => ["#{News.table_name}.created_on BETWEEN ? AND ?", @date_from, @date_to], :include => :author ).each { |i|
490 @project.news.find(:all, :conditions => ["#{News.table_name}.created_on BETWEEN ? AND ?", @date_from, @date_to], :include => :author ).each { |i|
481 @events_by_day[i.created_on.to_date] ||= []
491 @events_by_day[i.created_on.to_date] ||= []
482 @events_by_day[i.created_on.to_date] << i
492 @events_by_day[i.created_on.to_date] << i
483 }
493 }
484 @show_news = 1
494 @show_news = 1
485 end
495 end
486
496
487 unless params[:show_files] == "0"
497 unless params[:show_files] == "0"
488 Attachment.find(:all, :select => "#{Attachment.table_name}.*",
498 Attachment.find(:all, :select => "#{Attachment.table_name}.*",
489 :joins => "LEFT JOIN #{Version.table_name} ON #{Version.table_name}.id = #{Attachment.table_name}.container_id",
499 :joins => "LEFT JOIN #{Version.table_name} ON #{Version.table_name}.id = #{Attachment.table_name}.container_id",
490 :conditions => ["#{Attachment.table_name}.container_type='Version' and #{Version.table_name}.project_id=? and #{Attachment.table_name}.created_on BETWEEN ? AND ?", @project.id, @date_from, @date_to],
500 :conditions => ["#{Attachment.table_name}.container_type='Version' and #{Version.table_name}.project_id=? and #{Attachment.table_name}.created_on BETWEEN ? AND ?", @project.id, @date_from, @date_to],
491 :include => :author ).each { |i|
501 :include => :author ).each { |i|
492 @events_by_day[i.created_on.to_date] ||= []
502 @events_by_day[i.created_on.to_date] ||= []
493 @events_by_day[i.created_on.to_date] << i
503 @events_by_day[i.created_on.to_date] << i
494 }
504 }
495 @show_files = 1
505 @show_files = 1
496 end
506 end
497
507
498 unless params[:show_documents] == "0"
508 unless params[:show_documents] == "0"
499 @project.documents.find(:all, :conditions => ["#{Document.table_name}.created_on BETWEEN ? AND ?", @date_from, @date_to] ).each { |i|
509 @project.documents.find(:all, :conditions => ["#{Document.table_name}.created_on BETWEEN ? AND ?", @date_from, @date_to] ).each { |i|
500 @events_by_day[i.created_on.to_date] ||= []
510 @events_by_day[i.created_on.to_date] ||= []
501 @events_by_day[i.created_on.to_date] << i
511 @events_by_day[i.created_on.to_date] << i
502 }
512 }
503 Attachment.find(:all, :select => "attachments.*",
513 Attachment.find(:all, :select => "attachments.*",
504 :joins => "LEFT JOIN #{Document.table_name} ON #{Document.table_name}.id = #{Attachment.table_name}.container_id",
514 :joins => "LEFT JOIN #{Document.table_name} ON #{Document.table_name}.id = #{Attachment.table_name}.container_id",
505 :conditions => ["#{Attachment.table_name}.container_type='Document' and #{Document.table_name}.project_id=? and #{Attachment.table_name}.created_on BETWEEN ? AND ?", @project.id, @date_from, @date_to],
515 :conditions => ["#{Attachment.table_name}.container_type='Document' and #{Document.table_name}.project_id=? and #{Attachment.table_name}.created_on BETWEEN ? AND ?", @project.id, @date_from, @date_to],
506 :include => :author ).each { |i|
516 :include => :author ).each { |i|
507 @events_by_day[i.created_on.to_date] ||= []
517 @events_by_day[i.created_on.to_date] ||= []
508 @events_by_day[i.created_on.to_date] << i
518 @events_by_day[i.created_on.to_date] << i
509 }
519 }
510 @show_documents = 1
520 @show_documents = 1
511 end
521 end
512
522
513 unless @project.wiki.nil? || params[:show_wiki_edits] == "0"
523 unless @project.wiki.nil? || params[:show_wiki_edits] == "0"
514 select = "#{WikiContent.versioned_table_name}.updated_on, #{WikiContent.versioned_table_name}.comments, " +
524 select = "#{WikiContent.versioned_table_name}.updated_on, #{WikiContent.versioned_table_name}.comments, " +
515 "#{WikiContent.versioned_table_name}.#{WikiContent.version_column}, #{WikiPage.table_name}.title"
525 "#{WikiContent.versioned_table_name}.#{WikiContent.version_column}, #{WikiPage.table_name}.title"
516 joins = "LEFT JOIN #{WikiPage.table_name} ON #{WikiPage.table_name}.id = #{WikiContent.versioned_table_name}.page_id " +
526 joins = "LEFT JOIN #{WikiPage.table_name} ON #{WikiPage.table_name}.id = #{WikiContent.versioned_table_name}.page_id " +
517 "LEFT JOIN #{Wiki.table_name} ON #{Wiki.table_name}.id = #{WikiPage.table_name}.wiki_id "
527 "LEFT JOIN #{Wiki.table_name} ON #{Wiki.table_name}.id = #{WikiPage.table_name}.wiki_id "
518 conditions = ["#{Wiki.table_name}.project_id = ? AND #{WikiContent.versioned_table_name}.updated_on BETWEEN ? AND ?",
528 conditions = ["#{Wiki.table_name}.project_id = ? AND #{WikiContent.versioned_table_name}.updated_on BETWEEN ? AND ?",
519 @project.id, @date_from, @date_to]
529 @project.id, @date_from, @date_to]
520
530
521 WikiContent.versioned_class.find(:all, :select => select, :joins => joins, :conditions => conditions).each { |i|
531 WikiContent.versioned_class.find(:all, :select => select, :joins => joins, :conditions => conditions).each { |i|
522 # We provide this alias so all events can be treated in the same manner
532 # We provide this alias so all events can be treated in the same manner
523 def i.created_on
533 def i.created_on
524 self.updated_on
534 self.updated_on
525 end
535 end
526
536
527 @events_by_day[i.created_on.to_date] ||= []
537 @events_by_day[i.created_on.to_date] ||= []
528 @events_by_day[i.created_on.to_date] << i
538 @events_by_day[i.created_on.to_date] << i
529 }
539 }
530 @show_wiki_edits = 1
540 @show_wiki_edits = 1
531 end
541 end
532
542
533 unless @project.repository.nil? || params[:show_changesets] == "0"
543 unless @project.repository.nil? || params[:show_changesets] == "0"
534 @project.repository.changesets.find(:all, :conditions => ["#{Changeset.table_name}.committed_on BETWEEN ? AND ?", @date_from, @date_to]).each { |i|
544 @project.repository.changesets.find(:all, :conditions => ["#{Changeset.table_name}.committed_on BETWEEN ? AND ?", @date_from, @date_to]).each { |i|
535 def i.created_on
545 def i.created_on
536 self.committed_on
546 self.committed_on
537 end
547 end
538 @events_by_day[i.created_on.to_date] ||= []
548 @events_by_day[i.created_on.to_date] ||= []
539 @events_by_day[i.created_on.to_date] << i
549 @events_by_day[i.created_on.to_date] << i
540 }
550 }
541 @show_changesets = 1
551 @show_changesets = 1
542 end
552 end
543
553
544 render :layout => false if request.xhr?
554 render :layout => false if request.xhr?
545 end
555 end
546
556
547 def calendar
557 def calendar
548 @trackers = Tracker.find(:all, :order => 'position')
558 @trackers = Tracker.find(:all, :order => 'position')
549 retrieve_selected_tracker_ids(@trackers)
559 retrieve_selected_tracker_ids(@trackers)
550
560
551 if params[:year] and params[:year].to_i > 1900
561 if params[:year] and params[:year].to_i > 1900
552 @year = params[:year].to_i
562 @year = params[:year].to_i
553 if params[:month] and params[:month].to_i > 0 and params[:month].to_i < 13
563 if params[:month] and params[:month].to_i > 0 and params[:month].to_i < 13
554 @month = params[:month].to_i
564 @month = params[:month].to_i
555 end
565 end
556 end
566 end
557 @year ||= Date.today.year
567 @year ||= Date.today.year
558 @month ||= Date.today.month
568 @month ||= Date.today.month
559
569
560 @date_from = Date.civil(@year, @month, 1)
570 @date_from = Date.civil(@year, @month, 1)
561 @date_to = (@date_from >> 1)-1
571 @date_to = (@date_from >> 1)-1
562 # start on monday
572 # start on monday
563 @date_from = @date_from - (@date_from.cwday-1)
573 @date_from = @date_from - (@date_from.cwday-1)
564 # finish on sunday
574 # finish on sunday
565 @date_to = @date_to + (7-@date_to.cwday)
575 @date_to = @date_to + (7-@date_to.cwday)
566
576
567 @events = []
577 @events = []
568 @project.issues_with_subprojects(params[:with_subprojects]) do
578 @project.issues_with_subprojects(params[:with_subprojects]) do
569 @events += Issue.find(:all,
579 @events += Issue.find(:all,
570 :include => [:tracker, :status, :assigned_to, :priority, :project],
580 :include => [:tracker, :status, :assigned_to, :priority, :project],
571 :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]
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]
572 ) unless @selected_tracker_ids.empty?
582 ) unless @selected_tracker_ids.empty?
573 end
583 end
574 @events += @project.versions.find(:all, :conditions => ["effective_date BETWEEN ? AND ?", @date_from, @date_to])
584 @events += @project.versions.find(:all, :conditions => ["effective_date BETWEEN ? AND ?", @date_from, @date_to])
575
585
576 @ending_events_by_days = @events.group_by {|event| event.due_date}
586 @ending_events_by_days = @events.group_by {|event| event.due_date}
577 @starting_events_by_days = @events.group_by {|event| event.start_date}
587 @starting_events_by_days = @events.group_by {|event| event.start_date}
578
588
579 render :layout => false if request.xhr?
589 render :layout => false if request.xhr?
580 end
590 end
581
591
582 def gantt
592 def gantt
583 @trackers = Tracker.find(:all, :order => 'position')
593 @trackers = Tracker.find(:all, :order => 'position')
584 retrieve_selected_tracker_ids(@trackers)
594 retrieve_selected_tracker_ids(@trackers)
585
595
586 if params[:year] and params[:year].to_i >0
596 if params[:year] and params[:year].to_i >0
587 @year_from = params[:year].to_i
597 @year_from = params[:year].to_i
588 if params[:month] and params[:month].to_i >=1 and params[:month].to_i <= 12
598 if params[:month] and params[:month].to_i >=1 and params[:month].to_i <= 12
589 @month_from = params[:month].to_i
599 @month_from = params[:month].to_i
590 else
600 else
591 @month_from = 1
601 @month_from = 1
592 end
602 end
593 else
603 else
594 @month_from ||= (Date.today << 1).month
604 @month_from ||= (Date.today << 1).month
595 @year_from ||= (Date.today << 1).year
605 @year_from ||= (Date.today << 1).year
596 end
606 end
597
607
598 @zoom = (params[:zoom].to_i > 0 and params[:zoom].to_i < 5) ? params[:zoom].to_i : 2
608 @zoom = (params[:zoom].to_i > 0 and params[:zoom].to_i < 5) ? params[:zoom].to_i : 2
599 @months = (params[:months].to_i > 0 and params[:months].to_i < 25) ? params[:months].to_i : 6
609 @months = (params[:months].to_i > 0 and params[:months].to_i < 25) ? params[:months].to_i : 6
600
610
601 @date_from = Date.civil(@year_from, @month_from, 1)
611 @date_from = Date.civil(@year_from, @month_from, 1)
602 @date_to = (@date_from >> @months) - 1
612 @date_to = (@date_from >> @months) - 1
603
613
604 @events = []
614 @events = []
605 @project.issues_with_subprojects(params[:with_subprojects]) do
615 @project.issues_with_subprojects(params[:with_subprojects]) do
606 @events += Issue.find(:all,
616 @events += Issue.find(:all,
607 :order => "start_date, due_date",
617 :order => "start_date, due_date",
608 :include => [:tracker, :status, :assigned_to, :priority, :project],
618 :include => [:tracker, :status, :assigned_to, :priority, :project],
609 :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]
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]
610 ) unless @selected_tracker_ids.empty?
620 ) unless @selected_tracker_ids.empty?
611 end
621 end
612 @events += @project.versions.find(:all, :conditions => ["effective_date BETWEEN ? AND ?", @date_from, @date_to])
622 @events += @project.versions.find(:all, :conditions => ["effective_date BETWEEN ? AND ?", @date_from, @date_to])
613 @events.sort! {|x,y| x.start_date <=> y.start_date }
623 @events.sort! {|x,y| x.start_date <=> y.start_date }
614
624
615 if params[:output]=='pdf'
625 if params[:output]=='pdf'
616 @options_for_rfpdf ||= {}
626 @options_for_rfpdf ||= {}
617 @options_for_rfpdf[:file_name] = "gantt.pdf"
627 @options_for_rfpdf[:file_name] = "gantt.pdf"
618 render :template => "projects/gantt.rfpdf", :layout => false
628 render :template => "projects/gantt.rfpdf", :layout => false
619 else
629 else
620 render :template => "projects/gantt.rhtml"
630 render :template => "projects/gantt.rhtml"
621 end
631 end
622 end
632 end
623
633
624 def feeds
634 def feeds
625 @queries = @project.queries.find :all, :conditions => ["is_public=? or user_id=?", true, (logged_in_user ? logged_in_user.id : 0)]
635 @queries = @project.queries.find :all, :conditions => ["is_public=? or user_id=?", true, (logged_in_user ? logged_in_user.id : 0)]
626 @key = logged_in_user.get_or_create_rss_key.value if logged_in_user
636 @key = logged_in_user.get_or_create_rss_key.value if logged_in_user
627 end
637 end
628
638
629 private
639 private
630 # Find project of id params[:id]
640 # Find project of id params[:id]
631 # if not found, redirect to project list
641 # if not found, redirect to project list
632 # Used as a before_filter
642 # Used as a before_filter
633 def find_project
643 def find_project
634 @project = Project.find(params[:id])
644 @project = Project.find(params[:id])
635 @html_title = @project.name
645 @html_title = @project.name
636 rescue ActiveRecord::RecordNotFound
646 rescue ActiveRecord::RecordNotFound
637 render_404
647 render_404
638 end
648 end
639
649
640 def retrieve_selected_tracker_ids(selectable_trackers)
650 def retrieve_selected_tracker_ids(selectable_trackers)
641 if ids = params[:tracker_ids]
651 if ids = params[:tracker_ids]
642 @selected_tracker_ids = (ids.is_a? Array) ? ids.collect { |id| id.to_i.to_s } : ids.split('/').collect { |id| id.to_i.to_s }
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 }
643 else
653 else
644 @selected_tracker_ids = selectable_trackers.collect {|t| t.id.to_s }
654 @selected_tracker_ids = selectable_trackers.collect {|t| t.id.to_s }
645 end
655 end
646 end
656 end
647
657
648 # Retrieve query from session or build a new query
658 # Retrieve query from session or build a new query
649 def retrieve_query
659 def retrieve_query
650 if params[:query_id]
660 if params[:query_id]
651 @query = @project.queries.find(params[:query_id])
661 @query = @project.queries.find(params[:query_id])
652 @query.executed_by = logged_in_user
662 @query.executed_by = logged_in_user
653 session[:query] = @query
663 session[:query] = @query
654 else
664 else
655 if params[:set_filter] or !session[:query] or session[:query].project_id != @project.id
665 if params[:set_filter] or !session[:query] or session[:query].project_id != @project.id
656 # Give it a name, required to be valid
666 # Give it a name, required to be valid
657 @query = Query.new(:name => "_", :executed_by => logged_in_user)
667 @query = Query.new(:name => "_", :executed_by => logged_in_user)
658 @query.project = @project
668 @query.project = @project
659 if params[:fields] and params[:fields].is_a? Array
669 if params[:fields] and params[:fields].is_a? Array
660 params[:fields].each do |field|
670 params[:fields].each do |field|
661 @query.add_filter(field, params[:operators][field], params[:values][field])
671 @query.add_filter(field, params[:operators][field], params[:values][field])
662 end
672 end
663 else
673 else
664 @query.available_filters.keys.each do |field|
674 @query.available_filters.keys.each do |field|
665 @query.add_short_filter(field, params[field]) if params[field]
675 @query.add_short_filter(field, params[field]) if params[field]
666 end
676 end
667 end
677 end
668 session[:query] = @query
678 session[:query] = @query
669 else
679 else
670 @query = session[:query]
680 @query = session[:query]
671 end
681 end
672 end
682 end
673 end
683 end
674 end
684 end
@@ -1,300 +1,305
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 RedCloth
18 class RedCloth
19 # Patch for RedCloth. Fixed in RedCloth r128 but _why hasn't released it yet.
19 # Patch for RedCloth. Fixed in RedCloth r128 but _why hasn't released it yet.
20 # <a href="http://code.whytheluckystiff.net/redcloth/changeset/128">http://code.whytheluckystiff.net/redcloth/changeset/128</a>
20 # <a href="http://code.whytheluckystiff.net/redcloth/changeset/128">http://code.whytheluckystiff.net/redcloth/changeset/128</a>
21 def hard_break( text )
21 def hard_break( text )
22 text.gsub!( /(.)\n(?!\n|\Z| *([#*=]+(\s|$)|[{|]))/, "\\1<br />" ) if hard_breaks
22 text.gsub!( /(.)\n(?!\n|\Z| *([#*=]+(\s|$)|[{|]))/, "\\1<br />" ) if hard_breaks
23 end
23 end
24 end
24 end
25
25
26 module ApplicationHelper
26 module ApplicationHelper
27
27
28 # Return current logged in user or nil
28 # Return current logged in user or nil
29 def loggedin?
29 def loggedin?
30 @logged_in_user
30 @logged_in_user
31 end
31 end
32
32
33 # Return true if user is logged in and is admin, otherwise false
33 # Return true if user is logged in and is admin, otherwise false
34 def admin_loggedin?
34 def admin_loggedin?
35 @logged_in_user and @logged_in_user.admin?
35 @logged_in_user and @logged_in_user.admin?
36 end
36 end
37
37
38 # Return true if user is authorized for controller/action, otherwise false
38 # Return true if user is authorized for controller/action, otherwise false
39 def authorize_for(controller, action)
39 def authorize_for(controller, action)
40 # check if action is allowed on public projects
40 # check if action is allowed on public projects
41 if @project.is_public? and Permission.allowed_to_public "%s/%s" % [ controller, action ]
41 if @project.is_public? and Permission.allowed_to_public "%s/%s" % [ controller, action ]
42 return true
42 return true
43 end
43 end
44 # check if user is authorized
44 # check if user is authorized
45 if @logged_in_user and (@logged_in_user.admin? or Permission.allowed_to_role( "%s/%s" % [ controller, action ], @logged_in_user.role_for_project(@project) ) )
45 if @logged_in_user and (@logged_in_user.admin? or Permission.allowed_to_role( "%s/%s" % [ controller, action ], @logged_in_user.role_for_project(@project) ) )
46 return true
46 return true
47 end
47 end
48 return false
48 return false
49 end
49 end
50
50
51 # Display a link if user is authorized
51 # Display a link if user is authorized
52 def link_to_if_authorized(name, options = {}, html_options = nil, *parameters_for_method_reference)
52 def link_to_if_authorized(name, options = {}, html_options = nil, *parameters_for_method_reference)
53 link_to(name, options, html_options, *parameters_for_method_reference) if authorize_for(options[:controller], options[:action])
53 link_to(name, options, html_options, *parameters_for_method_reference) if authorize_for(options[:controller], options[:action])
54 end
54 end
55
55
56 # Display a link to user's account page
56 # Display a link to user's account page
57 def link_to_user(user)
57 def link_to_user(user)
58 link_to user.name, :controller => 'account', :action => 'show', :id => user
58 link_to user.name, :controller => 'account', :action => 'show', :id => user
59 end
59 end
60
60
61 def link_to_issue(issue)
61 def link_to_issue(issue)
62 link_to "#{issue.tracker.name} ##{issue.id}", :controller => "issues", :action => "show", :id => issue
62 link_to "#{issue.tracker.name} ##{issue.id}", :controller => "issues", :action => "show", :id => issue
63 end
63 end
64
64
65 def toggle_link(name, id, options={})
65 def toggle_link(name, id, options={})
66 onclick = "Element.toggle('#{id}'); "
66 onclick = "Element.toggle('#{id}'); "
67 onclick << (options[:focus] ? "Form.Element.focus('#{options[:focus]}'); " : "this.blur(); ")
67 onclick << (options[:focus] ? "Form.Element.focus('#{options[:focus]}'); " : "this.blur(); ")
68 onclick << "return false;"
68 onclick << "return false;"
69 link_to(name, "#", :onclick => onclick)
69 link_to(name, "#", :onclick => onclick)
70 end
70 end
71
71
72 def image_to_function(name, function, html_options = {})
72 def image_to_function(name, function, html_options = {})
73 html_options.symbolize_keys!
73 html_options.symbolize_keys!
74 tag(:input, html_options.merge({
74 tag(:input, html_options.merge({
75 :type => "image", :src => image_path(name),
75 :type => "image", :src => image_path(name),
76 :onclick => (html_options[:onclick] ? "#{html_options[:onclick]}; " : "") + "#{function};"
76 :onclick => (html_options[:onclick] ? "#{html_options[:onclick]}; " : "") + "#{function};"
77 }))
77 }))
78 end
78 end
79
79
80 def prompt_to_remote(name, text, param, url, html_options = {})
81 html_options[:onclick] = "promptToRemote('#{text}', '#{param}', '#{url_for(url)}'); return false;"
82 link_to name, {}, html_options
83 end
84
80 def format_date(date)
85 def format_date(date)
81 return nil unless date
86 return nil unless date
82 @date_format_setting ||= Setting.date_format.to_i
87 @date_format_setting ||= Setting.date_format.to_i
83 @date_format_setting == 0 ? l_date(date) : date.strftime("%Y-%m-%d")
88 @date_format_setting == 0 ? l_date(date) : date.strftime("%Y-%m-%d")
84 end
89 end
85
90
86 def format_time(time)
91 def format_time(time)
87 return nil unless time
92 return nil unless time
88 @date_format_setting ||= Setting.date_format.to_i
93 @date_format_setting ||= Setting.date_format.to_i
89 time = time.to_time if time.is_a?(String)
94 time = time.to_time if time.is_a?(String)
90 @date_format_setting == 0 ? l_datetime(time) : (time.strftime("%Y-%m-%d") + ' ' + l_time(time))
95 @date_format_setting == 0 ? l_datetime(time) : (time.strftime("%Y-%m-%d") + ' ' + l_time(time))
91 end
96 end
92
97
93 def day_name(day)
98 def day_name(day)
94 l(:general_day_names).split(',')[day-1]
99 l(:general_day_names).split(',')[day-1]
95 end
100 end
96
101
97 def month_name(month)
102 def month_name(month)
98 l(:actionview_datehelper_select_month_names).split(',')[month-1]
103 l(:actionview_datehelper_select_month_names).split(',')[month-1]
99 end
104 end
100
105
101 def pagination_links_full(paginator, options={}, html_options={})
106 def pagination_links_full(paginator, options={}, html_options={})
102 page_param = options.delete(:page_param) || :page
107 page_param = options.delete(:page_param) || :page
103
108
104 html = ''
109 html = ''
105 html << link_to_remote(('&#171; ' + l(:label_previous)),
110 html << link_to_remote(('&#171; ' + l(:label_previous)),
106 {:update => "content", :url => options.merge(page_param => paginator.current.previous)},
111 {:update => "content", :url => options.merge(page_param => paginator.current.previous)},
107 {:href => url_for(:params => options.merge(page_param => paginator.current.previous))}) + ' ' if paginator.current.previous
112 {:href => url_for(:params => options.merge(page_param => paginator.current.previous))}) + ' ' if paginator.current.previous
108
113
109 html << (pagination_links_each(paginator, options) do |n|
114 html << (pagination_links_each(paginator, options) do |n|
110 link_to_remote(n.to_s,
115 link_to_remote(n.to_s,
111 {:url => {:params => options.merge(page_param => n)}, :update => 'content'},
116 {:url => {:params => options.merge(page_param => n)}, :update => 'content'},
112 {:href => url_for(:params => options.merge(page_param => n))})
117 {:href => url_for(:params => options.merge(page_param => n))})
113 end || '')
118 end || '')
114
119
115 html << ' ' + link_to_remote((l(:label_next) + ' &#187;'),
120 html << ' ' + link_to_remote((l(:label_next) + ' &#187;'),
116 {:update => "content", :url => options.merge(page_param => paginator.current.next)},
121 {:update => "content", :url => options.merge(page_param => paginator.current.next)},
117 {:href => url_for(:params => options.merge(page_param => paginator.current.next))}) if paginator.current.next
122 {:href => url_for(:params => options.merge(page_param => paginator.current.next))}) if paginator.current.next
118 html
123 html
119 end
124 end
120
125
121 # textilize text according to system settings and RedCloth availability
126 # textilize text according to system settings and RedCloth availability
122 def textilizable(text, options = {})
127 def textilizable(text, options = {})
123 return "" if text.blank?
128 return "" if text.blank?
124
129
125 # different methods for formatting wiki links
130 # different methods for formatting wiki links
126 case options[:wiki_links]
131 case options[:wiki_links]
127 when :local
132 when :local
128 # used for local links to html files
133 # used for local links to html files
129 format_wiki_link = Proc.new {|project, title| "#{title}.html" }
134 format_wiki_link = Proc.new {|project, title| "#{title}.html" }
130 when :anchor
135 when :anchor
131 # used for single-file wiki export
136 # used for single-file wiki export
132 format_wiki_link = Proc.new {|project, title| "##{title}" }
137 format_wiki_link = Proc.new {|project, title| "##{title}" }
133 else
138 else
134 format_wiki_link = Proc.new {|project, title| url_for :controller => 'wiki', :action => 'index', :id => project, :page => title }
139 format_wiki_link = Proc.new {|project, title| url_for :controller => 'wiki', :action => 'index', :id => project, :page => title }
135 end
140 end
136
141
137 # turn wiki links into html links
142 # turn wiki links into html links
138 # example:
143 # example:
139 # [[mypage]]
144 # [[mypage]]
140 # [[mypage|mytext]]
145 # [[mypage|mytext]]
141 # wiki links can refer other project wikis, using project name or identifier:
146 # wiki links can refer other project wikis, using project name or identifier:
142 # [[project:]] -> wiki starting page
147 # [[project:]] -> wiki starting page
143 # [[project:mypage]]
148 # [[project:mypage]]
144 # [[project:mypage|mytext]]
149 # [[project:mypage|mytext]]
145 text = text.gsub(/\[\[([^\]\|]+)(\|([^\]\|]+))?\]\]/) do |m|
150 text = text.gsub(/\[\[([^\]\|]+)(\|([^\]\|]+))?\]\]/) do |m|
146 project = @project
151 project = @project
147 page = $1
152 page = $1
148 title = $3
153 title = $3
149 if page =~ /^([^\:]+)\:(.*)$/
154 if page =~ /^([^\:]+)\:(.*)$/
150 project = Project.find_by_name($1) || Project.find_by_identifier($1)
155 project = Project.find_by_name($1) || Project.find_by_identifier($1)
151 page = $2
156 page = $2
152 title = $1 if page.blank?
157 title = $1 if page.blank?
153 end
158 end
154 link_to((title || page), format_wiki_link.call(project, Wiki.titleize(page)), :class => 'wiki-page')
159 link_to((title || page), format_wiki_link.call(project, Wiki.titleize(page)), :class => 'wiki-page')
155 end
160 end
156
161
157 # turn issue ids into links
162 # turn issue ids into links
158 # example:
163 # example:
159 # #52 -> <a href="/issues/show/52">#52</a>
164 # #52 -> <a href="/issues/show/52">#52</a>
160 text = text.gsub(/#(\d+)(?=\b)/) {|m| link_to "##{$1}", {:controller => 'issues', :action => 'show', :id => $1}, :class => 'issue' }
165 text = text.gsub(/#(\d+)(?=\b)/) {|m| link_to "##{$1}", {:controller => 'issues', :action => 'show', :id => $1}, :class => 'issue' }
161
166
162 # turn revision ids into links (@project needed)
167 # turn revision ids into links (@project needed)
163 # example:
168 # example:
164 # r52 -> <a href="/repositories/revision/6?rev=52">r52</a> (@project.id is 6)
169 # r52 -> <a href="/repositories/revision/6?rev=52">r52</a> (@project.id is 6)
165 text = text.gsub(/(?=\b)r(\d+)(?=\b)/) {|m| link_to "r#{$1}", {:controller => 'repositories', :action => 'revision', :id => @project.id, :rev => $1}, :class => 'changeset' } if @project
170 text = text.gsub(/(?=\b)r(\d+)(?=\b)/) {|m| link_to "r#{$1}", {:controller => 'repositories', :action => 'revision', :id => @project.id, :rev => $1}, :class => 'changeset' } if @project
166
171
167 # when using an image link, try to use an attachment, if possible
172 # when using an image link, try to use an attachment, if possible
168 attachments = options[:attachments]
173 attachments = options[:attachments]
169 if attachments
174 if attachments
170 text = text.gsub(/!([<>=]*)(\S+\.(gif|jpg|jpeg|png))!/) do |m|
175 text = text.gsub(/!([<>=]*)(\S+\.(gif|jpg|jpeg|png))!/) do |m|
171 align = $1
176 align = $1
172 filename = $2
177 filename = $2
173 rf = Regexp.new(filename, Regexp::IGNORECASE)
178 rf = Regexp.new(filename, Regexp::IGNORECASE)
174 # search for the picture in attachments
179 # search for the picture in attachments
175 if found = attachments.detect { |att| att.filename =~ rf }
180 if found = attachments.detect { |att| att.filename =~ rf }
176 image_url = url_for :controller => 'attachments', :action => 'download', :id => found.id
181 image_url = url_for :controller => 'attachments', :action => 'download', :id => found.id
177 "!#{align}#{image_url}!"
182 "!#{align}#{image_url}!"
178 else
183 else
179 "!#{align}#{filename}!"
184 "!#{align}#{filename}!"
180 end
185 end
181 end
186 end
182 end
187 end
183
188
184 # finally textilize text
189 # finally textilize text
185 @do_textilize ||= (Setting.text_formatting == 'textile') && (ActionView::Helpers::TextHelper.method_defined? "textilize")
190 @do_textilize ||= (Setting.text_formatting == 'textile') && (ActionView::Helpers::TextHelper.method_defined? "textilize")
186 text = @do_textilize ? auto_link(RedCloth.new(text, [:hard_breaks]).to_html) : simple_format(auto_link(h(text)))
191 text = @do_textilize ? auto_link(RedCloth.new(text, [:hard_breaks]).to_html) : simple_format(auto_link(h(text)))
187 end
192 end
188
193
189 # Same as Rails' simple_format helper without using paragraphs
194 # Same as Rails' simple_format helper without using paragraphs
190 def simple_format_without_paragraph(text)
195 def simple_format_without_paragraph(text)
191 text.to_s.
196 text.to_s.
192 gsub(/\r\n?/, "\n"). # \r\n and \r -> \n
197 gsub(/\r\n?/, "\n"). # \r\n and \r -> \n
193 gsub(/\n\n+/, "<br /><br />"). # 2+ newline -> 2 br
198 gsub(/\n\n+/, "<br /><br />"). # 2+ newline -> 2 br
194 gsub(/([^\n]\n)(?=[^\n])/, '\1<br />') # 1 newline -> br
199 gsub(/([^\n]\n)(?=[^\n])/, '\1<br />') # 1 newline -> br
195 end
200 end
196
201
197 def error_messages_for(object_name, options = {})
202 def error_messages_for(object_name, options = {})
198 options = options.symbolize_keys
203 options = options.symbolize_keys
199 object = instance_variable_get("@#{object_name}")
204 object = instance_variable_get("@#{object_name}")
200 if object && !object.errors.empty?
205 if object && !object.errors.empty?
201 # build full_messages here with controller current language
206 # build full_messages here with controller current language
202 full_messages = []
207 full_messages = []
203 object.errors.each do |attr, msg|
208 object.errors.each do |attr, msg|
204 next if msg.nil?
209 next if msg.nil?
205 msg = msg.first if msg.is_a? Array
210 msg = msg.first if msg.is_a? Array
206 if attr == "base"
211 if attr == "base"
207 full_messages << l(msg)
212 full_messages << l(msg)
208 else
213 else
209 full_messages << "&#171; " + (l_has_string?("field_" + attr) ? l("field_" + attr) : object.class.human_attribute_name(attr)) + " &#187; " + l(msg) unless attr == "custom_values"
214 full_messages << "&#171; " + (l_has_string?("field_" + attr) ? l("field_" + attr) : object.class.human_attribute_name(attr)) + " &#187; " + l(msg) unless attr == "custom_values"
210 end
215 end
211 end
216 end
212 # retrieve custom values error messages
217 # retrieve custom values error messages
213 if object.errors[:custom_values]
218 if object.errors[:custom_values]
214 object.custom_values.each do |v|
219 object.custom_values.each do |v|
215 v.errors.each do |attr, msg|
220 v.errors.each do |attr, msg|
216 next if msg.nil?
221 next if msg.nil?
217 msg = msg.first if msg.is_a? Array
222 msg = msg.first if msg.is_a? Array
218 full_messages << "&#171; " + v.custom_field.name + " &#187; " + l(msg)
223 full_messages << "&#171; " + v.custom_field.name + " &#187; " + l(msg)
219 end
224 end
220 end
225 end
221 end
226 end
222 content_tag("div",
227 content_tag("div",
223 content_tag(
228 content_tag(
224 options[:header_tag] || "span", lwr(:gui_validation_error, full_messages.length) + ":"
229 options[:header_tag] || "span", lwr(:gui_validation_error, full_messages.length) + ":"
225 ) +
230 ) +
226 content_tag("ul", full_messages.collect { |msg| content_tag("li", msg) }),
231 content_tag("ul", full_messages.collect { |msg| content_tag("li", msg) }),
227 "id" => options[:id] || "errorExplanation", "class" => options[:class] || "errorExplanation"
232 "id" => options[:id] || "errorExplanation", "class" => options[:class] || "errorExplanation"
228 )
233 )
229 else
234 else
230 ""
235 ""
231 end
236 end
232 end
237 end
233
238
234 def lang_options_for_select(blank=true)
239 def lang_options_for_select(blank=true)
235 (blank ? [["(auto)", ""]] : []) +
240 (blank ? [["(auto)", ""]] : []) +
236 GLoc.valid_languages.collect{|lang| [ ll(lang.to_s, :general_lang_name), lang.to_s]}.sort{|x,y| x.first <=> y.first }
241 GLoc.valid_languages.collect{|lang| [ ll(lang.to_s, :general_lang_name), lang.to_s]}.sort{|x,y| x.first <=> y.first }
237 end
242 end
238
243
239 def label_tag_for(name, option_tags = nil, options = {})
244 def label_tag_for(name, option_tags = nil, options = {})
240 label_text = l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) + (options.delete(:required) ? @template.content_tag("span", " *", :class => "required"): "")
245 label_text = l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) + (options.delete(:required) ? @template.content_tag("span", " *", :class => "required"): "")
241 content_tag("label", label_text)
246 content_tag("label", label_text)
242 end
247 end
243
248
244 def labelled_tabular_form_for(name, object, options, &proc)
249 def labelled_tabular_form_for(name, object, options, &proc)
245 options[:html] ||= {}
250 options[:html] ||= {}
246 options[:html].store :class, "tabular"
251 options[:html].store :class, "tabular"
247 form_for(name, object, options.merge({ :builder => TabularFormBuilder, :lang => current_language}), &proc)
252 form_for(name, object, options.merge({ :builder => TabularFormBuilder, :lang => current_language}), &proc)
248 end
253 end
249
254
250 def check_all_links(form_name)
255 def check_all_links(form_name)
251 link_to_function(l(:button_check_all), "checkAll('#{form_name}', true)") +
256 link_to_function(l(:button_check_all), "checkAll('#{form_name}', true)") +
252 " | " +
257 " | " +
253 link_to_function(l(:button_uncheck_all), "checkAll('#{form_name}', false)")
258 link_to_function(l(:button_uncheck_all), "checkAll('#{form_name}', false)")
254 end
259 end
255
260
256 def calendar_for(field_id)
261 def calendar_for(field_id)
257 image_tag("calendar.png", {:id => "#{field_id}_trigger",:class => "calendar-trigger"}) +
262 image_tag("calendar.png", {:id => "#{field_id}_trigger",:class => "calendar-trigger"}) +
258 javascript_tag("Calendar.setup({inputField : '#{field_id}', ifFormat : '%Y-%m-%d', button : '#{field_id}_trigger' });")
263 javascript_tag("Calendar.setup({inputField : '#{field_id}', ifFormat : '%Y-%m-%d', button : '#{field_id}_trigger' });")
259 end
264 end
260
265
261 def wikitoolbar_for(field_id)
266 def wikitoolbar_for(field_id)
262 return '' unless Setting.text_formatting == 'textile'
267 return '' unless Setting.text_formatting == 'textile'
263 javascript_include_tag('jstoolbar') + javascript_tag("var toolbar = new jsToolBar($('#{field_id}')); toolbar.draw();")
268 javascript_include_tag('jstoolbar') + javascript_tag("var toolbar = new jsToolBar($('#{field_id}')); toolbar.draw();")
264 end
269 end
265 end
270 end
266
271
267 class TabularFormBuilder < ActionView::Helpers::FormBuilder
272 class TabularFormBuilder < ActionView::Helpers::FormBuilder
268 include GLoc
273 include GLoc
269
274
270 def initialize(object_name, object, template, options, proc)
275 def initialize(object_name, object, template, options, proc)
271 set_language_if_valid options.delete(:lang)
276 set_language_if_valid options.delete(:lang)
272 @object_name, @object, @template, @options, @proc = object_name, object, template, options, proc
277 @object_name, @object, @template, @options, @proc = object_name, object, template, options, proc
273 end
278 end
274
279
275 (field_helpers - %w(radio_button hidden_field) + %w(date_select)).each do |selector|
280 (field_helpers - %w(radio_button hidden_field) + %w(date_select)).each do |selector|
276 src = <<-END_SRC
281 src = <<-END_SRC
277 def #{selector}(field, options = {})
282 def #{selector}(field, options = {})
278 return super if options.delete :no_label
283 return super if options.delete :no_label
279 label_text = l(options[:label]) if options[:label]
284 label_text = l(options[:label]) if options[:label]
280 label_text ||= l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym)
285 label_text ||= l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym)
281 label_text << @template.content_tag("span", " *", :class => "required") if options.delete(:required)
286 label_text << @template.content_tag("span", " *", :class => "required") if options.delete(:required)
282 label = @template.content_tag("label", label_text,
287 label = @template.content_tag("label", label_text,
283 :class => (@object && @object.errors[field] ? "error" : nil),
288 :class => (@object && @object.errors[field] ? "error" : nil),
284 :for => (@object_name.to_s + "_" + field.to_s))
289 :for => (@object_name.to_s + "_" + field.to_s))
285 label + super
290 label + super
286 end
291 end
287 END_SRC
292 END_SRC
288 class_eval src, __FILE__, __LINE__
293 class_eval src, __FILE__, __LINE__
289 end
294 end
290
295
291 def select(field, choices, options = {}, html_options = {})
296 def select(field, choices, options = {}, html_options = {})
292 label_text = l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) + (options.delete(:required) ? @template.content_tag("span", " *", :class => "required"): "")
297 label_text = l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) + (options.delete(:required) ? @template.content_tag("span", " *", :class => "required"): "")
293 label = @template.content_tag("label", label_text,
298 label = @template.content_tag("label", label_text,
294 :class => (@object && @object.errors[field] ? "error" : nil),
299 :class => (@object && @object.errors[field] ? "error" : nil),
295 :for => (@object_name.to_s + "_" + field.to_s))
300 :for => (@object_name.to_s + "_" + field.to_s))
296 label + super
301 label + super
297 end
302 end
298
303
299 end
304 end
300
305
@@ -1,43 +1,47
1 <h2><%= @issue.tracker.name %> #<%= @issue.id %> - <%=h @issue.subject %></h2>
1 <h2><%= @issue.tracker.name %> #<%= @issue.id %> - <%=h @issue.subject %></h2>
2
2
3 <% labelled_tabular_form_for :issue, @issue, :url => {:action => 'edit'} do |f| %>
3 <% labelled_tabular_form_for :issue, @issue, :url => {:action => 'edit'} do |f| %>
4 <%= error_messages_for 'issue' %>
4 <%= error_messages_for 'issue' %>
5 <div class="box">
5 <div class="box">
6 <!--[form:issue]-->
6 <!--[form:issue]-->
7 <div class="splitcontentleft">
7 <div class="splitcontentleft">
8 <p><label><%=l(:field_status)%></label> <%= @issue.status.name %></p>
8 <p><label><%=l(:field_status)%></label> <%= @issue.status.name %></p>
9 <p><%= f.select :priority_id, (@priorities.collect {|p| [p.name, p.id]}), :required => true %></p>
9 <p><%= f.select :priority_id, (@priorities.collect {|p| [p.name, p.id]}), :required => true %></p>
10 <p><%= f.select :assigned_to_id, (@issue.project.members.collect {|m| [m.name, m.user_id]}), :include_blank => true %></p>
10 <p><%= f.select :assigned_to_id, (@issue.project.members.collect {|m| [m.name, m.user_id]}), :include_blank => true %></p>
11 <p><%= f.select :category_id, (@project.issue_categories.collect {|c| [c.name, c.id]}), :include_blank => true %></p>
11 <p><%= f.select :category_id, (@project.issue_categories.collect {|c| [c.name, c.id]}), :include_blank => true %>
12 <%= prompt_to_remote(l(:label_issue_category_new),
13 l(:label_issue_category_new), 'category[name]',
14 {:controller => 'projects', :action => 'add_issue_category', :id => @project},
15 :class => 'small') if authorize_for('projects', 'add_issue_category') %></p>
12 </div>
16 </div>
13
17
14 <div class="splitcontentright">
18 <div class="splitcontentright">
15 <p><%= f.text_field :start_date, :size => 10 %><%= calendar_for('issue_start_date') %></p>
19 <p><%= f.text_field :start_date, :size => 10 %><%= calendar_for('issue_start_date') %></p>
16 <p><%= f.text_field :due_date, :size => 10 %><%= calendar_for('issue_due_date') %></p>
20 <p><%= f.text_field :due_date, :size => 10 %><%= calendar_for('issue_due_date') %></p>
17 <p><%= f.select :done_ratio, ((0..10).to_a.collect {|r| ["#{r*10} %", r*10] }) %></p>
21 <p><%= f.select :done_ratio, ((0..10).to_a.collect {|r| ["#{r*10} %", r*10] }) %></p>
18 </div>
22 </div>
19
23
20 <div class="clear">
24 <div class="clear">
21 <p><%= f.text_field :subject, :size => 80, :required => true %></p>
25 <p><%= f.text_field :subject, :size => 80, :required => true %></p>
22 <p><%= f.text_area :description, :required => true, :cols => 60, :rows => [[10, @issue.description.length / 50].max, 100].min, :class => 'wiki-edit' %></p>
26 <p><%= f.text_area :description, :required => true, :cols => 60, :rows => [[10, @issue.description.length / 50].max, 100].min, :class => 'wiki-edit' %></p>
23
27
24 <% for @custom_value in @custom_values %>
28 <% for @custom_value in @custom_values %>
25 <p><%= custom_field_tag_with_label @custom_value %></p>
29 <p><%= custom_field_tag_with_label @custom_value %></p>
26 <% end %>
30 <% end %>
27
31
28 <p><%= f.select :fixed_version_id, (@project.versions.sort.collect {|v| [v.name, v.id]}), { :include_blank => true } %></p>
32 <p><%= f.select :fixed_version_id, (@project.versions.sort.collect {|v| [v.name, v.id]}), { :include_blank => true } %></p>
29 </div>
33 </div>
30 <!--[eoform:issue]-->
34 <!--[eoform:issue]-->
31 </div>
35 </div>
32 <%= f.hidden_field :lock_version %>
36 <%= f.hidden_field :lock_version %>
33 <%= submit_tag l(:button_save) %>
37 <%= submit_tag l(:button_save) %>
34 <% end %>
38 <% end %>
35
39
36 <%= wikitoolbar_for 'issue_description' %>
40 <%= wikitoolbar_for 'issue_description' %>
37
41
38 <% content_for :header_tags do %>
42 <% content_for :header_tags do %>
39 <%= javascript_include_tag 'calendar/calendar' %>
43 <%= javascript_include_tag 'calendar/calendar' %>
40 <%= javascript_include_tag "calendar/lang/calendar-#{current_language}.js" %>
44 <%= javascript_include_tag "calendar/lang/calendar-#{current_language}.js" %>
41 <%= javascript_include_tag 'calendar/calendar-setup' %>
45 <%= javascript_include_tag 'calendar/calendar-setup' %>
42 <%= stylesheet_link_tag 'calendar' %>
46 <%= stylesheet_link_tag 'calendar' %>
43 <% end %> No newline at end of file
47 <% end %>
@@ -1,48 +1,52
1 <h2><%=l(:label_issue_new)%>: <%= @tracker.name %></h2>
1 <h2><%=l(:label_issue_new)%>: <%= @tracker.name %></h2>
2
2
3 <% labelled_tabular_form_for :issue, @issue, :url => {:action => 'add_issue'}, :html => {:multipart => true} do |f| %>
3 <% labelled_tabular_form_for :issue, @issue, :url => {:action => 'add_issue'}, :html => {:multipart => true} do |f| %>
4 <%= error_messages_for 'issue' %>
4 <%= error_messages_for 'issue' %>
5 <div class="box">
5 <div class="box">
6 <!--[form:issue]-->
6 <!--[form:issue]-->
7 <%= hidden_field_tag 'tracker_id', @tracker.id %>
7 <%= hidden_field_tag 'tracker_id', @tracker.id %>
8
8
9 <div class="splitcontentleft">
9 <div class="splitcontentleft">
10 <p><%= f.select :status_id, (@allowed_statuses.collect {|p| [p.name, p.id]}), :required => true %></p>
10 <p><%= f.select :status_id, (@allowed_statuses.collect {|p| [p.name, p.id]}), :required => true %></p>
11 <p><%= f.select :priority_id, (@priorities.collect {|p| [p.name, p.id]}), :required => true %></p>
11 <p><%= f.select :priority_id, (@priorities.collect {|p| [p.name, p.id]}), :required => true %></p>
12 <p><%= f.select :assigned_to_id, (@issue.project.members.collect {|m| [m.name, m.user_id]}), :include_blank => true %></p>
12 <p><%= f.select :assigned_to_id, (@issue.project.members.collect {|m| [m.name, m.user_id]}), :include_blank => true %></p>
13 <p><%= f.select :category_id, (@project.issue_categories.collect {|c| [c.name, c.id]}), :include_blank => true %></p>
13 <p><%= f.select :category_id, (@project.issue_categories.collect {|c| [c.name, c.id]}), :include_blank => true %>
14 <%= prompt_to_remote(l(:label_issue_category_new),
15 l(:label_issue_category_new), 'category[name]',
16 {:controller => 'projects', :action => 'add_issue_category', :id => @project},
17 :class => 'small') if authorize_for('projects', 'add_issue_category') %></p>
14 </div>
18 </div>
15 <div class="splitcontentright">
19 <div class="splitcontentright">
16 <p><%= f.text_field :start_date, :size => 10 %><%= calendar_for('issue_start_date') %></p>
20 <p><%= f.text_field :start_date, :size => 10 %><%= calendar_for('issue_start_date') %></p>
17 <p><%= f.text_field :due_date, :size => 10 %><%= calendar_for('issue_due_date') %></p>
21 <p><%= f.text_field :due_date, :size => 10 %><%= calendar_for('issue_due_date') %></p>
18 <p><%= f.select :done_ratio, ((0..10).to_a.collect {|r| ["#{r*10} %", r*10] }) %></p>
22 <p><%= f.select :done_ratio, ((0..10).to_a.collect {|r| ["#{r*10} %", r*10] }) %></p>
19 </div>
23 </div>
20
24
21 <div class="clear">
25 <div class="clear">
22 <p><%= f.text_field :subject, :size => 80, :required => true %></p>
26 <p><%= f.text_field :subject, :size => 80, :required => true %></p>
23 <p><%= f.text_area :description, :cols => 60, :rows => 10, :required => true, :class => 'wiki-edit' %></p>
27 <p><%= f.text_area :description, :cols => 60, :rows => 10, :required => true, :class => 'wiki-edit' %></p>
24
28
25 <% for @custom_value in @custom_values %>
29 <% for @custom_value in @custom_values %>
26 <p><%= custom_field_tag_with_label @custom_value %></p>
30 <p><%= custom_field_tag_with_label @custom_value %></p>
27 <% end %>
31 <% end %>
28
32
29 <p><%= f.select :fixed_version_id, (@project.versions.sort.collect {|v| [v.name, v.id]}), { :include_blank => true } %></p>
33 <p><%= f.select :fixed_version_id, (@project.versions.sort.collect {|v| [v.name, v.id]}), { :include_blank => true } %></p>
30
34
31 <p id="attachments_p"><label for="attachment_file"><%=l(:label_attachment)%>
35 <p id="attachments_p"><label for="attachment_file"><%=l(:label_attachment)%>
32 <%= image_to_function "add.png", "addFileField();return false" %></label>
36 <%= image_to_function "add.png", "addFileField();return false" %></label>
33 <%= file_field_tag 'attachments[]', :size => 30 %> <em>(<%= l(:label_max_size) %>: <%= number_to_human_size(Setting.attachment_max_size.to_i.kilobytes) %>)</em></p>
37 <%= file_field_tag 'attachments[]', :size => 30 %> <em>(<%= l(:label_max_size) %>: <%= number_to_human_size(Setting.attachment_max_size.to_i.kilobytes) %>)</em></p>
34
38
35 </div>
39 </div>
36 <!--[eoform:issue]-->
40 <!--[eoform:issue]-->
37 </div>
41 </div>
38 <%= submit_tag l(:button_create) %>
42 <%= submit_tag l(:button_create) %>
39 <% end %>
43 <% end %>
40
44
41 <%= wikitoolbar_for 'issue_description' %>
45 <%= wikitoolbar_for 'issue_description' %>
42
46
43 <% content_for :header_tags do %>
47 <% content_for :header_tags do %>
44 <%= javascript_include_tag 'calendar/calendar' %>
48 <%= javascript_include_tag 'calendar/calendar' %>
45 <%= javascript_include_tag "calendar/lang/calendar-#{current_language}.js" %>
49 <%= javascript_include_tag "calendar/lang/calendar-#{current_language}.js" %>
46 <%= javascript_include_tag 'calendar/calendar-setup' %>
50 <%= javascript_include_tag 'calendar/calendar-setup' %>
47 <%= stylesheet_link_tag 'calendar' %>
51 <%= stylesheet_link_tag 'calendar' %>
48 <% end %> No newline at end of file
52 <% end %>
@@ -1,56 +1,64
1 function checkAll (id, checked) {
1 function checkAll (id, checked) {
2 var el = document.getElementById(id);
2 var el = document.getElementById(id);
3 for (var i = 0; i < el.elements.length; i++) {
3 for (var i = 0; i < el.elements.length; i++) {
4 if (el.elements[i].disabled==false) {
4 if (el.elements[i].disabled==false) {
5 el.elements[i].checked = checked;
5 el.elements[i].checked = checked;
6 }
6 }
7 }
7 }
8 }
8 }
9
9
10 function addFileField() {
10 function addFileField() {
11 var f = document.createElement("input");
11 var f = document.createElement("input");
12 f.type = "file";
12 f.type = "file";
13 f.name = "attachments[]";
13 f.name = "attachments[]";
14 f.size = 30;
14 f.size = 30;
15
15
16 p = document.getElementById("attachments_p");
16 p = document.getElementById("attachments_p");
17 p.appendChild(document.createElement("br"));
17 p.appendChild(document.createElement("br"));
18 p.appendChild(f);
18 p.appendChild(f);
19 }
19 }
20
20
21 function showTab(name) {
21 function showTab(name) {
22 var f = $$('div#content .tab-content');
22 var f = $$('div#content .tab-content');
23 for(var i=0; i<f.length; i++){
23 for(var i=0; i<f.length; i++){
24 Element.hide(f[i]);
24 Element.hide(f[i]);
25 }
25 }
26 var f = $$('div.tabs a');
26 var f = $$('div.tabs a');
27 for(var i=0; i<f.length; i++){
27 for(var i=0; i<f.length; i++){
28 Element.removeClassName(f[i], "selected");
28 Element.removeClassName(f[i], "selected");
29 }
29 }
30 Element.show('tab-content-' + name);
30 Element.show('tab-content-' + name);
31 Element.addClassName('tab-' + name, "selected");
31 Element.addClassName('tab-' + name, "selected");
32 return false;
32 return false;
33 }
33 }
34
34
35 function setPredecessorFieldsVisibility() {
35 function setPredecessorFieldsVisibility() {
36 relationType = $('relation_relation_type');
36 relationType = $('relation_relation_type');
37 if (relationType && relationType.value == "precedes") {
37 if (relationType && relationType.value == "precedes") {
38 Element.show('predecessor_fields');
38 Element.show('predecessor_fields');
39 } else {
39 } else {
40 Element.hide('predecessor_fields');
40 Element.hide('predecessor_fields');
41 }
41 }
42 }
42 }
43
43
44 function promptToRemote(text, param, url) {
45 value = prompt(text + ':');
46 if (value) {
47 new Ajax.Request(url + '?' + param + '=' + value, {asynchronous:true, evalScripts:true});
48 return false;
49 }
50 }
51
44 /* shows and hides ajax indicator */
52 /* shows and hides ajax indicator */
45 Ajax.Responders.register({
53 Ajax.Responders.register({
46 onCreate: function(){
54 onCreate: function(){
47 if ($('ajax-indicator') && Ajax.activeRequestCount > 0) {
55 if ($('ajax-indicator') && Ajax.activeRequestCount > 0) {
48 Element.show('ajax-indicator');
56 Element.show('ajax-indicator');
49 }
57 }
50 },
58 },
51 onComplete: function(){
59 onComplete: function(){
52 if ($('ajax-indicator') && Ajax.activeRequestCount == 0) {
60 if ($('ajax-indicator') && Ajax.activeRequestCount == 0) {
53 Element.hide('ajax-indicator');
61 Element.hide('ajax-indicator');
54 }
62 }
55 }
63 }
56 });
64 });
General Comments 0
You need to be logged in to leave comments. Login now