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