##// END OF EJS Templates
Removed fragment caching on gantt and calendar....
Jean-Philippe Lang -
r1143:a798a1ac4dc7
parent child
Show More
@@ -1,383 +1,381
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 IssuesController < ApplicationController
18 class IssuesController < ApplicationController
19 layout 'base'
19 layout 'base'
20 menu_item :new_issue, :only => :new
20 menu_item :new_issue, :only => :new
21
21
22 before_filter :find_issue, :only => [:show, :edit, :destroy_attachment]
22 before_filter :find_issue, :only => [:show, :edit, :destroy_attachment]
23 before_filter :find_issues, :only => [:bulk_edit, :move, :destroy]
23 before_filter :find_issues, :only => [:bulk_edit, :move, :destroy]
24 before_filter :find_project, :only => [:new, :update_form, :preview]
24 before_filter :find_project, :only => [:new, :update_form, :preview]
25 before_filter :authorize, :except => [:index, :changes, :preview, :update_form, :context_menu]
25 before_filter :authorize, :except => [:index, :changes, :preview, :update_form, :context_menu]
26 before_filter :find_optional_project, :only => [:index, :changes]
26 before_filter :find_optional_project, :only => [:index, :changes]
27 accept_key_auth :index, :changes
27 accept_key_auth :index, :changes
28
29 cache_sweeper :issue_sweeper, :only => [ :new, :edit, :bulk_edit, :destroy ]
30
28
31 helper :journals
29 helper :journals
32 helper :projects
30 helper :projects
33 include ProjectsHelper
31 include ProjectsHelper
34 helper :custom_fields
32 helper :custom_fields
35 include CustomFieldsHelper
33 include CustomFieldsHelper
36 helper :ifpdf
34 helper :ifpdf
37 include IfpdfHelper
35 include IfpdfHelper
38 helper :issue_relations
36 helper :issue_relations
39 include IssueRelationsHelper
37 include IssueRelationsHelper
40 helper :watchers
38 helper :watchers
41 include WatchersHelper
39 include WatchersHelper
42 helper :attachments
40 helper :attachments
43 include AttachmentsHelper
41 include AttachmentsHelper
44 helper :queries
42 helper :queries
45 helper :sort
43 helper :sort
46 include SortHelper
44 include SortHelper
47 include IssuesHelper
45 include IssuesHelper
48
46
49 def index
47 def index
50 sort_init "#{Issue.table_name}.id", "desc"
48 sort_init "#{Issue.table_name}.id", "desc"
51 sort_update
49 sort_update
52 retrieve_query
50 retrieve_query
53 if @query.valid?
51 if @query.valid?
54 limit = %w(pdf csv).include?(params[:format]) ? Setting.issues_export_limit.to_i : per_page_option
52 limit = %w(pdf csv).include?(params[:format]) ? Setting.issues_export_limit.to_i : per_page_option
55 @issue_count = Issue.count(:include => [:status, :project], :conditions => @query.statement)
53 @issue_count = Issue.count(:include => [:status, :project], :conditions => @query.statement)
56 @issue_pages = Paginator.new self, @issue_count, limit, params['page']
54 @issue_pages = Paginator.new self, @issue_count, limit, params['page']
57 @issues = Issue.find :all, :order => sort_clause,
55 @issues = Issue.find :all, :order => sort_clause,
58 :include => [ :assigned_to, :status, :tracker, :project, :priority, :category, :fixed_version ],
56 :include => [ :assigned_to, :status, :tracker, :project, :priority, :category, :fixed_version ],
59 :conditions => @query.statement,
57 :conditions => @query.statement,
60 :limit => limit,
58 :limit => limit,
61 :offset => @issue_pages.current.offset
59 :offset => @issue_pages.current.offset
62 respond_to do |format|
60 respond_to do |format|
63 format.html { render :template => 'issues/index.rhtml', :layout => !request.xhr? }
61 format.html { render :template => 'issues/index.rhtml', :layout => !request.xhr? }
64 format.atom { render_feed(@issues, :title => l(:label_issue_plural)) }
62 format.atom { render_feed(@issues, :title => l(:label_issue_plural)) }
65 format.csv { send_data(issues_to_csv(@issues, @project).read, :type => 'text/csv; header=present', :filename => 'export.csv') }
63 format.csv { send_data(issues_to_csv(@issues, @project).read, :type => 'text/csv; header=present', :filename => 'export.csv') }
66 format.pdf { send_data(render(:template => 'issues/index.rfpdf', :layout => false), :type => 'application/pdf', :filename => 'export.pdf') }
64 format.pdf { send_data(render(:template => 'issues/index.rfpdf', :layout => false), :type => 'application/pdf', :filename => 'export.pdf') }
67 end
65 end
68 else
66 else
69 # Send html if the query is not valid
67 # Send html if the query is not valid
70 render(:template => 'issues/index.rhtml', :layout => !request.xhr?)
68 render(:template => 'issues/index.rhtml', :layout => !request.xhr?)
71 end
69 end
72 end
70 end
73
71
74 def changes
72 def changes
75 sort_init "#{Issue.table_name}.id", "desc"
73 sort_init "#{Issue.table_name}.id", "desc"
76 sort_update
74 sort_update
77 retrieve_query
75 retrieve_query
78 if @query.valid?
76 if @query.valid?
79 @changes = Journal.find :all, :include => [ :details, :user, {:issue => [:project, :author, :tracker, :status]} ],
77 @changes = Journal.find :all, :include => [ :details, :user, {:issue => [:project, :author, :tracker, :status]} ],
80 :conditions => @query.statement,
78 :conditions => @query.statement,
81 :limit => 25,
79 :limit => 25,
82 :order => "#{Journal.table_name}.created_on DESC"
80 :order => "#{Journal.table_name}.created_on DESC"
83 end
81 end
84 @title = (@project ? @project.name : Setting.app_title) + ": " + (@query.new_record? ? l(:label_changes_details) : @query.name)
82 @title = (@project ? @project.name : Setting.app_title) + ": " + (@query.new_record? ? l(:label_changes_details) : @query.name)
85 render :layout => false, :content_type => 'application/atom+xml'
83 render :layout => false, :content_type => 'application/atom+xml'
86 end
84 end
87
85
88 def show
86 def show
89 @custom_values = @project.custom_fields_for_issues(@issue.tracker).collect { |x| @issue.custom_values.find_by_custom_field_id(x.id) || CustomValue.new(:custom_field => x, :customized => @issue) }
87 @custom_values = @project.custom_fields_for_issues(@issue.tracker).collect { |x| @issue.custom_values.find_by_custom_field_id(x.id) || CustomValue.new(:custom_field => x, :customized => @issue) }
90 @journals = @issue.journals.find(:all, :include => [:user, :details], :order => "#{Journal.table_name}.created_on ASC")
88 @journals = @issue.journals.find(:all, :include => [:user, :details], :order => "#{Journal.table_name}.created_on ASC")
91 @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
89 @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
92 @edit_allowed = User.current.allowed_to?(:edit_issues, @project)
90 @edit_allowed = User.current.allowed_to?(:edit_issues, @project)
93 @activities = Enumeration::get_values('ACTI')
91 @activities = Enumeration::get_values('ACTI')
94 @priorities = Enumeration::get_values('IPRI')
92 @priorities = Enumeration::get_values('IPRI')
95 respond_to do |format|
93 respond_to do |format|
96 format.html { render :template => 'issues/show.rhtml' }
94 format.html { render :template => 'issues/show.rhtml' }
97 format.pdf { send_data(render(:template => 'issues/show.rfpdf', :layout => false), :type => 'application/pdf', :filename => "#{@project.identifier}-#{@issue.id}.pdf") }
95 format.pdf { send_data(render(:template => 'issues/show.rfpdf', :layout => false), :type => 'application/pdf', :filename => "#{@project.identifier}-#{@issue.id}.pdf") }
98 end
96 end
99 end
97 end
100
98
101 # Add a new issue
99 # Add a new issue
102 # The new issue will be created from an existing one if copy_from parameter is given
100 # The new issue will be created from an existing one if copy_from parameter is given
103 def new
101 def new
104 @issue = params[:copy_from] ? Issue.new.copy_from(params[:copy_from]) : Issue.new(params[:issue])
102 @issue = params[:copy_from] ? Issue.new.copy_from(params[:copy_from]) : Issue.new(params[:issue])
105 @issue.project = @project
103 @issue.project = @project
106 @issue.author = User.current
104 @issue.author = User.current
107 @issue.tracker ||= @project.trackers.find(params[:tracker_id] ? params[:tracker_id] : :first)
105 @issue.tracker ||= @project.trackers.find(params[:tracker_id] ? params[:tracker_id] : :first)
108 if @issue.tracker.nil?
106 if @issue.tracker.nil?
109 flash.now[:error] = 'No tracker is associated to this project. Please check the Project settings.'
107 flash.now[:error] = 'No tracker is associated to this project. Please check the Project settings.'
110 render :nothing => true, :layout => true
108 render :nothing => true, :layout => true
111 return
109 return
112 end
110 end
113
111
114 default_status = IssueStatus.default
112 default_status = IssueStatus.default
115 unless default_status
113 unless default_status
116 flash.now[:error] = 'No default issue status is defined. Please check your configuration (Go to "Administration -> Issue statuses").'
114 flash.now[:error] = 'No default issue status is defined. Please check your configuration (Go to "Administration -> Issue statuses").'
117 render :nothing => true, :layout => true
115 render :nothing => true, :layout => true
118 return
116 return
119 end
117 end
120 @issue.status = default_status
118 @issue.status = default_status
121 @allowed_statuses = ([default_status] + default_status.find_new_statuses_allowed_to(User.current.role_for_project(@project), @issue.tracker))
119 @allowed_statuses = ([default_status] + default_status.find_new_statuses_allowed_to(User.current.role_for_project(@project), @issue.tracker))
122
120
123 if request.get? || request.xhr?
121 if request.get? || request.xhr?
124 @issue.start_date ||= Date.today
122 @issue.start_date ||= Date.today
125 @custom_values = @issue.custom_values.empty? ?
123 @custom_values = @issue.custom_values.empty? ?
126 @project.custom_fields_for_issues(@issue.tracker).collect { |x| CustomValue.new(:custom_field => x, :customized => @issue) } :
124 @project.custom_fields_for_issues(@issue.tracker).collect { |x| CustomValue.new(:custom_field => x, :customized => @issue) } :
127 @issue.custom_values
125 @issue.custom_values
128 else
126 else
129 requested_status = IssueStatus.find_by_id(params[:issue][:status_id])
127 requested_status = IssueStatus.find_by_id(params[:issue][:status_id])
130 # Check that the user is allowed to apply the requested status
128 # Check that the user is allowed to apply the requested status
131 @issue.status = (@allowed_statuses.include? requested_status) ? requested_status : default_status
129 @issue.status = (@allowed_statuses.include? requested_status) ? requested_status : default_status
132 @custom_values = @project.custom_fields_for_issues(@issue.tracker).collect { |x| CustomValue.new(:custom_field => x, :customized => @issue, :value => params["custom_fields"][x.id.to_s]) }
130 @custom_values = @project.custom_fields_for_issues(@issue.tracker).collect { |x| CustomValue.new(:custom_field => x, :customized => @issue, :value => params["custom_fields"][x.id.to_s]) }
133 @issue.custom_values = @custom_values
131 @issue.custom_values = @custom_values
134 if @issue.save
132 if @issue.save
135 attach_files(@issue, params[:attachments])
133 attach_files(@issue, params[:attachments])
136 flash[:notice] = l(:notice_successful_create)
134 flash[:notice] = l(:notice_successful_create)
137 Mailer.deliver_issue_add(@issue) if Setting.notified_events.include?('issue_added')
135 Mailer.deliver_issue_add(@issue) if Setting.notified_events.include?('issue_added')
138 redirect_to :controller => 'issues', :action => 'index', :project_id => @project
136 redirect_to :controller => 'issues', :action => 'index', :project_id => @project
139 return
137 return
140 end
138 end
141 end
139 end
142 @priorities = Enumeration::get_values('IPRI')
140 @priorities = Enumeration::get_values('IPRI')
143 render :layout => !request.xhr?
141 render :layout => !request.xhr?
144 end
142 end
145
143
146 # Attributes that can be updated on workflow transition (without :edit permission)
144 # Attributes that can be updated on workflow transition (without :edit permission)
147 # TODO: make it configurable (at least per role)
145 # TODO: make it configurable (at least per role)
148 UPDATABLE_ATTRS_ON_TRANSITION = %w(status_id assigned_to_id fixed_version_id done_ratio) unless const_defined?(:UPDATABLE_ATTRS_ON_TRANSITION)
146 UPDATABLE_ATTRS_ON_TRANSITION = %w(status_id assigned_to_id fixed_version_id done_ratio) unless const_defined?(:UPDATABLE_ATTRS_ON_TRANSITION)
149
147
150 def edit
148 def edit
151 @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
149 @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
152 @activities = Enumeration::get_values('ACTI')
150 @activities = Enumeration::get_values('ACTI')
153 @priorities = Enumeration::get_values('IPRI')
151 @priorities = Enumeration::get_values('IPRI')
154 @custom_values = []
152 @custom_values = []
155 @edit_allowed = User.current.allowed_to?(:edit_issues, @project)
153 @edit_allowed = User.current.allowed_to?(:edit_issues, @project)
156
154
157 @notes = params[:notes]
155 @notes = params[:notes]
158 journal = @issue.init_journal(User.current, @notes)
156 journal = @issue.init_journal(User.current, @notes)
159 # User can change issue attributes only if he has :edit permission or if a workflow transition is allowed
157 # User can change issue attributes only if he has :edit permission or if a workflow transition is allowed
160 if (@edit_allowed || !@allowed_statuses.empty?) && params[:issue]
158 if (@edit_allowed || !@allowed_statuses.empty?) && params[:issue]
161 attrs = params[:issue].dup
159 attrs = params[:issue].dup
162 attrs.delete_if {|k,v| !UPDATABLE_ATTRS_ON_TRANSITION.include?(k) } unless @edit_allowed
160 attrs.delete_if {|k,v| !UPDATABLE_ATTRS_ON_TRANSITION.include?(k) } unless @edit_allowed
163 attrs.delete(:status_id) unless @allowed_statuses.detect {|s| s.id.to_s == attrs[:status_id].to_s}
161 attrs.delete(:status_id) unless @allowed_statuses.detect {|s| s.id.to_s == attrs[:status_id].to_s}
164 @issue.attributes = attrs
162 @issue.attributes = attrs
165 end
163 end
166
164
167 if request.get?
165 if request.get?
168 @custom_values = @project.custom_fields_for_issues(@issue.tracker).collect { |x| @issue.custom_values.find_by_custom_field_id(x.id) || CustomValue.new(:custom_field => x, :customized => @issue) }
166 @custom_values = @project.custom_fields_for_issues(@issue.tracker).collect { |x| @issue.custom_values.find_by_custom_field_id(x.id) || CustomValue.new(:custom_field => x, :customized => @issue) }
169 else
167 else
170 # Update custom fields if user has :edit permission
168 # Update custom fields if user has :edit permission
171 if @edit_allowed && params[:custom_fields]
169 if @edit_allowed && params[:custom_fields]
172 @custom_values = @project.custom_fields_for_issues(@issue.tracker).collect { |x| CustomValue.new(:custom_field => x, :customized => @issue, :value => params["custom_fields"][x.id.to_s]) }
170 @custom_values = @project.custom_fields_for_issues(@issue.tracker).collect { |x| CustomValue.new(:custom_field => x, :customized => @issue, :value => params["custom_fields"][x.id.to_s]) }
173 @issue.custom_values = @custom_values
171 @issue.custom_values = @custom_values
174 end
172 end
175 attachments = attach_files(@issue, params[:attachments])
173 attachments = attach_files(@issue, params[:attachments])
176 attachments.each {|a| journal.details << JournalDetail.new(:property => 'attachment', :prop_key => a.id, :value => a.filename)}
174 attachments.each {|a| journal.details << JournalDetail.new(:property => 'attachment', :prop_key => a.id, :value => a.filename)}
177 if @issue.save
175 if @issue.save
178 # Log spend time
176 # Log spend time
179 if current_role.allowed_to?(:log_time)
177 if current_role.allowed_to?(:log_time)
180 @time_entry = TimeEntry.new(:project => @project, :issue => @issue, :user => User.current, :spent_on => Date.today)
178 @time_entry = TimeEntry.new(:project => @project, :issue => @issue, :user => User.current, :spent_on => Date.today)
181 @time_entry.attributes = params[:time_entry]
179 @time_entry.attributes = params[:time_entry]
182 @time_entry.save
180 @time_entry.save
183 end
181 end
184 if !journal.new_record?
182 if !journal.new_record?
185 # Only send notification if something was actually changed
183 # Only send notification if something was actually changed
186 flash[:notice] = l(:notice_successful_update)
184 flash[:notice] = l(:notice_successful_update)
187 Mailer.deliver_issue_edit(journal) if Setting.notified_events.include?('issue_updated')
185 Mailer.deliver_issue_edit(journal) if Setting.notified_events.include?('issue_updated')
188 end
186 end
189 redirect_to(params[:back_to] || {:action => 'show', :id => @issue})
187 redirect_to(params[:back_to] || {:action => 'show', :id => @issue})
190 end
188 end
191 end
189 end
192 rescue ActiveRecord::StaleObjectError
190 rescue ActiveRecord::StaleObjectError
193 # Optimistic locking exception
191 # Optimistic locking exception
194 flash.now[:error] = l(:notice_locking_conflict)
192 flash.now[:error] = l(:notice_locking_conflict)
195 end
193 end
196
194
197 # Bulk edit a set of issues
195 # Bulk edit a set of issues
198 def bulk_edit
196 def bulk_edit
199 if request.post?
197 if request.post?
200 status = params[:status_id].blank? ? nil : IssueStatus.find_by_id(params[:status_id])
198 status = params[:status_id].blank? ? nil : IssueStatus.find_by_id(params[:status_id])
201 priority = params[:priority_id].blank? ? nil : Enumeration.find_by_id(params[:priority_id])
199 priority = params[:priority_id].blank? ? nil : Enumeration.find_by_id(params[:priority_id])
202 assigned_to = params[:assigned_to_id].blank? ? nil : User.find_by_id(params[:assigned_to_id])
200 assigned_to = params[:assigned_to_id].blank? ? nil : User.find_by_id(params[:assigned_to_id])
203 category = params[:category_id].blank? ? nil : @project.issue_categories.find_by_id(params[:category_id])
201 category = params[:category_id].blank? ? nil : @project.issue_categories.find_by_id(params[:category_id])
204 fixed_version = params[:fixed_version_id].blank? ? nil : @project.versions.find_by_id(params[:fixed_version_id])
202 fixed_version = params[:fixed_version_id].blank? ? nil : @project.versions.find_by_id(params[:fixed_version_id])
205
203
206 unsaved_issue_ids = []
204 unsaved_issue_ids = []
207 @issues.each do |issue|
205 @issues.each do |issue|
208 journal = issue.init_journal(User.current, params[:notes])
206 journal = issue.init_journal(User.current, params[:notes])
209 issue.priority = priority if priority
207 issue.priority = priority if priority
210 issue.assigned_to = assigned_to if assigned_to || params[:assigned_to_id] == 'none'
208 issue.assigned_to = assigned_to if assigned_to || params[:assigned_to_id] == 'none'
211 issue.category = category if category
209 issue.category = category if category
212 issue.fixed_version = fixed_version if fixed_version
210 issue.fixed_version = fixed_version if fixed_version
213 issue.start_date = params[:start_date] unless params[:start_date].blank?
211 issue.start_date = params[:start_date] unless params[:start_date].blank?
214 issue.due_date = params[:due_date] unless params[:due_date].blank?
212 issue.due_date = params[:due_date] unless params[:due_date].blank?
215 issue.done_ratio = params[:done_ratio] unless params[:done_ratio].blank?
213 issue.done_ratio = params[:done_ratio] unless params[:done_ratio].blank?
216 # Don't save any change to the issue if the user is not authorized to apply the requested status
214 # Don't save any change to the issue if the user is not authorized to apply the requested status
217 if (status.nil? || (issue.status.new_status_allowed_to?(status, current_role, issue.tracker) && issue.status = status)) && issue.save
215 if (status.nil? || (issue.status.new_status_allowed_to?(status, current_role, issue.tracker) && issue.status = status)) && issue.save
218 # Send notification for each issue (if changed)
216 # Send notification for each issue (if changed)
219 Mailer.deliver_issue_edit(journal) if journal.details.any? && Setting.notified_events.include?('issue_updated')
217 Mailer.deliver_issue_edit(journal) if journal.details.any? && Setting.notified_events.include?('issue_updated')
220 else
218 else
221 # Keep unsaved issue ids to display them in flash error
219 # Keep unsaved issue ids to display them in flash error
222 unsaved_issue_ids << issue.id
220 unsaved_issue_ids << issue.id
223 end
221 end
224 end
222 end
225 if unsaved_issue_ids.empty?
223 if unsaved_issue_ids.empty?
226 flash[:notice] = l(:notice_successful_update) unless @issues.empty?
224 flash[:notice] = l(:notice_successful_update) unless @issues.empty?
227 else
225 else
228 flash[:error] = l(:notice_failed_to_save_issues, unsaved_issue_ids.size, @issues.size, '#' + unsaved_issue_ids.join(', #'))
226 flash[:error] = l(:notice_failed_to_save_issues, unsaved_issue_ids.size, @issues.size, '#' + unsaved_issue_ids.join(', #'))
229 end
227 end
230 redirect_to :controller => 'issues', :action => 'index', :project_id => @project
228 redirect_to :controller => 'issues', :action => 'index', :project_id => @project
231 return
229 return
232 end
230 end
233 # Find potential statuses the user could be allowed to switch issues to
231 # Find potential statuses the user could be allowed to switch issues to
234 @available_statuses = Workflow.find(:all, :include => :new_status,
232 @available_statuses = Workflow.find(:all, :include => :new_status,
235 :conditions => {:role_id => current_role.id}).collect(&:new_status).compact.uniq
233 :conditions => {:role_id => current_role.id}).collect(&:new_status).compact.uniq
236 end
234 end
237
235
238 def move
236 def move
239 @allowed_projects = []
237 @allowed_projects = []
240 # find projects to which the user is allowed to move the issue
238 # find projects to which the user is allowed to move the issue
241 if User.current.admin?
239 if User.current.admin?
242 # admin is allowed to move issues to any active (visible) project
240 # admin is allowed to move issues to any active (visible) project
243 @allowed_projects = Project.find(:all, :conditions => Project.visible_by(User.current), :order => 'name')
241 @allowed_projects = Project.find(:all, :conditions => Project.visible_by(User.current), :order => 'name')
244 else
242 else
245 User.current.memberships.each {|m| @allowed_projects << m.project if m.role.allowed_to?(:move_issues)}
243 User.current.memberships.each {|m| @allowed_projects << m.project if m.role.allowed_to?(:move_issues)}
246 end
244 end
247 @target_project = @allowed_projects.detect {|p| p.id.to_s == params[:new_project_id]} if params[:new_project_id]
245 @target_project = @allowed_projects.detect {|p| p.id.to_s == params[:new_project_id]} if params[:new_project_id]
248 @target_project ||= @project
246 @target_project ||= @project
249 @trackers = @target_project.trackers
247 @trackers = @target_project.trackers
250 if request.post?
248 if request.post?
251 new_tracker = params[:new_tracker_id].blank? ? nil : @target_project.trackers.find_by_id(params[:new_tracker_id])
249 new_tracker = params[:new_tracker_id].blank? ? nil : @target_project.trackers.find_by_id(params[:new_tracker_id])
252 unsaved_issue_ids = []
250 unsaved_issue_ids = []
253 @issues.each do |issue|
251 @issues.each do |issue|
254 unsaved_issue_ids << issue.id unless issue.move_to(@target_project, new_tracker)
252 unsaved_issue_ids << issue.id unless issue.move_to(@target_project, new_tracker)
255 end
253 end
256 if unsaved_issue_ids.empty?
254 if unsaved_issue_ids.empty?
257 flash[:notice] = l(:notice_successful_update) unless @issues.empty?
255 flash[:notice] = l(:notice_successful_update) unless @issues.empty?
258 else
256 else
259 flash[:error] = l(:notice_failed_to_save_issues, unsaved_issue_ids.size, @issues.size, '#' + unsaved_issue_ids.join(', #'))
257 flash[:error] = l(:notice_failed_to_save_issues, unsaved_issue_ids.size, @issues.size, '#' + unsaved_issue_ids.join(', #'))
260 end
258 end
261 redirect_to :controller => 'issues', :action => 'index', :project_id => @project
259 redirect_to :controller => 'issues', :action => 'index', :project_id => @project
262 return
260 return
263 end
261 end
264 render :layout => false if request.xhr?
262 render :layout => false if request.xhr?
265 end
263 end
266
264
267 def destroy
265 def destroy
268 @issues.each(&:destroy)
266 @issues.each(&:destroy)
269 redirect_to :action => 'index', :project_id => @project
267 redirect_to :action => 'index', :project_id => @project
270 end
268 end
271
269
272 def destroy_attachment
270 def destroy_attachment
273 a = @issue.attachments.find(params[:attachment_id])
271 a = @issue.attachments.find(params[:attachment_id])
274 a.destroy
272 a.destroy
275 journal = @issue.init_journal(User.current)
273 journal = @issue.init_journal(User.current)
276 journal.details << JournalDetail.new(:property => 'attachment',
274 journal.details << JournalDetail.new(:property => 'attachment',
277 :prop_key => a.id,
275 :prop_key => a.id,
278 :old_value => a.filename)
276 :old_value => a.filename)
279 journal.save
277 journal.save
280 redirect_to :action => 'show', :id => @issue
278 redirect_to :action => 'show', :id => @issue
281 end
279 end
282
280
283 def context_menu
281 def context_menu
284 @issues = Issue.find_all_by_id(params[:ids], :include => :project)
282 @issues = Issue.find_all_by_id(params[:ids], :include => :project)
285 if (@issues.size == 1)
283 if (@issues.size == 1)
286 @issue = @issues.first
284 @issue = @issues.first
287 @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
285 @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
288 @assignables = @issue.assignable_users
286 @assignables = @issue.assignable_users
289 @assignables << @issue.assigned_to if @issue.assigned_to && !@assignables.include?(@issue.assigned_to)
287 @assignables << @issue.assigned_to if @issue.assigned_to && !@assignables.include?(@issue.assigned_to)
290 end
288 end
291 projects = @issues.collect(&:project).compact.uniq
289 projects = @issues.collect(&:project).compact.uniq
292 @project = projects.first if projects.size == 1
290 @project = projects.first if projects.size == 1
293
291
294 @can = {:edit => (@project && User.current.allowed_to?(:edit_issues, @project)),
292 @can = {:edit => (@project && User.current.allowed_to?(:edit_issues, @project)),
295 :update => (@issue && (User.current.allowed_to?(:edit_issues, @project) || (User.current.allowed_to?(:change_status, @project) && !@allowed_statuses.empty?))),
293 :update => (@issue && (User.current.allowed_to?(:edit_issues, @project) || (User.current.allowed_to?(:change_status, @project) && !@allowed_statuses.empty?))),
296 :move => (@project && User.current.allowed_to?(:move_issues, @project)),
294 :move => (@project && User.current.allowed_to?(:move_issues, @project)),
297 :copy => (@issue && @project.trackers.include?(@issue.tracker) && User.current.allowed_to?(:add_issues, @project)),
295 :copy => (@issue && @project.trackers.include?(@issue.tracker) && User.current.allowed_to?(:add_issues, @project)),
298 :delete => (@project && User.current.allowed_to?(:delete_issues, @project))
296 :delete => (@project && User.current.allowed_to?(:delete_issues, @project))
299 }
297 }
300
298
301 @priorities = Enumeration.get_values('IPRI').reverse
299 @priorities = Enumeration.get_values('IPRI').reverse
302 @statuses = IssueStatus.find(:all, :order => 'position')
300 @statuses = IssueStatus.find(:all, :order => 'position')
303 @back = request.env['HTTP_REFERER']
301 @back = request.env['HTTP_REFERER']
304
302
305 render :layout => false
303 render :layout => false
306 end
304 end
307
305
308 def update_form
306 def update_form
309 @issue = Issue.new(params[:issue])
307 @issue = Issue.new(params[:issue])
310 render :action => :new, :layout => false
308 render :action => :new, :layout => false
311 end
309 end
312
310
313 def preview
311 def preview
314 issue = @project.issues.find_by_id(params[:id])
312 issue = @project.issues.find_by_id(params[:id])
315 @attachements = issue.attachments if issue
313 @attachements = issue.attachments if issue
316 @text = params[:notes] || (params[:issue] ? params[:issue][:description] : nil)
314 @text = params[:notes] || (params[:issue] ? params[:issue][:description] : nil)
317 render :partial => 'common/preview'
315 render :partial => 'common/preview'
318 end
316 end
319
317
320 private
318 private
321 def find_issue
319 def find_issue
322 @issue = Issue.find(params[:id], :include => [:project, :tracker, :status, :author, :priority, :category])
320 @issue = Issue.find(params[:id], :include => [:project, :tracker, :status, :author, :priority, :category])
323 @project = @issue.project
321 @project = @issue.project
324 rescue ActiveRecord::RecordNotFound
322 rescue ActiveRecord::RecordNotFound
325 render_404
323 render_404
326 end
324 end
327
325
328 # Filter for bulk operations
326 # Filter for bulk operations
329 def find_issues
327 def find_issues
330 @issues = Issue.find_all_by_id(params[:id] || params[:ids])
328 @issues = Issue.find_all_by_id(params[:id] || params[:ids])
331 raise ActiveRecord::RecordNotFound if @issues.empty?
329 raise ActiveRecord::RecordNotFound if @issues.empty?
332 projects = @issues.collect(&:project).compact.uniq
330 projects = @issues.collect(&:project).compact.uniq
333 if projects.size == 1
331 if projects.size == 1
334 @project = projects.first
332 @project = projects.first
335 else
333 else
336 # TODO: let users bulk edit/move/destroy issues from different projects
334 # TODO: let users bulk edit/move/destroy issues from different projects
337 render_error 'Can not bulk edit/move/destroy issues from different projects' and return false
335 render_error 'Can not bulk edit/move/destroy issues from different projects' and return false
338 end
336 end
339 rescue ActiveRecord::RecordNotFound
337 rescue ActiveRecord::RecordNotFound
340 render_404
338 render_404
341 end
339 end
342
340
343 def find_project
341 def find_project
344 @project = Project.find(params[:project_id])
342 @project = Project.find(params[:project_id])
345 rescue ActiveRecord::RecordNotFound
343 rescue ActiveRecord::RecordNotFound
346 render_404
344 render_404
347 end
345 end
348
346
349 def find_optional_project
347 def find_optional_project
350 return true unless params[:project_id]
348 return true unless params[:project_id]
351 @project = Project.find(params[:project_id])
349 @project = Project.find(params[:project_id])
352 authorize
350 authorize
353 rescue ActiveRecord::RecordNotFound
351 rescue ActiveRecord::RecordNotFound
354 render_404
352 render_404
355 end
353 end
356
354
357 # Retrieve query from session or build a new query
355 # Retrieve query from session or build a new query
358 def retrieve_query
356 def retrieve_query
359 if !params[:query_id].blank?
357 if !params[:query_id].blank?
360 @query = Query.find(params[:query_id], :conditions => {:project_id => (@project ? @project.id : nil)})
358 @query = Query.find(params[:query_id], :conditions => {:project_id => (@project ? @project.id : nil)})
361 session[:query] = {:id => @query.id, :project_id => @query.project_id}
359 session[:query] = {:id => @query.id, :project_id => @query.project_id}
362 else
360 else
363 if params[:set_filter] || session[:query].nil? || session[:query][:project_id] != (@project ? @project.id : nil)
361 if params[:set_filter] || session[:query].nil? || session[:query][:project_id] != (@project ? @project.id : nil)
364 # Give it a name, required to be valid
362 # Give it a name, required to be valid
365 @query = Query.new(:name => "_")
363 @query = Query.new(:name => "_")
366 @query.project = @project
364 @query.project = @project
367 if params[:fields] and params[:fields].is_a? Array
365 if params[:fields] and params[:fields].is_a? Array
368 params[:fields].each do |field|
366 params[:fields].each do |field|
369 @query.add_filter(field, params[:operators][field], params[:values][field])
367 @query.add_filter(field, params[:operators][field], params[:values][field])
370 end
368 end
371 else
369 else
372 @query.available_filters.keys.each do |field|
370 @query.available_filters.keys.each do |field|
373 @query.add_short_filter(field, params[field]) if params[field]
371 @query.add_short_filter(field, params[field]) if params[field]
374 end
372 end
375 end
373 end
376 session[:query] = {:project_id => @query.project_id, :filters => @query.filters}
374 session[:query] = {:project_id => @query.project_id, :filters => @query.filters}
377 else
375 else
378 @query = Query.find_by_id(session[:query][:id]) if session[:query][:id]
376 @query = Query.find_by_id(session[:query][:id]) if session[:query][:id]
379 @query ||= Query.new(:name => "_", :project => @project, :filters => session[:query][:filters])
377 @query ||= Query.new(:name => "_", :project => @project, :filters => session[:query][:filters])
380 end
378 end
381 end
379 end
382 end
380 end
383 end
381 end
@@ -1,394 +1,391
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 ProjectsController < ApplicationController
18 class ProjectsController < ApplicationController
19 layout 'base'
19 layout 'base'
20 menu_item :overview
20 menu_item :overview
21 menu_item :activity, :only => :activity
21 menu_item :activity, :only => :activity
22 menu_item :roadmap, :only => :roadmap
22 menu_item :roadmap, :only => :roadmap
23 menu_item :files, :only => [:list_files, :add_file]
23 menu_item :files, :only => [:list_files, :add_file]
24 menu_item :settings, :only => :settings
24 menu_item :settings, :only => :settings
25 menu_item :issues, :only => [:changelog]
25 menu_item :issues, :only => [:changelog]
26
26
27 before_filter :find_project, :except => [ :index, :list, :add ]
27 before_filter :find_project, :except => [ :index, :list, :add ]
28 before_filter :authorize, :except => [ :index, :list, :add, :archive, :unarchive, :destroy ]
28 before_filter :authorize, :except => [ :index, :list, :add, :archive, :unarchive, :destroy ]
29 before_filter :require_admin, :only => [ :add, :archive, :unarchive, :destroy ]
29 before_filter :require_admin, :only => [ :add, :archive, :unarchive, :destroy ]
30 accept_key_auth :activity, :calendar
30 accept_key_auth :activity, :calendar
31
31
32 cache_sweeper :project_sweeper, :only => [ :add, :edit, :archive, :unarchive, :destroy ]
33 cache_sweeper :version_sweeper, :only => [ :add_version ]
34
35 helper :sort
32 helper :sort
36 include SortHelper
33 include SortHelper
37 helper :custom_fields
34 helper :custom_fields
38 include CustomFieldsHelper
35 include CustomFieldsHelper
39 helper :ifpdf
36 helper :ifpdf
40 include IfpdfHelper
37 include IfpdfHelper
41 helper :issues
38 helper :issues
42 helper IssuesHelper
39 helper IssuesHelper
43 helper :queries
40 helper :queries
44 include QueriesHelper
41 include QueriesHelper
45 helper :repositories
42 helper :repositories
46 include RepositoriesHelper
43 include RepositoriesHelper
47 include ProjectsHelper
44 include ProjectsHelper
48
45
49 def index
46 def index
50 list
47 list
51 render :action => 'list' unless request.xhr?
48 render :action => 'list' unless request.xhr?
52 end
49 end
53
50
54 # Lists visible projects
51 # Lists visible projects
55 def list
52 def list
56 projects = Project.find :all,
53 projects = Project.find :all,
57 :conditions => Project.visible_by(User.current),
54 :conditions => Project.visible_by(User.current),
58 :include => :parent
55 :include => :parent
59 @project_tree = projects.group_by {|p| p.parent || p}
56 @project_tree = projects.group_by {|p| p.parent || p}
60 @project_tree.each_key {|p| @project_tree[p] -= [p]}
57 @project_tree.each_key {|p| @project_tree[p] -= [p]}
61 end
58 end
62
59
63 # Add a new project
60 # Add a new project
64 def add
61 def add
65 @custom_fields = IssueCustomField.find(:all, :order => "#{CustomField.table_name}.position")
62 @custom_fields = IssueCustomField.find(:all, :order => "#{CustomField.table_name}.position")
66 @trackers = Tracker.all
63 @trackers = Tracker.all
67 @root_projects = Project.find(:all,
64 @root_projects = Project.find(:all,
68 :conditions => "parent_id IS NULL AND status = #{Project::STATUS_ACTIVE}",
65 :conditions => "parent_id IS NULL AND status = #{Project::STATUS_ACTIVE}",
69 :order => 'name')
66 :order => 'name')
70 @project = Project.new(params[:project])
67 @project = Project.new(params[:project])
71 @project.enabled_module_names = Redmine::AccessControl.available_project_modules
68 @project.enabled_module_names = Redmine::AccessControl.available_project_modules
72 if request.get?
69 if request.get?
73 @custom_values = ProjectCustomField.find(:all, :order => "#{CustomField.table_name}.position").collect { |x| CustomValue.new(:custom_field => x, :customized => @project) }
70 @custom_values = ProjectCustomField.find(:all, :order => "#{CustomField.table_name}.position").collect { |x| CustomValue.new(:custom_field => x, :customized => @project) }
74 @project.trackers = Tracker.all
71 @project.trackers = Tracker.all
75 else
72 else
76 @project.custom_fields = CustomField.find(params[:custom_field_ids]) if params[:custom_field_ids]
73 @project.custom_fields = CustomField.find(params[:custom_field_ids]) if params[:custom_field_ids]
77 @custom_values = ProjectCustomField.find(:all, :order => "#{CustomField.table_name}.position").collect { |x| CustomValue.new(:custom_field => x, :customized => @project, :value => (params[:custom_fields] ? params["custom_fields"][x.id.to_s] : nil)) }
74 @custom_values = ProjectCustomField.find(:all, :order => "#{CustomField.table_name}.position").collect { |x| CustomValue.new(:custom_field => x, :customized => @project, :value => (params[:custom_fields] ? params["custom_fields"][x.id.to_s] : nil)) }
78 @project.custom_values = @custom_values
75 @project.custom_values = @custom_values
79 if @project.save
76 if @project.save
80 @project.enabled_module_names = params[:enabled_modules]
77 @project.enabled_module_names = params[:enabled_modules]
81 flash[:notice] = l(:notice_successful_create)
78 flash[:notice] = l(:notice_successful_create)
82 redirect_to :controller => 'admin', :action => 'projects'
79 redirect_to :controller => 'admin', :action => 'projects'
83 end
80 end
84 end
81 end
85 end
82 end
86
83
87 # Show @project
84 # Show @project
88 def show
85 def show
89 @custom_values = @project.custom_values.find(:all, :include => :custom_field, :order => "#{CustomField.table_name}.position")
86 @custom_values = @project.custom_values.find(:all, :include => :custom_field, :order => "#{CustomField.table_name}.position")
90 @members_by_role = @project.members.find(:all, :include => [:user, :role], :order => 'position').group_by {|m| m.role}
87 @members_by_role = @project.members.find(:all, :include => [:user, :role], :order => 'position').group_by {|m| m.role}
91 @subprojects = @project.active_children
88 @subprojects = @project.active_children
92 @news = @project.news.find(:all, :limit => 5, :include => [ :author, :project ], :order => "#{News.table_name}.created_on DESC")
89 @news = @project.news.find(:all, :limit => 5, :include => [ :author, :project ], :order => "#{News.table_name}.created_on DESC")
93 @trackers = @project.trackers
90 @trackers = @project.trackers
94 @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])
91 @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])
95 @total_issues_by_tracker = Issue.count(:group => :tracker, :conditions => ["project_id=?", @project.id])
92 @total_issues_by_tracker = Issue.count(:group => :tracker, :conditions => ["project_id=?", @project.id])
96 @total_hours = @project.time_entries.sum(:hours)
93 @total_hours = @project.time_entries.sum(:hours)
97 @key = User.current.rss_key
94 @key = User.current.rss_key
98 end
95 end
99
96
100 def settings
97 def settings
101 @root_projects = Project.find(:all,
98 @root_projects = Project.find(:all,
102 :conditions => ["parent_id IS NULL AND status = #{Project::STATUS_ACTIVE} AND id <> ?", @project.id],
99 :conditions => ["parent_id IS NULL AND status = #{Project::STATUS_ACTIVE} AND id <> ?", @project.id],
103 :order => 'name')
100 :order => 'name')
104 @custom_fields = IssueCustomField.find(:all)
101 @custom_fields = IssueCustomField.find(:all)
105 @issue_category ||= IssueCategory.new
102 @issue_category ||= IssueCategory.new
106 @member ||= @project.members.new
103 @member ||= @project.members.new
107 @trackers = Tracker.all
104 @trackers = Tracker.all
108 @custom_values ||= ProjectCustomField.find(:all, :order => "#{CustomField.table_name}.position").collect { |x| @project.custom_values.find_by_custom_field_id(x.id) || CustomValue.new(:custom_field => x) }
105 @custom_values ||= ProjectCustomField.find(:all, :order => "#{CustomField.table_name}.position").collect { |x| @project.custom_values.find_by_custom_field_id(x.id) || CustomValue.new(:custom_field => x) }
109 @repository ||= @project.repository
106 @repository ||= @project.repository
110 @wiki ||= @project.wiki
107 @wiki ||= @project.wiki
111 end
108 end
112
109
113 # Edit @project
110 # Edit @project
114 def edit
111 def edit
115 if request.post?
112 if request.post?
116 if params[:custom_fields]
113 if params[:custom_fields]
117 @custom_values = ProjectCustomField.find(:all, :order => "#{CustomField.table_name}.position").collect { |x| CustomValue.new(:custom_field => x, :customized => @project, :value => params["custom_fields"][x.id.to_s]) }
114 @custom_values = ProjectCustomField.find(:all, :order => "#{CustomField.table_name}.position").collect { |x| CustomValue.new(:custom_field => x, :customized => @project, :value => params["custom_fields"][x.id.to_s]) }
118 @project.custom_values = @custom_values
115 @project.custom_values = @custom_values
119 end
116 end
120 @project.attributes = params[:project]
117 @project.attributes = params[:project]
121 if @project.save
118 if @project.save
122 flash[:notice] = l(:notice_successful_update)
119 flash[:notice] = l(:notice_successful_update)
123 redirect_to :action => 'settings', :id => @project
120 redirect_to :action => 'settings', :id => @project
124 else
121 else
125 settings
122 settings
126 render :action => 'settings'
123 render :action => 'settings'
127 end
124 end
128 end
125 end
129 end
126 end
130
127
131 def modules
128 def modules
132 @project.enabled_module_names = params[:enabled_modules]
129 @project.enabled_module_names = params[:enabled_modules]
133 redirect_to :action => 'settings', :id => @project, :tab => 'modules'
130 redirect_to :action => 'settings', :id => @project, :tab => 'modules'
134 end
131 end
135
132
136 def archive
133 def archive
137 @project.archive if request.post? && @project.active?
134 @project.archive if request.post? && @project.active?
138 redirect_to :controller => 'admin', :action => 'projects'
135 redirect_to :controller => 'admin', :action => 'projects'
139 end
136 end
140
137
141 def unarchive
138 def unarchive
142 @project.unarchive if request.post? && !@project.active?
139 @project.unarchive if request.post? && !@project.active?
143 redirect_to :controller => 'admin', :action => 'projects'
140 redirect_to :controller => 'admin', :action => 'projects'
144 end
141 end
145
142
146 # Delete @project
143 # Delete @project
147 def destroy
144 def destroy
148 @project_to_destroy = @project
145 @project_to_destroy = @project
149 if request.post? and params[:confirm]
146 if request.post? and params[:confirm]
150 @project_to_destroy.destroy
147 @project_to_destroy.destroy
151 redirect_to :controller => 'admin', :action => 'projects'
148 redirect_to :controller => 'admin', :action => 'projects'
152 end
149 end
153 # hide project in layout
150 # hide project in layout
154 @project = nil
151 @project = nil
155 end
152 end
156
153
157 # Add a new issue category to @project
154 # Add a new issue category to @project
158 def add_issue_category
155 def add_issue_category
159 @category = @project.issue_categories.build(params[:category])
156 @category = @project.issue_categories.build(params[:category])
160 if request.post? and @category.save
157 if request.post? and @category.save
161 respond_to do |format|
158 respond_to do |format|
162 format.html do
159 format.html do
163 flash[:notice] = l(:notice_successful_create)
160 flash[:notice] = l(:notice_successful_create)
164 redirect_to :action => 'settings', :tab => 'categories', :id => @project
161 redirect_to :action => 'settings', :tab => 'categories', :id => @project
165 end
162 end
166 format.js do
163 format.js do
167 # IE doesn't support the replace_html rjs method for select box options
164 # IE doesn't support the replace_html rjs method for select box options
168 render(:update) {|page| page.replace "issue_category_id",
165 render(:update) {|page| page.replace "issue_category_id",
169 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]')
166 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]')
170 }
167 }
171 end
168 end
172 end
169 end
173 end
170 end
174 end
171 end
175
172
176 # Add a new version to @project
173 # Add a new version to @project
177 def add_version
174 def add_version
178 @version = @project.versions.build(params[:version])
175 @version = @project.versions.build(params[:version])
179 if request.post? and @version.save
176 if request.post? and @version.save
180 flash[:notice] = l(:notice_successful_create)
177 flash[:notice] = l(:notice_successful_create)
181 redirect_to :action => 'settings', :tab => 'versions', :id => @project
178 redirect_to :action => 'settings', :tab => 'versions', :id => @project
182 end
179 end
183 end
180 end
184
181
185 def add_file
182 def add_file
186 if request.post?
183 if request.post?
187 @version = @project.versions.find_by_id(params[:version_id])
184 @version = @project.versions.find_by_id(params[:version_id])
188 attachments = attach_files(@version, params[:attachments])
185 attachments = attach_files(@version, params[:attachments])
189 Mailer.deliver_attachments_added(attachments) if !attachments.empty? && Setting.notified_events.include?('file_added')
186 Mailer.deliver_attachments_added(attachments) if !attachments.empty? && Setting.notified_events.include?('file_added')
190 redirect_to :controller => 'projects', :action => 'list_files', :id => @project
187 redirect_to :controller => 'projects', :action => 'list_files', :id => @project
191 end
188 end
192 @versions = @project.versions.sort
189 @versions = @project.versions.sort
193 end
190 end
194
191
195 def list_files
192 def list_files
196 @versions = @project.versions.sort
193 @versions = @project.versions.sort
197 end
194 end
198
195
199 # Show changelog for @project
196 # Show changelog for @project
200 def changelog
197 def changelog
201 @trackers = @project.trackers.find(:all, :conditions => ["is_in_chlog=?", true], :order => 'position')
198 @trackers = @project.trackers.find(:all, :conditions => ["is_in_chlog=?", true], :order => 'position')
202 retrieve_selected_tracker_ids(@trackers)
199 retrieve_selected_tracker_ids(@trackers)
203 @versions = @project.versions.sort
200 @versions = @project.versions.sort
204 end
201 end
205
202
206 def roadmap
203 def roadmap
207 @trackers = @project.trackers.find(:all, :conditions => ["is_in_roadmap=?", true])
204 @trackers = @project.trackers.find(:all, :conditions => ["is_in_roadmap=?", true])
208 retrieve_selected_tracker_ids(@trackers)
205 retrieve_selected_tracker_ids(@trackers)
209 @versions = @project.versions.sort
206 @versions = @project.versions.sort
210 @versions = @versions.select {|v| !v.completed? } unless params[:completed]
207 @versions = @versions.select {|v| !v.completed? } unless params[:completed]
211 end
208 end
212
209
213 def activity
210 def activity
214 if params[:year] and params[:year].to_i > 1900
211 if params[:year] and params[:year].to_i > 1900
215 @year = params[:year].to_i
212 @year = params[:year].to_i
216 if params[:month] and params[:month].to_i > 0 and params[:month].to_i < 13
213 if params[:month] and params[:month].to_i > 0 and params[:month].to_i < 13
217 @month = params[:month].to_i
214 @month = params[:month].to_i
218 end
215 end
219 end
216 end
220 @year ||= Date.today.year
217 @year ||= Date.today.year
221 @month ||= Date.today.month
218 @month ||= Date.today.month
222
219
223 case params[:format]
220 case params[:format]
224 when 'atom'
221 when 'atom'
225 # 30 last days
222 # 30 last days
226 @date_from = Date.today - 30
223 @date_from = Date.today - 30
227 @date_to = Date.today + 1
224 @date_to = Date.today + 1
228 else
225 else
229 # current month
226 # current month
230 @date_from = Date.civil(@year, @month, 1)
227 @date_from = Date.civil(@year, @month, 1)
231 @date_to = @date_from >> 1
228 @date_to = @date_from >> 1
232 end
229 end
233
230
234 @event_types = %w(issues news files documents changesets wiki_pages messages)
231 @event_types = %w(issues news files documents changesets wiki_pages messages)
235 @event_types.delete('wiki_pages') unless @project.wiki
232 @event_types.delete('wiki_pages') unless @project.wiki
236 @event_types.delete('changesets') unless @project.repository
233 @event_types.delete('changesets') unless @project.repository
237 @event_types.delete('messages') unless @project.boards.any?
234 @event_types.delete('messages') unless @project.boards.any?
238 # only show what the user is allowed to view
235 # only show what the user is allowed to view
239 @event_types = @event_types.select {|o| User.current.allowed_to?("view_#{o}".to_sym, @project)}
236 @event_types = @event_types.select {|o| User.current.allowed_to?("view_#{o}".to_sym, @project)}
240
237
241 @scope = @event_types.select {|t| params["show_#{t}"]}
238 @scope = @event_types.select {|t| params["show_#{t}"]}
242 # default events if none is specified in parameters
239 # default events if none is specified in parameters
243 @scope = (@event_types - %w(wiki_pages messages))if @scope.empty?
240 @scope = (@event_types - %w(wiki_pages messages))if @scope.empty?
244
241
245 @events = []
242 @events = []
246
243
247 if @scope.include?('issues')
244 if @scope.include?('issues')
248 @events += @project.issues.find(:all, :include => [:author, :tracker], :conditions => ["#{Issue.table_name}.created_on>=? and #{Issue.table_name}.created_on<=?", @date_from, @date_to] )
245 @events += @project.issues.find(:all, :include => [:author, :tracker], :conditions => ["#{Issue.table_name}.created_on>=? and #{Issue.table_name}.created_on<=?", @date_from, @date_to] )
249 @events += @project.issues_status_changes(@date_from, @date_to)
246 @events += @project.issues_status_changes(@date_from, @date_to)
250 end
247 end
251
248
252 if @scope.include?('news')
249 if @scope.include?('news')
253 @events += @project.news.find(:all, :conditions => ["#{News.table_name}.created_on>=? and #{News.table_name}.created_on<=?", @date_from, @date_to], :include => :author )
250 @events += @project.news.find(:all, :conditions => ["#{News.table_name}.created_on>=? and #{News.table_name}.created_on<=?", @date_from, @date_to], :include => :author )
254 end
251 end
255
252
256 if @scope.include?('files')
253 if @scope.include?('files')
257 @events += Attachment.find(:all, :select => "#{Attachment.table_name}.*", :joins => "LEFT JOIN #{Version.table_name} ON #{Version.table_name}.id = #{Attachment.table_name}.container_id", :conditions => ["#{Attachment.table_name}.container_type='Version' and #{Version.table_name}.project_id=? and #{Attachment.table_name}.created_on>=? and #{Attachment.table_name}.created_on<=?", @project.id, @date_from, @date_to], :include => :author )
254 @events += Attachment.find(:all, :select => "#{Attachment.table_name}.*", :joins => "LEFT JOIN #{Version.table_name} ON #{Version.table_name}.id = #{Attachment.table_name}.container_id", :conditions => ["#{Attachment.table_name}.container_type='Version' and #{Version.table_name}.project_id=? and #{Attachment.table_name}.created_on>=? and #{Attachment.table_name}.created_on<=?", @project.id, @date_from, @date_to], :include => :author )
258 end
255 end
259
256
260 if @scope.include?('documents')
257 if @scope.include?('documents')
261 @events += @project.documents.find(:all, :conditions => ["#{Document.table_name}.created_on>=? and #{Document.table_name}.created_on<=?", @date_from, @date_to] )
258 @events += @project.documents.find(:all, :conditions => ["#{Document.table_name}.created_on>=? and #{Document.table_name}.created_on<=?", @date_from, @date_to] )
262 @events += Attachment.find(:all, :select => "attachments.*", :joins => "LEFT JOIN #{Document.table_name} ON #{Document.table_name}.id = #{Attachment.table_name}.container_id", :conditions => ["#{Attachment.table_name}.container_type='Document' and #{Document.table_name}.project_id=? and #{Attachment.table_name}.created_on>=? and #{Attachment.table_name}.created_on<=?", @project.id, @date_from, @date_to], :include => :author )
259 @events += Attachment.find(:all, :select => "attachments.*", :joins => "LEFT JOIN #{Document.table_name} ON #{Document.table_name}.id = #{Attachment.table_name}.container_id", :conditions => ["#{Attachment.table_name}.container_type='Document' and #{Document.table_name}.project_id=? and #{Attachment.table_name}.created_on>=? and #{Attachment.table_name}.created_on<=?", @project.id, @date_from, @date_to], :include => :author )
263 end
260 end
264
261
265 if @scope.include?('wiki_pages')
262 if @scope.include?('wiki_pages')
266 select = "#{WikiContent.versioned_table_name}.updated_on, #{WikiContent.versioned_table_name}.comments, " +
263 select = "#{WikiContent.versioned_table_name}.updated_on, #{WikiContent.versioned_table_name}.comments, " +
267 "#{WikiContent.versioned_table_name}.#{WikiContent.version_column}, #{WikiPage.table_name}.title, " +
264 "#{WikiContent.versioned_table_name}.#{WikiContent.version_column}, #{WikiPage.table_name}.title, " +
268 "#{WikiContent.versioned_table_name}.page_id, #{WikiContent.versioned_table_name}.author_id, " +
265 "#{WikiContent.versioned_table_name}.page_id, #{WikiContent.versioned_table_name}.author_id, " +
269 "#{WikiContent.versioned_table_name}.id"
266 "#{WikiContent.versioned_table_name}.id"
270 joins = "LEFT JOIN #{WikiPage.table_name} ON #{WikiPage.table_name}.id = #{WikiContent.versioned_table_name}.page_id " +
267 joins = "LEFT JOIN #{WikiPage.table_name} ON #{WikiPage.table_name}.id = #{WikiContent.versioned_table_name}.page_id " +
271 "LEFT JOIN #{Wiki.table_name} ON #{Wiki.table_name}.id = #{WikiPage.table_name}.wiki_id "
268 "LEFT JOIN #{Wiki.table_name} ON #{Wiki.table_name}.id = #{WikiPage.table_name}.wiki_id "
272 conditions = ["#{Wiki.table_name}.project_id = ? AND #{WikiContent.versioned_table_name}.updated_on BETWEEN ? AND ?",
269 conditions = ["#{Wiki.table_name}.project_id = ? AND #{WikiContent.versioned_table_name}.updated_on BETWEEN ? AND ?",
273 @project.id, @date_from, @date_to]
270 @project.id, @date_from, @date_to]
274
271
275 @events += WikiContent.versioned_class.find(:all, :select => select, :joins => joins, :conditions => conditions)
272 @events += WikiContent.versioned_class.find(:all, :select => select, :joins => joins, :conditions => conditions)
276 end
273 end
277
274
278 if @scope.include?('changesets')
275 if @scope.include?('changesets')
279 @events += Changeset.find(:all, :include => :repository, :conditions => ["#{Repository.table_name}.project_id = ? AND #{Changeset.table_name}.committed_on BETWEEN ? AND ?", @project.id, @date_from, @date_to])
276 @events += Changeset.find(:all, :include => :repository, :conditions => ["#{Repository.table_name}.project_id = ? AND #{Changeset.table_name}.committed_on BETWEEN ? AND ?", @project.id, @date_from, @date_to])
280 end
277 end
281
278
282 if @scope.include?('messages')
279 if @scope.include?('messages')
283 @events += Message.find(:all,
280 @events += Message.find(:all,
284 :include => [:board, :author],
281 :include => [:board, :author],
285 :conditions => ["#{Board.table_name}.project_id=? AND #{Message.table_name}.parent_id IS NULL AND #{Message.table_name}.created_on BETWEEN ? AND ?", @project.id, @date_from, @date_to])
282 :conditions => ["#{Board.table_name}.project_id=? AND #{Message.table_name}.parent_id IS NULL AND #{Message.table_name}.created_on BETWEEN ? AND ?", @project.id, @date_from, @date_to])
286 end
283 end
287
284
288 @events_by_day = @events.group_by(&:event_date)
285 @events_by_day = @events.group_by(&:event_date)
289
286
290 respond_to do |format|
287 respond_to do |format|
291 format.html { render :layout => false if request.xhr? }
288 format.html { render :layout => false if request.xhr? }
292 format.atom { render_feed(@events, :title => "#{@project.name}: #{l(:label_activity)}") }
289 format.atom { render_feed(@events, :title => "#{@project.name}: #{l(:label_activity)}") }
293 end
290 end
294 end
291 end
295
292
296 def calendar
293 def calendar
297 @trackers = @project.rolled_up_trackers
294 @trackers = @project.rolled_up_trackers
298 retrieve_selected_tracker_ids(@trackers)
295 retrieve_selected_tracker_ids(@trackers)
299
296
300 if params[:year] and params[:year].to_i > 1900
297 if params[:year] and params[:year].to_i > 1900
301 @year = params[:year].to_i
298 @year = params[:year].to_i
302 if params[:month] and params[:month].to_i > 0 and params[:month].to_i < 13
299 if params[:month] and params[:month].to_i > 0 and params[:month].to_i < 13
303 @month = params[:month].to_i
300 @month = params[:month].to_i
304 end
301 end
305 end
302 end
306 @year ||= Date.today.year
303 @year ||= Date.today.year
307 @month ||= Date.today.month
304 @month ||= Date.today.month
308 @calendar = Redmine::Helpers::Calendar.new(Date.civil(@year, @month, 1), current_language, :month)
305 @calendar = Redmine::Helpers::Calendar.new(Date.civil(@year, @month, 1), current_language, :month)
309
306
310 events = []
307 events = []
311 @project.issues_with_subprojects(params[:with_subprojects]) do
308 @project.issues_with_subprojects(params[:with_subprojects]) do
312 events += Issue.find(:all,
309 events += Issue.find(:all,
313 :include => [:tracker, :status, :assigned_to, :priority, :project],
310 :include => [:tracker, :status, :assigned_to, :priority, :project],
314 :conditions => ["((start_date BETWEEN ? AND ?) OR (due_date BETWEEN ? AND ?)) AND #{Issue.table_name}.tracker_id IN (#{@selected_tracker_ids.join(',')})", @calendar.startdt, @calendar.enddt, @calendar.startdt, @calendar.enddt]
311 :conditions => ["((start_date BETWEEN ? AND ?) OR (due_date BETWEEN ? AND ?)) AND #{Issue.table_name}.tracker_id IN (#{@selected_tracker_ids.join(',')})", @calendar.startdt, @calendar.enddt, @calendar.startdt, @calendar.enddt]
315 ) unless @selected_tracker_ids.empty?
312 ) unless @selected_tracker_ids.empty?
316 end
313 end
317 events += @project.versions.find(:all, :conditions => ["effective_date BETWEEN ? AND ?", @calendar.startdt, @calendar.enddt])
314 events += @project.versions.find(:all, :conditions => ["effective_date BETWEEN ? AND ?", @calendar.startdt, @calendar.enddt])
318 @calendar.events = events
315 @calendar.events = events
319
316
320 render :layout => false if request.xhr?
317 render :layout => false if request.xhr?
321 end
318 end
322
319
323 def gantt
320 def gantt
324 @trackers = @project.rolled_up_trackers
321 @trackers = @project.rolled_up_trackers
325 retrieve_selected_tracker_ids(@trackers)
322 retrieve_selected_tracker_ids(@trackers)
326
323
327 if params[:year] and params[:year].to_i >0
324 if params[:year] and params[:year].to_i >0
328 @year_from = params[:year].to_i
325 @year_from = params[:year].to_i
329 if params[:month] and params[:month].to_i >=1 and params[:month].to_i <= 12
326 if params[:month] and params[:month].to_i >=1 and params[:month].to_i <= 12
330 @month_from = params[:month].to_i
327 @month_from = params[:month].to_i
331 else
328 else
332 @month_from = 1
329 @month_from = 1
333 end
330 end
334 else
331 else
335 @month_from ||= Date.today.month
332 @month_from ||= Date.today.month
336 @year_from ||= Date.today.year
333 @year_from ||= Date.today.year
337 end
334 end
338
335
339 zoom = (params[:zoom] || User.current.pref[:gantt_zoom]).to_i
336 zoom = (params[:zoom] || User.current.pref[:gantt_zoom]).to_i
340 @zoom = (zoom > 0 && zoom < 5) ? zoom : 2
337 @zoom = (zoom > 0 && zoom < 5) ? zoom : 2
341 months = (params[:months] || User.current.pref[:gantt_months]).to_i
338 months = (params[:months] || User.current.pref[:gantt_months]).to_i
342 @months = (months > 0 && months < 25) ? months : 6
339 @months = (months > 0 && months < 25) ? months : 6
343
340
344 # Save gantt paramters as user preference (zoom and months count)
341 # Save gantt paramters as user preference (zoom and months count)
345 if (User.current.logged? && (@zoom != User.current.pref[:gantt_zoom] || @months != User.current.pref[:gantt_months]))
342 if (User.current.logged? && (@zoom != User.current.pref[:gantt_zoom] || @months != User.current.pref[:gantt_months]))
346 User.current.pref[:gantt_zoom], User.current.pref[:gantt_months] = @zoom, @months
343 User.current.pref[:gantt_zoom], User.current.pref[:gantt_months] = @zoom, @months
347 User.current.preference.save
344 User.current.preference.save
348 end
345 end
349
346
350 @date_from = Date.civil(@year_from, @month_from, 1)
347 @date_from = Date.civil(@year_from, @month_from, 1)
351 @date_to = (@date_from >> @months) - 1
348 @date_to = (@date_from >> @months) - 1
352
349
353 @events = []
350 @events = []
354 @project.issues_with_subprojects(params[:with_subprojects]) do
351 @project.issues_with_subprojects(params[:with_subprojects]) do
355 @events += Issue.find(:all,
352 @events += Issue.find(:all,
356 :order => "start_date, due_date",
353 :order => "start_date, due_date",
357 :include => [:tracker, :status, :assigned_to, :priority, :project],
354 :include => [:tracker, :status, :assigned_to, :priority, :project],
358 :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]
355 :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]
359 ) unless @selected_tracker_ids.empty?
356 ) unless @selected_tracker_ids.empty?
360 end
357 end
361 @events += @project.versions.find(:all, :conditions => ["effective_date BETWEEN ? AND ?", @date_from, @date_to])
358 @events += @project.versions.find(:all, :conditions => ["effective_date BETWEEN ? AND ?", @date_from, @date_to])
362 @events.sort! {|x,y| x.start_date <=> y.start_date }
359 @events.sort! {|x,y| x.start_date <=> y.start_date }
363
360
364 if params[:format]=='pdf'
361 if params[:format]=='pdf'
365 @options_for_rfpdf ||= {}
362 @options_for_rfpdf ||= {}
366 @options_for_rfpdf[:file_name] = "#{@project.identifier}-gantt.pdf"
363 @options_for_rfpdf[:file_name] = "#{@project.identifier}-gantt.pdf"
367 render :template => "projects/gantt.rfpdf", :layout => false
364 render :template => "projects/gantt.rfpdf", :layout => false
368 elsif params[:format]=='png' && respond_to?('gantt_image')
365 elsif params[:format]=='png' && respond_to?('gantt_image')
369 image = gantt_image(@events, @date_from, @months, @zoom)
366 image = gantt_image(@events, @date_from, @months, @zoom)
370 image.format = 'PNG'
367 image.format = 'PNG'
371 send_data(image.to_blob, :disposition => 'inline', :type => 'image/png', :filename => "#{@project.identifier}-gantt.png")
368 send_data(image.to_blob, :disposition => 'inline', :type => 'image/png', :filename => "#{@project.identifier}-gantt.png")
372 else
369 else
373 render :template => "projects/gantt.rhtml"
370 render :template => "projects/gantt.rhtml"
374 end
371 end
375 end
372 end
376
373
377 private
374 private
378 # Find project of id params[:id]
375 # Find project of id params[:id]
379 # if not found, redirect to project list
376 # if not found, redirect to project list
380 # Used as a before_filter
377 # Used as a before_filter
381 def find_project
378 def find_project
382 @project = Project.find(params[:id])
379 @project = Project.find(params[:id])
383 rescue ActiveRecord::RecordNotFound
380 rescue ActiveRecord::RecordNotFound
384 render_404
381 render_404
385 end
382 end
386
383
387 def retrieve_selected_tracker_ids(selectable_trackers)
384 def retrieve_selected_tracker_ids(selectable_trackers)
388 if ids = params[:tracker_ids]
385 if ids = params[:tracker_ids]
389 @selected_tracker_ids = (ids.is_a? Array) ? ids.collect { |id| id.to_i.to_s } : ids.split('/').collect { |id| id.to_i.to_s }
386 @selected_tracker_ids = (ids.is_a? Array) ? ids.collect { |id| id.to_i.to_s } : ids.split('/').collect { |id| id.to_i.to_s }
390 else
387 else
391 @selected_tracker_ids = selectable_trackers.collect {|t| t.id.to_s }
388 @selected_tracker_ids = selectable_trackers.collect {|t| t.id.to_s }
392 end
389 end
393 end
390 end
394 end
391 end
@@ -1,72 +1,70
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006 Jean-Philippe Lang
2 # Copyright (C) 2006 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 class VersionsController < ApplicationController
18 class VersionsController < ApplicationController
19 layout 'base'
19 layout 'base'
20 menu_item :roadmap
20 menu_item :roadmap
21 before_filter :find_project, :authorize
21 before_filter :find_project, :authorize
22
22
23 cache_sweeper :version_sweeper, :only => [ :edit, :destroy ]
24
25 def show
23 def show
26 end
24 end
27
25
28 def edit
26 def edit
29 if request.post? and @version.update_attributes(params[:version])
27 if request.post? and @version.update_attributes(params[:version])
30 flash[:notice] = l(:notice_successful_update)
28 flash[:notice] = l(:notice_successful_update)
31 redirect_to :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project
29 redirect_to :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project
32 end
30 end
33 end
31 end
34
32
35 def destroy
33 def destroy
36 @version.destroy
34 @version.destroy
37 redirect_to :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project
35 redirect_to :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project
38 rescue
36 rescue
39 flash[:error] = "Unable to delete version"
37 flash[:error] = "Unable to delete version"
40 redirect_to :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project
38 redirect_to :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project
41 end
39 end
42
40
43 def download
41 def download
44 @attachment = @version.attachments.find(params[:attachment_id])
42 @attachment = @version.attachments.find(params[:attachment_id])
45 @attachment.increment_download
43 @attachment.increment_download
46 send_file @attachment.diskfile, :filename => filename_for_content_disposition(@attachment.filename),
44 send_file @attachment.diskfile, :filename => filename_for_content_disposition(@attachment.filename),
47 :type => @attachment.content_type
45 :type => @attachment.content_type
48 rescue
46 rescue
49 render_404
47 render_404
50 end
48 end
51
49
52 def destroy_file
50 def destroy_file
53 @version.attachments.find(params[:attachment_id]).destroy
51 @version.attachments.find(params[:attachment_id]).destroy
54 flash[:notice] = l(:notice_successful_delete)
52 flash[:notice] = l(:notice_successful_delete)
55 redirect_to :controller => 'projects', :action => 'list_files', :id => @project
53 redirect_to :controller => 'projects', :action => 'list_files', :id => @project
56 end
54 end
57
55
58 def status_by
56 def status_by
59 respond_to do |format|
57 respond_to do |format|
60 format.html { render :action => 'show' }
58 format.html { render :action => 'show' }
61 format.js { render(:update) {|page| page.replace_html 'status_by', render_issue_status_by(@version, params[:status_by])} }
59 format.js { render(:update) {|page| page.replace_html 'status_by', render_issue_status_by(@version, params[:status_by])} }
62 end
60 end
63 end
61 end
64
62
65 private
63 private
66 def find_project
64 def find_project
67 @version = Version.find(params[:id])
65 @version = Version.find(params[:id])
68 @project = @version.project
66 @project = @version.project
69 rescue ActiveRecord::RecordNotFound
67 rescue ActiveRecord::RecordNotFound
70 render_404
68 render_404
71 end
69 end
72 end
70 end
@@ -1,42 +1,40
1 <% cache(:year => @year, :month => @month, :tracker_ids => @selected_tracker_ids, :subprojects => params[:with_subprojects], :lang => current_language) do %>
2 <h2><%= "#{month_name(@month)} #{@year}" %></h2>
1 <h2><%= "#{month_name(@month)} #{@year}" %></h2>
3
2
4 <table width="100%">
3 <table width="100%">
5 <tr><td align="left">
4 <tr><td align="left">
6 <%= link_to_remote ('&#171; ' + (@month==1 ? "#{month_name(12)} #{@year-1}" : "#{month_name(@month-1)}")),
5 <%= link_to_remote ('&#171; ' + (@month==1 ? "#{month_name(12)} #{@year-1}" : "#{month_name(@month-1)}")),
7 {:update => "content", :url => { :year => (@month==1 ? @year-1 : @year), :month =>(@month==1 ? 12 : @month-1), :tracker_ids => @selected_tracker_ids, :with_subprojects => params[:with_subprojects] }},
6 {:update => "content", :url => { :year => (@month==1 ? @year-1 : @year), :month =>(@month==1 ? 12 : @month-1), :tracker_ids => @selected_tracker_ids, :with_subprojects => params[:with_subprojects] }},
8 {:href => url_for(:action => 'calendar', :year => (@month==1 ? @year-1 : @year), :month =>(@month==1 ? 12 : @month-1), :tracker_ids => @selected_tracker_ids, :with_subprojects => params[:with_subprojects])}
7 {:href => url_for(:action => 'calendar', :year => (@month==1 ? @year-1 : @year), :month =>(@month==1 ? 12 : @month-1), :tracker_ids => @selected_tracker_ids, :with_subprojects => params[:with_subprojects])}
9 %>
8 %>
10 </td><td align="right">
9 </td><td align="right">
11 <%= link_to_remote ((@month==12 ? "#{month_name(1)} #{@year+1}" : "#{month_name(@month+1)}") + ' &#187;'),
10 <%= link_to_remote ((@month==12 ? "#{month_name(1)} #{@year+1}" : "#{month_name(@month+1)}") + ' &#187;'),
12 {:update => "content", :url => { :year => (@month==12 ? @year+1 : @year), :month =>(@month==12 ? 1 : @month+1), :tracker_ids => @selected_tracker_ids, :with_subprojects => params[:with_subprojects] }},
11 {:update => "content", :url => { :year => (@month==12 ? @year+1 : @year), :month =>(@month==12 ? 1 : @month+1), :tracker_ids => @selected_tracker_ids, :with_subprojects => params[:with_subprojects] }},
13 {:href => url_for(:action => 'calendar', :year => (@month==12 ? @year+1 : @year), :month =>(@month==12 ? 1 : @month+1), :tracker_ids => @selected_tracker_ids, :with_subprojects => params[:with_subprojects])}
12 {:href => url_for(:action => 'calendar', :year => (@month==12 ? @year+1 : @year), :month =>(@month==12 ? 1 : @month+1), :tracker_ids => @selected_tracker_ids, :with_subprojects => params[:with_subprojects])}
14 %>
13 %>
15 </td></tr>
14 </td></tr>
16 </table>
15 </table>
17
16
18 <%= render :partial => 'common/calendar', :locals => {:calendar => @calendar} %>
17 <%= render :partial => 'common/calendar', :locals => {:calendar => @calendar} %>
19
18
20 <%= image_tag 'arrow_from.png' %>&nbsp;&nbsp;<%= l(:text_tip_task_begin_day) %><br />
19 <%= image_tag 'arrow_from.png' %>&nbsp;&nbsp;<%= l(:text_tip_task_begin_day) %><br />
21 <%= image_tag 'arrow_to.png' %>&nbsp;&nbsp;<%= l(:text_tip_task_end_day) %><br />
20 <%= image_tag 'arrow_to.png' %>&nbsp;&nbsp;<%= l(:text_tip_task_end_day) %><br />
22 <%= image_tag 'arrow_bw.png' %>&nbsp;&nbsp;<%= l(:text_tip_task_begin_end_day) %><br />
21 <%= image_tag 'arrow_bw.png' %>&nbsp;&nbsp;<%= l(:text_tip_task_begin_end_day) %><br />
23 <% end %>
24
22
25 <% content_for :sidebar do %>
23 <% content_for :sidebar do %>
26 <h3><%= l(:label_calendar) %></h3>
24 <h3><%= l(:label_calendar) %></h3>
27
25
28 <% form_tag() do %>
26 <% form_tag() do %>
29 <p><%= select_month(@month, :prefix => "month", :discard_type => true) %>
27 <p><%= select_month(@month, :prefix => "month", :discard_type => true) %>
30 <%= select_year(@year, :prefix => "year", :discard_type => true) %></p>
28 <%= select_year(@year, :prefix => "year", :discard_type => true) %></p>
31
29
32 <% @trackers.each do |tracker| %>
30 <% @trackers.each do |tracker| %>
33 <label><%= check_box_tag "tracker_ids[]", tracker.id, (@selected_tracker_ids.include? tracker.id.to_s) %> <%= tracker.name %></label><br />
31 <label><%= check_box_tag "tracker_ids[]", tracker.id, (@selected_tracker_ids.include? tracker.id.to_s) %> <%= tracker.name %></label><br />
34 <% end %>
32 <% end %>
35 <% if @project.active_children.any? %>
33 <% if @project.active_children.any? %>
36 <br /><label><%= check_box_tag "with_subprojects", 1, params[:with_subprojects] %> <%=l(:label_subproject_plural)%></label>
34 <br /><label><%= check_box_tag "with_subprojects", 1, params[:with_subprojects] %> <%=l(:label_subproject_plural)%></label>
37 <% end %>
35 <% end %>
38 <p><%= submit_tag l(:button_apply), :class => 'button-small' %></p>
36 <p><%= submit_tag l(:button_apply), :class => 'button-small' %></p>
39 <% end %>
37 <% end %>
40 <% end %>
38 <% end %>
41
39
42 <% html_title(l(:label_calendar)) -%>
40 <% html_title(l(:label_calendar)) -%>
@@ -1,249 +1,244
1 <% zoom = 1
1 <% zoom = 1
2 @zoom.times { zoom = zoom * 2 }
2 @zoom.times { zoom = zoom * 2 }
3
3
4 subject_width = 330
4 subject_width = 330
5 header_heigth = 18
5 header_heigth = 18
6
6
7 headers_height = header_heigth
7 headers_height = header_heigth
8 show_weeks = false
8 show_weeks = false
9 show_days = false
9 show_days = false
10
10
11 if @zoom >1
11 if @zoom >1
12 show_weeks = true
12 show_weeks = true
13 headers_height = 2*header_heigth
13 headers_height = 2*header_heigth
14 if @zoom > 2
14 if @zoom > 2
15 show_days = true
15 show_days = true
16 headers_height = 3*header_heigth
16 headers_height = 3*header_heigth
17 end
17 end
18 end
18 end
19
19
20 g_width = (@date_to - @date_from + 1)*zoom
20 g_width = (@date_to - @date_from + 1)*zoom
21 g_height = [(20 * @events.length + 6)+150, 206].max
21 g_height = [(20 * @events.length + 6)+150, 206].max
22 t_height = g_height + headers_height
22 t_height = g_height + headers_height
23 %>
23 %>
24
24
25 <div class="contextual">
25 <div class="contextual">
26 </div>
26 </div>
27
27
28 <h2><%= l(:label_gantt) %></h2>
28 <h2><%= l(:label_gantt) %></h2>
29
29
30 <% form_tag(params.merge(:month => nil, :year => nil, :months => nil)) do %>
30 <% form_tag(params.merge(:month => nil, :year => nil, :months => nil)) do %>
31 <table width="100%">
31 <table width="100%">
32 <tr>
32 <tr>
33 <td align="left">
33 <td align="left">
34 <input type="text" name="months" size="2" value="<%= @months %>" />
34 <input type="text" name="months" size="2" value="<%= @months %>" />
35 <%= l(:label_months_from) %>
35 <%= l(:label_months_from) %>
36 <%= select_month(@month_from, :prefix => "month", :discard_type => true) %>
36 <%= select_month(@month_from, :prefix => "month", :discard_type => true) %>
37 <%= select_year(@year_from, :prefix => "year", :discard_type => true) %>
37 <%= select_year(@year_from, :prefix => "year", :discard_type => true) %>
38 <%= hidden_field_tag 'zoom', @zoom %>
38 <%= hidden_field_tag 'zoom', @zoom %>
39 <%= submit_tag l(:button_submit), :class => "button-small" %>
39 <%= submit_tag l(:button_submit), :class => "button-small" %>
40 </td>
40 </td>
41
41
42 <td align="right">
42 <td align="right">
43 <%= if @zoom < 4
43 <%= if @zoom < 4
44 link_to image_tag('zoom_in.png'), {:zoom => (@zoom+1), :year => @year_from, :month => @month_from, :months => @months, :tracker_ids => @selected_tracker_ids, :with_subprojects => params[:with_subprojects]}
44 link_to image_tag('zoom_in.png'), {:zoom => (@zoom+1), :year => @year_from, :month => @month_from, :months => @months, :tracker_ids => @selected_tracker_ids, :with_subprojects => params[:with_subprojects]}
45 else
45 else
46 image_tag 'zoom_in_g.png'
46 image_tag 'zoom_in_g.png'
47 end %>
47 end %>
48 <%= if @zoom > 1
48 <%= if @zoom > 1
49 link_to image_tag('zoom_out.png'),{:zoom => (@zoom-1), :year => @year_from, :month => @month_from, :months => @months, :tracker_ids => @selected_tracker_ids, :with_subprojects => params[:with_subprojects]}
49 link_to image_tag('zoom_out.png'),{:zoom => (@zoom-1), :year => @year_from, :month => @month_from, :months => @months, :tracker_ids => @selected_tracker_ids, :with_subprojects => params[:with_subprojects]}
50 else
50 else
51 image_tag 'zoom_out_g.png'
51 image_tag 'zoom_out_g.png'
52 end %>
52 end %>
53 </td>
53 </td>
54 </tr>
54 </tr>
55 </table>
55 </table>
56 <% end %>
56 <% end %>
57
57
58 <% cache(:year => @year_from, :month => @month_from, :months => @months, :zoom => @zoom, :tracker_ids => @selected_tracker_ids, :subprojects => params[:with_subprojects], :lang => current_language) do %>
59
60 <table width="100%" style="border:0; border-collapse: collapse;">
58 <table width="100%" style="border:0; border-collapse: collapse;">
61 <tr>
59 <tr>
62 <td style="width:<%= subject_width %>px;">
60 <td style="width:<%= subject_width %>px;">
63
61
64 <div style="position:relative;height:<%= t_height + 24 %>px;width:<%= subject_width + 1 %>px;">
62 <div style="position:relative;height:<%= t_height + 24 %>px;width:<%= subject_width + 1 %>px;">
65 <div style="right:-2px;width:<%= subject_width %>px;height:<%= headers_height %>px;background: #eee;" class="gantt_hdr"></div>
63 <div style="right:-2px;width:<%= subject_width %>px;height:<%= headers_height %>px;background: #eee;" class="gantt_hdr"></div>
66 <div style="right:-2px;width:<%= subject_width %>px;height:<%= t_height %>px;border-left: 1px solid #c0c0c0;overflow:hidden;" class="gantt_hdr"></div>
64 <div style="right:-2px;width:<%= subject_width %>px;height:<%= t_height %>px;border-left: 1px solid #c0c0c0;overflow:hidden;" class="gantt_hdr"></div>
67 <%
65 <%
68 #
66 #
69 # Tasks subjects
67 # Tasks subjects
70 #
68 #
71 top = headers_height + 8
69 top = headers_height + 8
72 @events.each do |i| %>
70 @events.each do |i| %>
73 <div style="position: absolute;line-height:1.2em;height:16px;top:<%= top %>px;left:4px;overflow:hidden;"><small>
71 <div style="position: absolute;line-height:1.2em;height:16px;top:<%= top %>px;left:4px;overflow:hidden;"><small>
74 <% if i.is_a? Issue %>
72 <% if i.is_a? Issue %>
75 <%= h("#{i.project.name} -") unless @project && @project == i.project %>
73 <%= h("#{i.project.name} -") unless @project && @project == i.project %>
76 <%= link_to_issue i %>: <%=h i.subject %>
74 <%= link_to_issue i %>: <%=h i.subject %>
77 <% else %>
75 <% else %>
78 <%= link_to_version i, :class => "icon icon-package" %>
76 <%= link_to_version i, :class => "icon icon-package" %>
79 <% end %>
77 <% end %>
80 </small></div>
78 </small></div>
81 <% top = top + 20
79 <% top = top + 20
82 end %>
80 end %>
83 </div>
81 </div>
84 </td>
82 </td>
85 <td>
83 <td>
86
84
87 <div style="position:relative;height:<%= t_height + 24 %>px;overflow:auto;">
85 <div style="position:relative;height:<%= t_height + 24 %>px;overflow:auto;">
88 <div style="width:<%= g_width-1 %>px;height:<%= headers_height %>px;background: #eee;" class="gantt_hdr">&nbsp;</div>
86 <div style="width:<%= g_width-1 %>px;height:<%= headers_height %>px;background: #eee;" class="gantt_hdr">&nbsp;</div>
89 <%
87 <%
90 #
88 #
91 # Months headers
89 # Months headers
92 #
90 #
93 month_f = @date_from
91 month_f = @date_from
94 left = 0
92 left = 0
95 height = (show_weeks ? header_heigth : header_heigth + g_height)
93 height = (show_weeks ? header_heigth : header_heigth + g_height)
96 @months.times do
94 @months.times do
97 width = ((month_f >> 1) - month_f) * zoom - 1
95 width = ((month_f >> 1) - month_f) * zoom - 1
98 %>
96 %>
99 <div style="left:<%= left %>px;width:<%= width %>px;height:<%= height %>px;" class="gantt_hdr">
97 <div style="left:<%= left %>px;width:<%= width %>px;height:<%= height %>px;" class="gantt_hdr">
100 <%= link_to "#{month_f.year}-#{month_f.month}", { :year => month_f.year, :month => month_f.month, :zoom => @zoom, :months => @months, :tracker_ids => @selected_tracker_ids, :with_subprojects => params[:with_subprojects] }, :title => "#{month_name(month_f.month)} #{month_f.year}"%>
98 <%= link_to "#{month_f.year}-#{month_f.month}", { :year => month_f.year, :month => month_f.month, :zoom => @zoom, :months => @months, :tracker_ids => @selected_tracker_ids, :with_subprojects => params[:with_subprojects] }, :title => "#{month_name(month_f.month)} #{month_f.year}"%>
101 </div>
99 </div>
102 <%
100 <%
103 left = left + width + 1
101 left = left + width + 1
104 month_f = month_f >> 1
102 month_f = month_f >> 1
105 end %>
103 end %>
106
104
107 <%
105 <%
108 #
106 #
109 # Weeks headers
107 # Weeks headers
110 #
108 #
111 if show_weeks
109 if show_weeks
112 left = 0
110 left = 0
113 height = (show_days ? header_heigth-1 : header_heigth-1 + g_height)
111 height = (show_days ? header_heigth-1 : header_heigth-1 + g_height)
114 if @date_from.cwday == 1
112 if @date_from.cwday == 1
115 # @date_from is monday
113 # @date_from is monday
116 week_f = @date_from
114 week_f = @date_from
117 else
115 else
118 # find next monday after @date_from
116 # find next monday after @date_from
119 week_f = @date_from + (7 - @date_from.cwday + 1)
117 week_f = @date_from + (7 - @date_from.cwday + 1)
120 width = (7 - @date_from.cwday + 1) * zoom-1
118 width = (7 - @date_from.cwday + 1) * zoom-1
121 %>
119 %>
122 <div style="left:<%= left %>px;top:19px;width:<%= width %>px;height:<%= height %>px;" class="gantt_hdr">&nbsp;</div>
120 <div style="left:<%= left %>px;top:19px;width:<%= width %>px;height:<%= height %>px;" class="gantt_hdr">&nbsp;</div>
123 <%
121 <%
124 left = left + width+1
122 left = left + width+1
125 end %>
123 end %>
126 <%
124 <%
127 while week_f <= @date_to
125 while week_f <= @date_to
128 width = (week_f + 6 <= @date_to) ? 7 * zoom -1 : (@date_to - week_f + 1) * zoom-1
126 width = (week_f + 6 <= @date_to) ? 7 * zoom -1 : (@date_to - week_f + 1) * zoom-1
129 %>
127 %>
130 <div style="left:<%= left %>px;top:19px;width:<%= width %>px;height:<%= height %>px;" class="gantt_hdr">
128 <div style="left:<%= left %>px;top:19px;width:<%= width %>px;height:<%= height %>px;" class="gantt_hdr">
131 <small><%= week_f.cweek if width >= 16 %></small>
129 <small><%= week_f.cweek if width >= 16 %></small>
132 </div>
130 </div>
133 <%
131 <%
134 left = left + width+1
132 left = left + width+1
135 week_f = week_f+7
133 week_f = week_f+7
136 end
134 end
137 end %>
135 end %>
138
136
139 <%
137 <%
140 #
138 #
141 # Days headers
139 # Days headers
142 #
140 #
143 if show_days
141 if show_days
144 left = 0
142 left = 0
145 height = g_height + header_heigth - 1
143 height = g_height + header_heigth - 1
146 wday = @date_from.cwday
144 wday = @date_from.cwday
147 (@date_to - @date_from + 1).to_i.times do
145 (@date_to - @date_from + 1).to_i.times do
148 width = zoom - 1
146 width = zoom - 1
149 %>
147 %>
150 <div style="left:<%= left %>px;top:37px;width:<%= width %>px;height:<%= height %>px;font-size:0.7em;<%= "background:#f1f1f1;" if wday > 5 %>" class="gantt_hdr">
148 <div style="left:<%= left %>px;top:37px;width:<%= width %>px;height:<%= height %>px;font-size:0.7em;<%= "background:#f1f1f1;" if wday > 5 %>" class="gantt_hdr">
151 <%= day_name(wday).first %>
149 <%= day_name(wday).first %>
152 </div>
150 </div>
153 <%
151 <%
154 left = left + width+1
152 left = left + width+1
155 wday = wday + 1
153 wday = wday + 1
156 wday = 1 if wday > 7
154 wday = 1 if wday > 7
157 end
155 end
158 end %>
156 end %>
159
157
160 <%
158 <%
161 #
159 #
162 # Tasks
160 # Tasks
163 #
161 #
164 top = headers_height + 10
162 top = headers_height + 10
165 @events.each do |i|
163 @events.each do |i|
166 if i.is_a? Issue
164 if i.is_a? Issue
167 i_start_date = (i.start_date >= @date_from ? i.start_date : @date_from )
165 i_start_date = (i.start_date >= @date_from ? i.start_date : @date_from )
168 i_end_date = (i.due_date <= @date_to ? i.due_date : @date_to )
166 i_end_date = (i.due_date <= @date_to ? i.due_date : @date_to )
169
167
170 i_done_date = i.start_date + ((i.due_date - i.start_date+1)*i.done_ratio/100).floor
168 i_done_date = i.start_date + ((i.due_date - i.start_date+1)*i.done_ratio/100).floor
171 i_done_date = (i_done_date <= @date_from ? @date_from : i_done_date )
169 i_done_date = (i_done_date <= @date_from ? @date_from : i_done_date )
172 i_done_date = (i_done_date >= @date_to ? @date_to : i_done_date )
170 i_done_date = (i_done_date >= @date_to ? @date_to : i_done_date )
173
171
174 i_late_date = [i_end_date, Date.today].min if i_start_date < Date.today
172 i_late_date = [i_end_date, Date.today].min if i_start_date < Date.today
175
173
176 i_left = ((i_start_date - @date_from)*zoom).floor
174 i_left = ((i_start_date - @date_from)*zoom).floor
177 i_width = ((i_end_date - i_start_date + 1)*zoom).floor - 2 # total width of the issue (- 2 for left and right borders)
175 i_width = ((i_end_date - i_start_date + 1)*zoom).floor - 2 # total width of the issue (- 2 for left and right borders)
178 d_width = ((i_done_date - i_start_date)*zoom).floor - 2 # done width
176 d_width = ((i_done_date - i_start_date)*zoom).floor - 2 # done width
179 l_width = i_late_date ? ((i_late_date - i_start_date+1)*zoom).floor - 2 : 0 # delay width
177 l_width = i_late_date ? ((i_late_date - i_start_date+1)*zoom).floor - 2 : 0 # delay width
180 %>
178 %>
181 <div style="top:<%= top %>px;left:<%= i_left %>px;width:<%= i_width %>px;" class="task task_todo">&nbsp;</div>
179 <div style="top:<%= top %>px;left:<%= i_left %>px;width:<%= i_width %>px;" class="task task_todo">&nbsp;</div>
182 <% if l_width > 0 %>
180 <% if l_width > 0 %>
183 <div style="top:<%= top %>px;left:<%= i_left %>px;width:<%= l_width %>px;" class="task task_late">&nbsp;</div>
181 <div style="top:<%= top %>px;left:<%= i_left %>px;width:<%= l_width %>px;" class="task task_late">&nbsp;</div>
184 <% end %>
182 <% end %>
185 <% if d_width > 0 %>
183 <% if d_width > 0 %>
186 <div style="top:<%= top %>px;left:<%= i_left %>px;width:<%= d_width %>px;" class="task task_done">&nbsp;</div>
184 <div style="top:<%= top %>px;left:<%= i_left %>px;width:<%= d_width %>px;" class="task task_done">&nbsp;</div>
187 <% end %>
185 <% end %>
188 <div style="top:<%= top %>px;left:<%= i_left + i_width + 5 %>px;background:#fff;" class="task">
186 <div style="top:<%= top %>px;left:<%= i_left + i_width + 5 %>px;background:#fff;" class="task">
189 <%= i.status.name %>
187 <%= i.status.name %>
190 <%= (i.done_ratio).to_i %>%
188 <%= (i.done_ratio).to_i %>%
191 </div>
189 </div>
192 <% # === tooltip === %>
190 <% # === tooltip === %>
193 <div class="tooltip" style="position: absolute;top:<%= top %>px;left:<%= i_left %>px;width:<%= i_width %>px;height:12px;">
191 <div class="tooltip" style="position: absolute;top:<%= top %>px;left:<%= i_left %>px;width:<%= i_width %>px;height:12px;">
194 <span class="tip">
192 <span class="tip">
195 <%= render_issue_tooltip i %>
193 <%= render_issue_tooltip i %>
196 </span></div>
194 </span></div>
197 <% else
195 <% else
198 i_left = ((i.start_date - @date_from)*zoom).floor
196 i_left = ((i.start_date - @date_from)*zoom).floor
199 %>
197 %>
200 <div style="top:<%= top %>px;left:<%= i_left %>px;width:15px;" class="task milestone">&nbsp;</div>
198 <div style="top:<%= top %>px;left:<%= i_left %>px;width:15px;" class="task milestone">&nbsp;</div>
201 <div style="top:<%= top %>px;left:<%= i_left + 12 %>px;background:#fff;" class="task">
199 <div style="top:<%= top %>px;left:<%= i_left + 12 %>px;background:#fff;" class="task">
202 <strong><%= i.name %></strong>
200 <strong><%= i.name %></strong>
203 </div>
201 </div>
204 <% end %>
202 <% end %>
205 <% top = top + 20
203 <% top = top + 20
206 end %>
204 end %>
207
205
208 <% end # cache
209 %>
210
211 <%
206 <%
212 #
207 #
213 # Today red line (excluded from cache)
208 # Today red line (excluded from cache)
214 #
209 #
215 if Date.today >= @date_from and Date.today <= @date_to %>
210 if Date.today >= @date_from and Date.today <= @date_to %>
216 <div style="position: absolute;height:<%= g_height %>px;top:<%= headers_height + 1 %>px;left:<%= ((Date.today-@date_from+1)*zoom).floor()-1 %>px;width:10px;border-left: 1px dashed red;">&nbsp;</div>
211 <div style="position: absolute;height:<%= g_height %>px;top:<%= headers_height + 1 %>px;left:<%= ((Date.today-@date_from+1)*zoom).floor()-1 %>px;width:10px;border-left: 1px dashed red;">&nbsp;</div>
217 <% end %>
212 <% end %>
218
213
219 </div>
214 </div>
220 </td>
215 </td>
221 </tr>
216 </tr>
222 </table>
217 </table>
223
218
224 <table width="100%">
219 <table width="100%">
225 <tr>
220 <tr>
226 <td align="left"><%= link_to ('&#171; ' + l(:label_previous)), :year => (@date_from << @months).year, :month => (@date_from << @months).month, :zoom => @zoom, :months => @months, :tracker_ids => @selected_tracker_ids, :with_subprojects => params[:with_subprojects] %></td>
221 <td align="left"><%= link_to ('&#171; ' + l(:label_previous)), :year => (@date_from << @months).year, :month => (@date_from << @months).month, :zoom => @zoom, :months => @months, :tracker_ids => @selected_tracker_ids, :with_subprojects => params[:with_subprojects] %></td>
227 <td align="right"><%= link_to (l(:label_next) + ' &#187;'), :year => (@date_from >> @months).year, :month => (@date_from >> @months).month, :zoom => @zoom, :months => @months, :tracker_ids => @selected_tracker_ids, :with_subprojects => params[:with_subprojects] %></td>
222 <td align="right"><%= link_to (l(:label_next) + ' &#187;'), :year => (@date_from >> @months).year, :month => (@date_from >> @months).month, :zoom => @zoom, :months => @months, :tracker_ids => @selected_tracker_ids, :with_subprojects => params[:with_subprojects] %></td>
228 </tr>
223 </tr>
229 </table>
224 </table>
230
225
231 <div class="contextual"><%= l(:label_export_to) %>
226 <div class="contextual"><%= l(:label_export_to) %>
232 <%= link_to 'PDF', {:zoom => @zoom, :year => @year_from, :month => @month_from, :months => @months, :tracker_ids => @selected_tracker_ids, :with_subprojects => params[:with_subprojects], :format => 'pdf'}, :class => 'icon icon-pdf' %>
227 <%= link_to 'PDF', {:zoom => @zoom, :year => @year_from, :month => @month_from, :months => @months, :tracker_ids => @selected_tracker_ids, :with_subprojects => params[:with_subprojects], :format => 'pdf'}, :class => 'icon icon-pdf' %>
233 <%= link_to 'PNG', {:zoom => @zoom, :year => @year_from, :month => @month_from, :months => @months, :tracker_ids => @selected_tracker_ids, :with_subprojects => params[:with_subprojects], :format => 'png'}, :class => 'icon icon-image' if respond_to?('gantt_image') %>
228 <%= link_to 'PNG', {:zoom => @zoom, :year => @year_from, :month => @month_from, :months => @months, :tracker_ids => @selected_tracker_ids, :with_subprojects => params[:with_subprojects], :format => 'png'}, :class => 'icon icon-image' if respond_to?('gantt_image') %>
234 </div>
229 </div>
235
230
236 <% content_for :sidebar do %>
231 <% content_for :sidebar do %>
237 <h3><%= l(:label_gantt) %></h3>
232 <h3><%= l(:label_gantt) %></h3>
238 <% form_tag(params.merge(:tracker_ids => nil, :with_subprojects => nil)) do %>
233 <% form_tag(params.merge(:tracker_ids => nil, :with_subprojects => nil)) do %>
239 <% @trackers.each do |tracker| %>
234 <% @trackers.each do |tracker| %>
240 <label><%= check_box_tag "tracker_ids[]", tracker.id, (@selected_tracker_ids.include? tracker.id.to_s) %> <%= tracker.name %></label><br />
235 <label><%= check_box_tag "tracker_ids[]", tracker.id, (@selected_tracker_ids.include? tracker.id.to_s) %> <%= tracker.name %></label><br />
241 <% end %>
236 <% end %>
242 <% if @project.active_children.any? %>
237 <% if @project.active_children.any? %>
243 <br /><label><%= check_box_tag "with_subprojects", 1, params[:with_subprojects] %> <%=l(:label_subproject_plural)%></label>
238 <br /><label><%= check_box_tag "with_subprojects", 1, params[:with_subprojects] %> <%=l(:label_subproject_plural)%></label>
244 <% end %>
239 <% end %>
245 <p><%= submit_tag l(:button_apply), :class => 'button-small' %></p>
240 <p><%= submit_tag l(:button_apply), :class => 'button-small' %></p>
246 <% end %>
241 <% end %>
247 <% end %>
242 <% end %>
248
243
249 <% html_title(l(:label_gantt)) -%>
244 <% html_title(l(:label_gantt)) -%>
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now