Index: app/controllers/gantts_controller.rb =================================================================== --- app/controllers/gantts_controller.rb (revision 3764) +++ app/controllers/gantts_controller.rb (working copy) @@ -1,5 +1,6 @@ class GanttsController < ApplicationController before_filter :find_optional_project + before_filter :find_issue, :only => [:edit_gantt] rescue_from Query::StatementInvalid, :with => :query_statement_invalid @@ -10,6 +11,7 @@ helper :sort include SortHelper include Redmine::Export::PDF + include IssuesHelper def show @gantt = Redmine::Helpers::Gantt.new(params) @@ -42,4 +44,82 @@ end end + def find_optional_project + @project = Project.find(params[:project_id]) unless params[:project_id].blank? + if params[:action] == "edit_gantt" + allowed = User.current.allowed_to?({:controller => 'issues', :action => 'edit'}, @project, :global => true) + else + allowed = User.current.allowed_to?({:controller => params[:controller], :action => params[:action]}, @project, :global => true) + end + allowed ? true : deny_access + rescue ActiveRecord::RecordNotFound + render_404 + end + + def edit_gantt + if !@issue.start_date || !@issue.due_before + render :text=>l(:notice_locking_conflict), :status=>400 + return + end + @issue.init_journal(User.current) + date_from = Date.parse(params[:date_from]) + old_start_date = @issue.start_date + o = get_position(@issue, date_from, Date.parse(params[:date_to]), params[:zoom]) + text_for_revert = "#{@issue.id}=#{format_date(@issue.start_date)},#{@issue.start_date},#{format_date(@issue.due_before)},#{@issue.due_before},#{o[0]},#{o[1]},#{o[2]},#{o[3]}" + + if params[:day] + #bar moved + day = params[:day].to_i + duration = @issue.due_before - @issue.start_date + @issue.start_date = date_from + day + @issue.due_date = @issue.start_date + duration.to_i if @issue.due_date + elsif params[:start_date] + #start date changed + start_date = Date.parse(params[:start_date]) + if @issue.start_date == start_date + render :text=>"" + return #nothing has changed + end + @issue.start_date = start_date + @issue.due_date = start_date if @issue.due_date && start_date > @issue.due_date + elsif params[:due_date] + #due date changed + due_date = Date.parse(params[:due_date]) + if @issue.due_date == due_date + render :text=>"" + return #nothing has changed + end + @issue.due_date = due_date + @issue.start_date = due_date if due_date < @issue.start_date + end + fv = @issue.fixed_version + if fv && fv.effective_date && !@issue.due_date && fv.effective_date < @issue.start_date + @issue.start_date = old_start_date + end + + begin + @issue.save! + o = get_position(@issue, date_from, Date.parse(params[:date_to]), params[:zoom]) + text = "#{@issue.id}=#{format_date(@issue.start_date)},#{@issue.start_date},#{format_date(@issue.due_before)},#{@issue.due_before},#{o[0]},#{o[1]},#{o[2]},#{o[3]}" + + #check dependencies + issues = @issue.all_precedes_issues + issues.each do |i| + o = get_position(i, date_from, Date.parse(params[:date_to]), params[:zoom]) + text += "|#{i.id}=#{format_date(i.start_date)},#{i.start_date},#{format_date(i.due_before)},#{i.due_before},#{o[0]},#{o[1]},#{o[2]},#{o[3]}" + end + render :text=>text + rescue + render :text=>@issue.errors.full_messages.join("\n") + "|" + text_for_revert , :status=>400 + end + end + +private + def find_issue + @issue = Issue.find(params[:id], :include => [:project, :tracker, :status, :author, :priority, :category]) + @project = @issue.project + rescue ActiveRecord::RecordNotFound + render_404 + end + end Index: app/helpers/issues_helper.rb =================================================================== --- app/helpers/issues_helper.rb (revision 3764) +++ app/helpers/issues_helper.rb (working copy) @@ -36,8 +36,8 @@ @cached_label_priority ||= l(:field_priority) link_to_issue(issue) + "

" + - "#{@cached_label_start_date}: #{format_date(issue.start_date)}
" + - "#{@cached_label_due_date}: #{format_date(issue.due_date)}
" + + "#{@cached_label_start_date}: #{format_date(issue.start_date)}
" + + "#{@cached_label_due_date}: #{format_date(issue.due_date)}
" + "#{@cached_label_assigned_to}: #{issue.assigned_to}
" + "#{@cached_label_priority}: #{issue.priority.name}" end @@ -259,4 +259,26 @@ end end end + + def get_position(event, date_from, date_to, zoom_str) + zoom = zoom_str.to_i + i_start_date = (event.start_date >= date_from ? event.start_date : date_from ) + i_end_date = (event.due_before <= date_to ? event.due_before : date_to ) + + i_done_date = event.start_date + ((event.due_before - event.start_date+1)*event.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 = ((i_start_date - date_from)*zoom).floor + i_left = 0 if i_left < 0 + i_width = ((i_end_date - i_start_date + 1)*zoom).floor - 2 # total width of the issue (- 2 for left and right borders) + i_width = 0 if i_width < 0 + d_width = ((i_done_date - i_start_date)*zoom).floor - 2 # done width + d_width = 0 if d_width < 0 + l_width = i_late_date ? ((i_late_date - i_start_date+1)*zoom).floor - 2 : 0 # delay width + l_width = 0 if l_width < 0 + return i_left, i_width, l_width, d_width + end end Index: app/models/issue.rb =================================================================== --- app/models/issue.rb (revision 3764) +++ app/models/issue.rb (working copy) @@ -415,6 +415,16 @@ end dependencies end + + def all_precedes_issues + dependencies = [] + relations_from.each do |relation| + next unless relation.relation_type == IssueRelation::TYPE_PRECEDES + dependencies << relation.issue_to + dependencies += relation.issue_to.all_dependent_issues + end + dependencies + end # Returns an array of issues that duplicate this one def duplicates Index: app/views/gantts/show.html.erb =================================================================== --- app/views/gantts/show.html.erb (revision 3764) +++ app/views/gantts/show.html.erb (working copy) @@ -1,3 +1,4 @@ +<% include_calendar_headers_tags %>

<%= l(:label_gantt) %>

<% form_tag(params.merge(:month => nil, :year => nil, :months => nil), :id => 'query_form') do %> @@ -38,7 +39,7 @@ <% zoom = 1 @gantt.zoom.times { zoom = zoom * 2 } -subject_width = 330 +subject_width = 280 header_heigth = 18 headers_height = header_heigth @@ -58,7 +59,106 @@ g_height = [(20 * @gantt.events.length + 6)+150, 206].max t_height = g_height + headers_height %> + + + + + +
@@ -88,9 +188,56 @@ end %> +
+
+
+<% +top = headers_height + 8 +@gantt.events.each do |i| %> +
+ <% if i.is_a? Issue %> + <%= format_date(i.start_date) %> + + <%= calendar_for("#{i.id}_start_date") if i.is_a? Issue %> + + "> + <%= format_date(i.due_before) %> + + + <%= calendar_for("#{i.id}_due_date") if i.due_date%> + + <% else %> + <%= format_date(i.start_date) %> + <% end %> +
+<% top = top + 20 +end %> +
+
-
+
 
<% # @@ -150,15 +297,17 @@ left = 0 height = g_height + header_heigth - 1 wday = @gantt.date_from.cwday + dt = @gantt.date_from (@gantt.date_to - @gantt.date_from + 1).to_i.times do width = zoom - 1 %>
5 %>" class="gantt_hdr"> - <%= day_name(wday).first %> + <%= "#{dt.day}
"if @gantt.zoom == 4 %><%= day_name(wday).first %>
<% left = left + width+1 wday = wday + 1 + dt = dt + 1 wday = 1 if wday > 7 end end %> @@ -169,37 +318,30 @@ # top = headers_height + 10 @gantt.events.each do |i| - if i.is_a? Issue - i_start_date = (i.start_date >= @gantt.date_from ? i.start_date : @gantt.date_from ) - i_end_date = (i.due_before <= @gantt.date_to ? i.due_before : @gantt.date_to ) - - i_done_date = i.start_date + ((i.due_before - i.start_date+1)*i.done_ratio/100).floor - i_done_date = (i_done_date <= @gantt.date_from ? @gantt.date_from : i_done_date ) - i_done_date = (i_done_date >= @gantt.date_to ? @gantt.date_to : i_done_date ) - - i_late_date = [i_end_date, Date.today].min if i_start_date < Date.today - - i_left = ((i_start_date - @gantt.date_from)*zoom).floor - i_width = ((i_end_date - i_start_date + 1)*zoom).floor - 2 # total width of the issue (- 2 for left and right borders) - d_width = ((i_done_date - i_start_date)*zoom).floor - 2 # done width - l_width = i_late_date ? ((i_late_date - i_start_date+1)*zoom).floor - 2 : 0 # delay width - css = "task " + (i.leaf? ? 'leaf' : 'parent') + if i.is_a? Issue + i_left, i_width, l_width, d_width = get_position(i, @gantt.date_from, @gantt.date_to, zoom) %> -
 
- <% if l_width > 0 %> -
 
- <% end %> - <% if d_width > 0 %> -
 
- <% end %> -
- <%= i.status.name %> - <%= (i.done_ratio).to_i %>% -
-
- +
+
 
+
 
+
 
+
+ <%= h(i.status.name) %> + <%= (i.done_ratio).to_i %>% + <% if !i.due_date && i.fixed_version %> + - <%= h(i.fixed_version.name) %> + <% end %> +
+
+ <%# === tooltip === %> +
+ <%= render_issue_tooltip i %>
+ <%= draggable_element("ev_#{i.id}", + :revert =>false, :scroll=>"'gantt-container'", :constraint => "'horizontal'", :snap=>zoom, + :onEnd=>"function( draggable, event ) {issue_moved(draggable.element);}" + ) %> <% else i_left = ((i.start_date - @gantt.date_from)*zoom).floor %> Index: config/routes.rb =================================================================== --- config/routes.rb (revision 3764) +++ config/routes.rb (working copy) @@ -111,6 +111,7 @@ issues_views.connect 'projects/:project_id/issues.:format', :action => 'index' issues_views.connect 'projects/:project_id/issues/new', :action => 'new' issues_views.connect 'projects/:project_id/issues/gantt', :controller => 'gantts', :action => 'show' + issues_views.connect 'projects/:project_id/issues/edit_gantt/:id', :controller => 'gantts', :action => 'edit_gantt', :id => /\d+/ issues_views.connect 'projects/:project_id/issues/calendar', :controller => 'calendars', :action => 'show' issues_views.connect 'projects/:project_id/issues/:copy_from/copy', :action => 'new' issues_views.connect 'issues/:id', :action => 'show', :id => /\d+/ @@ -122,6 +123,7 @@ issues_actions.connect 'issues', :action => 'index' issues_actions.connect 'projects/:project_id/issues', :action => 'create' issues_actions.connect 'projects/:project_id/issues/gantt', :controller => 'gantts', :action => 'show' + issues_actions.connect 'projects/:project_id/issues/edit_gantt:id', :controller => 'gantts', :action => 'edit_gantt', :id => /\d+/ issues_actions.connect 'projects/:project_id/issues/calendar', :controller => 'calendars', :action => 'show' issues_actions.connect 'issues/:id/quoted', :action => 'reply', :id => /\d+/ issues_actions.connect 'issues/:id/:action', :action => /edit|move|destroy/, :id => /\d+/ @@ -135,6 +137,7 @@ issues_actions.connect 'issues/:id.:format', :action => 'destroy', :id => /\d+/, :format => /xml/ end issues_routes.connect 'issues/gantt', :controller => 'gantts', :action => 'show' + issues_routes.connect 'issues/edit_gantt/:id', :controller => 'gantts', :action => 'edit_gantt', :id => /\d+/ issues_routes.connect 'issues/calendar', :controller => 'calendars', :action => 'show' issues_routes.connect 'issues/:action' end Index: public/stylesheets/application.css =================================================================== --- public/stylesheets/application.css (revision 3764) +++ public/stylesheets/application.css (working copy) @@ -788,6 +788,7 @@ .task_late { background:#f66 url(../images/task_late.png); border: 1px solid #f66; } .task_done { background:#66f url(../images/task_done.png); border: 1px solid #66f; } .task_todo { background:#aaa url(../images/task_todo.png); border: 1px solid #aaa; } +.task_none { background:transparent; border-style: none; } .task_todo.parent { background: #888; border: 1px solid #888; height: 6px;} .task_late.parent, .task_done.parent { height: 3px;}