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