diff --git a/app/controllers/issues_controller.rb b/app/controllers/issues_controller.rb
index 7d572924f..43a04f26d 100644
--- a/app/controllers/issues_controller.rb
+++ b/app/controllers/issues_controller.rb
@@ -20,7 +20,7 @@ class IssuesController < ApplicationController
before_filter :find_issue, :only => [:show, :edit, :reply, :destroy_attachment]
before_filter :find_issues, :only => [:bulk_edit, :move, :destroy]
- before_filter :find_project, :only => [:new, :update_form, :preview]
+ before_filter :find_project, :only => [:new, :update_form, :preview, :gantt]
before_filter :authorize, :except => [:index, :changes, :preview, :update_form, :context_menu]
before_filter :find_optional_project, :only => [:index, :changes]
accept_key_auth :index, :changes
@@ -322,6 +322,38 @@ class IssuesController < ApplicationController
redirect_to :action => 'show', :id => @issue
end
+ def gantt
+ @gantt = Redmine::Helpers::Gantt.new(params)
+ retrieve_query
+ if @query.valid?
+ events = []
+ # Issues that have start and due dates
+ events += Issue.find(:all,
+ :order => "start_date, due_date",
+ :include => [:tracker, :status, :assigned_to, :priority, :project],
+ :conditions => ["(#{@query.statement}) AND (((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)", @gantt.date_from, @gantt.date_to, @gantt.date_from, @gantt.date_to, @gantt.date_from, @gantt.date_to]
+ )
+ # Issues that don't have a due date but that are assigned to a version with a date
+ events += Issue.find(:all,
+ :order => "start_date, effective_date",
+ :include => [:tracker, :status, :assigned_to, :priority, :project, :fixed_version],
+ :conditions => ["(#{@query.statement}) AND (((start_date>=? and start_date<=?) or (effective_date>=? and effective_date<=?) or (start_date and effective_date>?)) and start_date is not null and due_date is null and effective_date is not null)", @gantt.date_from, @gantt.date_to, @gantt.date_from, @gantt.date_to, @gantt.date_from, @gantt.date_to]
+ )
+ # Related versions
+ version_ids = events.collect(&:fixed_version_id).compact.uniq
+ events += Version.find_all_by_id(version_ids, :include => :project,
+ :conditions => ["effective_date BETWEEN ? AND ?", @gantt.date_from, @gantt.date_to]) unless version_ids.empty?
+
+ @gantt.events = events
+ end
+
+ respond_to do |format|
+ format.html { render :template => "issues/gantt.rhtml", :layout => !request.xhr? }
+ format.png { send_data(@gantt.to_image, :disposition => 'inline', :type => 'image/png', :filename => "#{@project.identifier}-gantt.png") } if @gantt.respond_to?('to_image')
+ format.pdf { send_data(render(:template => "issues/gantt.rfpdf", :layout => false), :type => 'application/pdf', :filename => "#{@project.identifier}-gantt.pdf") }
+ end
+ end
+
def context_menu
@issues = Issue.find_all_by_id(params[:ids], :include => :project)
if (@issues.size == 1)
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index 0d83d81b3..9e1df29a2 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -272,69 +272,6 @@ class ProjectsController < ApplicationController
@calendar.events = events
render :layout => false if request.xhr?
- end
-
- def gantt
- @trackers = @project.rolled_up_trackers
- retrieve_selected_tracker_ids(@trackers)
-
- if params[:year] and params[:year].to_i >0
- @year_from = params[:year].to_i
- if params[:month] and params[:month].to_i >=1 and params[:month].to_i <= 12
- @month_from = params[:month].to_i
- else
- @month_from = 1
- end
- else
- @month_from ||= Date.today.month
- @year_from ||= Date.today.year
- end
-
- zoom = (params[:zoom] || User.current.pref[:gantt_zoom]).to_i
- @zoom = (zoom > 0 && zoom < 5) ? zoom : 2
- months = (params[:months] || User.current.pref[:gantt_months]).to_i
- @months = (months > 0 && months < 25) ? months : 6
-
- # Save gantt paramters as user preference (zoom and months count)
- if (User.current.logged? && (@zoom != User.current.pref[:gantt_zoom] || @months != User.current.pref[:gantt_months]))
- User.current.pref[:gantt_zoom], User.current.pref[:gantt_months] = @zoom, @months
- User.current.preference.save
- end
-
- @date_from = Date.civil(@year_from, @month_from, 1)
- @date_to = (@date_from >> @months) - 1
- @with_subprojects = params[:with_subprojects].nil? ? Setting.display_subprojects_issues? : (params[:with_subprojects] == '1')
-
- @events = []
- @project.issues_with_subprojects(@with_subprojects) do
- # Issues that have start and due dates
- @events += Issue.find(:all,
- :order => "start_date, due_date",
- :include => [:tracker, :status, :assigned_to, :priority, :project],
- :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]
- ) unless @selected_tracker_ids.empty?
- # Issues that don't have a due date but that are assigned to a version with a date
- @events += Issue.find(:all,
- :order => "start_date, effective_date",
- :include => [:tracker, :status, :assigned_to, :priority, :project, :fixed_version],
- :conditions => ["(((start_date>=? and start_date<=?) or (effective_date>=? and effective_date<=?) or (start_date and effective_date>?)) and start_date is not null and due_date is null and effective_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]
- ) unless @selected_tracker_ids.empty?
- @events += Version.find(:all, :include => :project,
- :conditions => ["effective_date BETWEEN ? AND ?", @date_from, @date_to])
- end
- @events.sort! {|x,y| x.start_date <=> y.start_date }
-
- if params[:format]=='pdf'
- @options_for_rfpdf ||= {}
- @options_for_rfpdf[:file_name] = "#{@project.identifier}-gantt.pdf"
- render :template => "projects/gantt.rfpdf", :layout => false
- elsif params[:format]=='png' && respond_to?('gantt_image')
- image = gantt_image(@events, @date_from, @months, @zoom)
- image.format = 'PNG'
- send_data(image.to_blob, :disposition => 'inline', :type => 'image/png', :filename => "#{@project.identifier}-gantt.png")
- else
- render :template => "projects/gantt.rhtml"
- end
end
private
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index 912482f1c..cd2e743fe 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -45,154 +45,4 @@ module ProjectsHelper
]
tabs.select {|tab| User.current.allowed_to?(tab[:action], @project)}
end
-
- # Generates a gantt image
- # Only defined if RMagick is avalaible
- def gantt_image(events, date_from, months, zoom)
- date_to = (date_from >> months)-1
- show_weeks = zoom > 1
- show_days = zoom > 2
-
- subject_width = 320
- header_heigth = 18
- # width of one day in pixels
- zoom = zoom*2
- g_width = (date_to - date_from + 1)*zoom
- g_height = 20 * events.length + 20
- headers_heigth = (show_weeks ? 2*header_heigth : header_heigth)
- height = g_height + headers_heigth
-
- imgl = Magick::ImageList.new
- imgl.new_image(subject_width+g_width+1, height)
- gc = Magick::Draw.new
-
- # Subjects
- top = headers_heigth + 20
- gc.fill('black')
- gc.stroke('transparent')
- gc.stroke_width(1)
- events.each do |i|
- gc.text(4, top + 2, (i.is_a?(Issue) ? i.subject : i.name))
- top = top + 20
- end
-
- # Months headers
- month_f = date_from
- left = subject_width
- months.times do
- width = ((month_f >> 1) - month_f) * zoom
- gc.fill('white')
- gc.stroke('grey')
- gc.stroke_width(1)
- gc.rectangle(left, 0, left + width, height)
- gc.fill('black')
- gc.stroke('transparent')
- gc.stroke_width(1)
- gc.text(left.round + 8, 14, "#{month_f.year}-#{month_f.month}")
- left = left + width
- month_f = month_f >> 1
- end
-
- # Weeks headers
- if show_weeks
- left = subject_width
- height = header_heigth
- if date_from.cwday == 1
- # date_from is monday
- week_f = date_from
- else
- # find next monday after date_from
- week_f = date_from + (7 - date_from.cwday + 1)
- width = (7 - date_from.cwday + 1) * zoom
- gc.fill('white')
- gc.stroke('grey')
- gc.stroke_width(1)
- gc.rectangle(left, header_heigth, left + width, 2*header_heigth + g_height-1)
- left = left + width
- end
- while week_f <= date_to
- width = (week_f + 6 <= date_to) ? 7 * zoom : (date_to - week_f + 1) * zoom
- gc.fill('white')
- gc.stroke('grey')
- gc.stroke_width(1)
- gc.rectangle(left.round, header_heigth, left.round + width, 2*header_heigth + g_height-1)
- gc.fill('black')
- gc.stroke('transparent')
- gc.stroke_width(1)
- gc.text(left.round + 2, header_heigth + 14, week_f.cweek.to_s)
- left = left + width
- week_f = week_f+7
- end
- end
-
- # Days details (week-end in grey)
- if show_days
- left = subject_width
- height = g_height + header_heigth - 1
- wday = date_from.cwday
- (date_to - date_from + 1).to_i.times do
- width = zoom
- gc.fill(wday == 6 || wday == 7 ? '#eee' : 'white')
- gc.stroke('grey')
- gc.stroke_width(1)
- gc.rectangle(left, 2*header_heigth, left + width, 2*header_heigth + g_height-1)
- left = left + width
- wday = wday + 1
- wday = 1 if wday > 7
- end
- end
-
- # border
- gc.fill('transparent')
- gc.stroke('grey')
- gc.stroke_width(1)
- gc.rectangle(0, 0, subject_width+g_width, headers_heigth)
- gc.stroke('black')
- gc.rectangle(0, 0, subject_width+g_width, g_height+ headers_heigth-1)
-
- # content
- top = headers_heigth + 20
- gc.stroke('transparent')
- events.each do |i|
- if i.is_a?(Issue)
- i_start_date = (i.start_date >= date_from ? i.start_date : date_from )
- i_end_date = (i.due_date <= date_to ? i.due_date : date_to )
- i_done_date = i.start_date + ((i.due_date - i.start_date+1)*i.done_ratio/100).floor
- i_done_date = (i_done_date <= date_from ? date_from : i_done_date )
- i_done_date = (i_done_date >= date_to ? date_to : i_done_date )
- i_late_date = [i_end_date, Date.today].min if i_start_date < Date.today
-
- i_left = subject_width + ((i_start_date - date_from)*zoom).floor
- i_width = ((i_end_date - i_start_date + 1)*zoom).floor # total width of the issue
- d_width = ((i_done_date - i_start_date)*zoom).floor # done width
- l_width = i_late_date ? ((i_late_date - i_start_date+1)*zoom).floor : 0 # delay width
-
- gc.fill('grey')
- gc.rectangle(i_left, top, i_left + i_width, top - 6)
- gc.fill('red')
- gc.rectangle(i_left, top, i_left + l_width, top - 6) if l_width > 0
- gc.fill('blue')
- gc.rectangle(i_left, top, i_left + d_width, top - 6) if d_width > 0
- gc.fill('black')
- gc.text(i_left + i_width + 5,top + 1, "#{i.status.name} #{i.done_ratio}%")
- else
- i_left = subject_width + ((i.start_date - date_from)*zoom).floor
- gc.fill('green')
- gc.rectangle(i_left, top, i_left + 6, top - 6)
- gc.fill('black')
- gc.text(i_left + 11, top + 1, i.name)
- end
- top = top + 20
- end
-
- # today red line
- if Date.today >= date_from and Date.today <= date_to
- gc.stroke('red')
- x = (Date.today-date_from+1)*zoom + subject_width
- gc.line(x, headers_heigth, x, headers_heigth + g_height-1)
- end
-
- gc.draw(imgl)
- imgl
- end if Object.const_defined?(:Magick)
end
diff --git a/app/views/issues/_sidebar.rhtml b/app/views/issues/_sidebar.rhtml
index e94d4180b..bf9894939 100644
--- a/app/views/issues/_sidebar.rhtml
+++ b/app/views/issues/_sidebar.rhtml
@@ -3,12 +3,22 @@
<% if @project %>
<%= link_to l(:field_summary), :controller => 'reports', :action => 'issue_report', :id => @project %>
<%= link_to l(:label_change_log), :controller => 'projects', :action => 'changelog', :id => @project %>
+
+<% planning_links = []
+ planning_links << link_to_if_authorized(l(:label_calendar), :controller => 'projects', :action => 'calendar', :id => @project)
+ planning_links << link_to_if_authorized(l(:label_gantt), :action => 'gantt', :project_id => @project)
+ planning_links.compact!
+ unless planning_links.empty? %>
+
<%= planning_links.join(' | ') %>
+<% end %> + <% end %> <% unless sidebar_queries.empty? -%>+<%= if @gantt.zoom < 4 + link_to_remote image_tag('zoom_in.png'), {:url => @gantt.params.merge(:zoom => (@gantt.zoom+1)), :update => 'content'}, {:href => url_for(@gantt.params.merge(:zoom => (@gantt.zoom+1)))} + else + image_tag 'zoom_in_g.png' + end %> +<%= if @gantt.zoom > 1 + link_to_remote image_tag('zoom_out.png'), {:url => @gantt.params.merge(:zoom => (@gantt.zoom-1)), :update => 'content'}, {:href => url_for(@gantt.params.merge(:zoom => (@gantt.zoom-1)))} + else + image_tag 'zoom_out_g.png' + end %> +
+ + +<% end %> + +<%= error_messages_for 'query' %> +<% if @query.valid? %> <% zoom = 1 -@zoom.times { zoom = zoom * 2 } +@gantt.zoom.times { zoom = zoom * 2 } subject_width = 330 header_heigth = 18 @@ -8,56 +57,23 @@ headers_height = header_heigth show_weeks = false show_days = false -if @zoom >1 +if @gantt.zoom >1 show_weeks = true headers_height = 2*header_heigth - if @zoom > 2 + if @gantt.zoom > 2 show_days = true headers_height = 3*header_heigth end end -g_width = (@date_to - @date_from + 1)*zoom -g_height = [(20 * @events.length + 6)+150, 206].max +g_width = (@gantt.date_to - @gantt.date_from + 1)*zoom +g_height = [(20 * @gantt.events.length + 6)+150, 206].max t_height = g_height + headers_height %> -- - <%= l(:label_months_from) %> - <%= select_month(@month_from, :prefix => "month", :discard_type => true) %> - <%= select_year(@year_from, :prefix => "year", :discard_type => true) %> - <%= hidden_field_tag 'zoom', @zoom %> - <%= submit_tag l(:button_submit), :class => "button-small" %> - | - --<%= if @zoom < 4 - 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]} - else - image_tag 'zoom_in_g.png' - end %> -<%= if @zoom > 1 - 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]} - else - image_tag 'zoom_out_g.png' - end %> - | -
+ |
@@ -67,7 +83,7 @@ t_height = g_height + headers_height
# Tasks subjects
#
top = headers_height + 8
-@events.each do |i| %>
+@gantt.events.each do |i| %>
|