3
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Redmine ガントチャート用パッチをVer3.3.2へ適用する

Last updated at Posted at 2018-05-02

ファーエンドテクノロジーさんのRedmine ガントチャート用パッチを入れようと思いました。

こちらの記事より。
・Redmineを改善するパッチを書いて、OSSへの貢献もする仕事
 https://www.farend.co.jp/blog/2017/08/redmine-development/

3項目のパッチ

①Feature #20481: ガントチャートの表示領域の幅をドラッグでリサイズ
http://www.redmine.org/issues/20481
②Feature #10485: ガントチャートでコンテキストメニューを表示
http://www.redmine.org/issues/10485#note-10
③Feature #26409: ガントチャートに担当者を表示
http://www.redmine.org/issues/26409#note-2

しかし、バージョン3.4.0では大丈夫でしたが、バージョン3.3.2でパッチがうまく動作しなかったので。

パッチを修正しました。

パッチはこちらです。
https://www.dropbox.com/s/1buqigmcnaxl0rp/gantt_patch_for_ver332.zip?dl=0

gantt_for_ver332.patch
diff -u -r redmine-3.3.2/app/models/issue_query.rb redmine-3.3.2_fix/app/models/issue_query.rb
--- redmine-3.3.2/app/models/issue_query.rb	2017-01-07 00:42:18.000000000 +0900
+++ redmine-3.3.2_fix/app/models/issue_query.rb	2018-05-02 16:44:49.000000000 +0900
@@ -120,10 +120,20 @@
     options[:draw_progress_line] = (arg == '1' ? '1' : nil)
   end
 
+  def draw_assigned_to_names
+    r = options[:draw_assigned_to_names]
+    r == '1'
+  end
+
+  def draw_assigned_to_names=(arg)
+    options[:draw_assigned_to_names] = (arg == '1' ? '1' : nil)
+  end
+
   def build_from_params(params)
     super
     self.draw_relations = params[:draw_relations] || (params[:query] && params[:query][:draw_relations])
     self.draw_progress_line = params[:draw_progress_line] || (params[:query] && params[:query][:draw_progress_line])
+    self.draw_assigned_to_names = params[:draw_assigned_to_names] || (params[:query] && params[:query][:draw_assigned_to_names])
     self
   end
 
diff -u -r redmine-3.3.2/app/views/gantts/show.html.erb redmine-3.3.2_fix/app/views/gantts/show.html.erb
--- redmine-3.3.2/app/views/gantts/show.html.erb	2017-01-07 00:42:18.000000000 +0900
+++ redmine-3.3.2_fix/app/views/gantts/show.html.erb	2018-05-02 17:32:34.000000000 +0900
@@ -49,6 +49,15 @@
             </label>
           </fieldset>
         </td>
+        <td>
+          <fieldset>
+            <legend><%= l(:field_assigned_to) %></legend>
+            <label for="draw_assigned_to_names">
+              <%= check_box 'query', 'draw_assigned_to_names', :id => 'draw_assigned_to_names' %>
+              <%= l(:label_display) %>
+            </label>
+          </fieldset>
+        </td>
       </tr>
     </table>
   </div>
@@ -86,6 +95,7 @@
 
   subject_width = 330
   header_height = 18
+  assigned_to_width = 100
 
   headers_height = header_height
   show_weeks = false
@@ -119,19 +129,18 @@
   <p class="warning"><%= l(:notice_gantt_chart_truncated, :max => @gantt.max_rows) %></p>
 <% end %>
 
-<table style="width:100%; border:0; border-collapse: collapse;">
+<table style="width:100%; border:0; border-collapse: collapse;", class="gantt_container">
 <tr>
-<td style="width:<%= subject_width %>px; padding:0px;">
+<td style="width:<%= subject_width %>px; padding:0px;" class="gantt_subjects_column">
   <%
     style  = ""
     style += "position:relative;"
     style += "height: #{t_height + 24}px;"
     style += "width: #{subject_width + 1}px;"
   %>
-  <%= content_tag(:div, :style => style) do %>
+  <%= content_tag(:div, :style => style, :class => "gantt_subjects_container") do %>
     <%
       style  = ""
-      style += "right:-2px;"
       style += "width: #{subject_width}px;"
       style += "height: #{headers_height}px;"
       style += 'background: #eee;'
@@ -139,7 +148,6 @@
     <%= content_tag(:div, "", :style => style, :class => "gantt_hdr") %>
     <%
       style  = ""
-      style += "right:-2px;"
       style += "width: #{subject_width}px;"
       style += "height: #{t_height}px;"
       style += 'border-left: 1px solid #c0c0c0;'
@@ -147,7 +155,38 @@
     %>
     <%= content_tag(:div, "", :style => style, :class => "gantt_hdr") %>
     <%= content_tag(:div, :class => "gantt_subjects") do %>
-      <%= @gantt.subjects.html_safe %>
+      <%= form_tag({}, :data => {:cm_url => issues_context_menu_path}) do -%>
+        <%= @gantt.subjects.html_safe %>
+      <% end %>    
+    <% end %>
+  <% end %>
+</td>
+
+<td style="width:<%= assigned_to_width %>px; padding:0px; display:none;" class="gantt_assigned_to_names_column">
+  <%
+    style  = ""
+    style += "position:relative;"
+    style += "height: #{t_height + 24}px;"
+    style += "width: #{assigned_to_width + 1}px;"
+  %>
+  <%= content_tag(:div, :style => style, :class => "gantt_assigned_to_names_container") do %>
+    <%
+      style  = ""
+      style += "width: #{assigned_to_width}px;"
+      style += "height: #{headers_height}px;"
+      style += 'background: #eee;'
+    %>
+    <%= content_tag(:div, "", :style => style, :class => "gantt_hdr") %>
+    <%
+      style  = ""
+      style += "width: #{assigned_to_width}px;"
+      style += "height: #{t_height}px;"
+      style += 'border-left: 1px solid #c0c0c0;'
+      style += 'overflow: hidden;'
+    %>
+    <%= content_tag(:div, "", :style => style, :class => "gantt_hdr") %>
+    <%= content_tag(:div, :class => "gantt_assigned_to_names") do %>
+      <%= @gantt.assigned_to_names.html_safe %>
     <% end %>
   <% end %>
 </td>
@@ -299,7 +338,9 @@
   <% end %>
 <% end %>
 
-<%= @gantt.lines.html_safe %>
+<%= form_tag({}, :data => {:cm_url => issues_context_menu_path}) do -%>
+  <%= @gantt.lines.html_safe %>
+<% end %>
 
 <% ###### Today red line (excluded from cache) ###### %>
 <% if User.current.today >= @gantt.date_from and User.current.today <= @gantt.date_to %>
@@ -363,10 +404,14 @@
 
 <%= javascript_tag do %>
   var issue_relation_type = <%= raw Redmine::Helpers::Gantt::DRAW_TYPES.to_json %>;
-  $(document).ready(drawGanttHandler);
-  $(window).resize(drawGanttHandler);
   $(function() {
-    $("#draw_relations").change(drawGanttHandler);
-    $("#draw_progress_line").change(drawGanttHandler);
+    drawGanttHandler();
+    resizableSubjectColumn();
+    $("#draw_relations, #draw_progress_line, #draw_assigned_to_names").change(drawGanttHandler);
+  });
+  $(window).resize(function() {
+    drawGanttHandler();
+    resizableSubjectColumn();
   });
 <% end %>
+<%= context_menu issues_context_menu_path %>
diff -u -r redmine-3.3.2/app/views/queries/_form.html.erb redmine-3.3.2_fix/app/views/queries/_form.html.erb
--- redmine-3.3.2/app/views/queries/_form.html.erb	2017-01-07 00:42:18.000000000 +0900
+++ redmine-3.3.2_fix/app/views/queries/_form.html.erb	2018-05-02 16:51:17.000000000 +0900
@@ -42,6 +42,7 @@
   <p><label><%= l(:button_show) %></label>
   <label class="inline"><%= check_box_tag "query[draw_relations]", "1", @query.draw_relations %> <%= l(:label_related_issues) %></label>
   <label class="inline"><%= check_box_tag "query[draw_progress_line]", "1", @query.draw_progress_line %> <%= l(:label_gantt_progress_line) %></label>
+  <label class="inline"><%= check_box_tag "query[draw_assigned_to_names]", "1", @query.draw_assigned_to_names %> <%= l(:label_gantt_assigned_to_names) %></label>
   </p>
 </fieldset>
 <% end %>
diff -u -r redmine-3.3.2/config/locales/ja.yml redmine-3.3.2_fix/config/locales/ja.yml
--- redmine-3.3.2/config/locales/ja.yml	2017-01-07 00:42:18.000000000 +0900
+++ redmine-3.3.2_fix/config/locales/ja.yml	2018-05-02 17:58:57.000000000 +0900
@@ -820,6 +820,7 @@
   label_parent_revision: 親
   label_child_revision: 子
   label_gantt_progress_line: イナズマ線
+  label_gantt_assigned_to_names: 担当者
 
   button_login: ログイン
   button_submit: 送信
diff -u -r redmine-3.3.2/lib/redmine/helpers/gantt.rb redmine-3.3.2_fix/lib/redmine/helpers/gantt.rb
--- redmine-3.3.2/lib/redmine/helpers/gantt.rb	2017-01-07 00:42:18.000000000 +0900
+++ redmine-3.3.2_fix/lib/redmine/helpers/gantt.rb	2018-05-02 16:55:51.000000000 +0900
@@ -76,6 +76,7 @@
         @date_to = (@date_from >> @months) - 1
         @subjects = ''
         @lines = ''
+        @assigned_to_names = ''
         @number_of_rows = nil
         @truncated = false
         if options.has_key?(:max_rows)
@@ -135,6 +136,12 @@
         @lines
       end
 
+      # Renders the assigned_to_names of the Gantt chart, the right side of subjects.
+      def assigned_to_names(options={})
+        render(options.merge(:only => :assigned_to_names)) unless @assigned_to_names_rendered
+        @assigned_to_names
+      end
+
       # Returns issues that will be rendered
       def issues
         @issues ||= @query.issues(
@@ -196,8 +203,9 @@
                    :indent_increment => 20, :render => :subject,
                    :format => :html}.merge(options)
         indent = options[:indent] || 4
-        @subjects = '' unless options[:only] == :lines
-        @lines = '' unless options[:only] == :subjects
+        @subjects = '' unless options[:only] == :lines || options[:only] == :assigned_to_names
+        @lines = '' unless options[:only] == :subjects || options[:only] == :assigned_to_names
+        @assigned_to_names = '' unless options[:only] == :lines || options[:only] == :subjects
         @number_of_rows = 0
         begin
           Project.project_tree(projects) do |project, level|
@@ -207,8 +215,9 @@
         rescue MaxLinesLimitReached
           @truncated = true
         end
-        @subjects_rendered = true unless options[:only] == :lines
-        @lines_rendered = true unless options[:only] == :subjects
+        @subjects_rendered = true unless options[:only] == :lines || options[:only] == :assigned_to_names
+        @lines_rendered = true unless options[:only] == :subjects || options[:only] == :assigned_to_names
+        @assigned_to_names_rendered = true unless options[:only] == :lines || options[:only] == :subjects
         render_end(options)
       end
 
@@ -254,8 +263,9 @@
 
       def render_object_row(object, options)
         class_name = object.class.name.downcase
-        send("subject_for_#{class_name}", object, options) unless options[:only] == :lines
-        send("line_for_#{class_name}", object, options) unless options[:only] == :subjects
+        send("subject_for_#{class_name}", object, options) unless options[:only] == :lines || options[:only] == :assigned_to_names
+        send("line_for_#{class_name}", object, options) unless options[:only] == :subjects || options[:only] == :assigned_to_names
+        assigned_to_name(object, options) unless options[:only] == :lines || options[:only] == :subjects
         options[:top] += options[:top_increment]
         @number_of_rows += 1
         if @max_rows && @number_of_rows >= @max_rows
@@ -320,6 +330,18 @@
         end
       end
 
+      def assigned_to_name(issue, options)
+        if issue.is_a?(Issue) && options[:format] == :html && issue.assigned_to.present?
+          style = "position: absolute;top: #{options[:top]}px;"
+          content = view.avatar(issue.assigned_to,
+                                 :class => 'gravatar icon-gravatar',
+                                 :size => 13) + view.link_to_user(issue.assigned_to)
+          assigned_to_name = view.content_tag(:div, content.html_safe, :style => style, :class => 'issue-assigned-name')
+          @assigned_to_names << assigned_to_name
+          assigned_to_name
+        end
+      end
+
       def subject(label, options, object=nil)
         send "#{options[:format]}_subject", options, label, object
       end
@@ -667,6 +689,7 @@
                              :title => assigned_string).to_s.html_safe
           end
           s << view.link_to_issue(issue).html_safe
+          s << view.content_tag(:input, nil, :type => 'checkbox', :name => 'ids[]', :value => issue.id, :style => 'display:none;', :class => 'toggle-selection')
           view.content_tag(:span, s, :class => css_classes).html_safe
         when Version
           version = object
@@ -701,7 +724,7 @@
         case object
         when Issue
           tag_options[:id] = "issue-#{object.id}"
-          tag_options[:class] = "issue-subject"
+          tag_options[:class] = "issue-subject hascontextmenu"
           tag_options[:title] = object.subject
         when Version
           tag_options[:id] = "version-#{object.id}"
@@ -838,6 +861,7 @@
           s = view.content_tag(:span,
                                view.render_issue_tooltip(object).html_safe,
                                :class => "tip")
+          s += view.content_tag(:input, nil, :type => 'checkbox', :name => 'ids[]', :value => object.id, :style => 'display:none;', :class => 'toggle-selection')          
           style = ""
           style << "position: absolute;"
           style << "top:#{params[:top]}px;"
@@ -846,7 +870,7 @@
           style << "height:12px;"
           output << view.content_tag(:div, s.html_safe,
                                      :style => style,
-                                     :class => "tooltip")
+                                     :class => "tooltip hascontextmenu")
         end
         @lines << output
         output
diff -u -r redmine-3.3.2/public/javascripts/context_menu.js redmine-3.3.2_fix/public/javascripts/context_menu.js
--- redmine-3.3.2/public/javascripts/context_menu.js	2017-01-07 00:42:18.000000000 +0900
+++ redmine-3.3.2_fix/public/javascripts/context_menu.js	2018-05-02 18:00:28.000000000 +0900
@@ -7,8 +7,8 @@
 function contextMenuRightClick(event) {
   var target = $(event.target);
   if (target.is('a')) {return;}
-  var tr = target.parents('tr').first();
-  if (!tr.hasClass('hascontextmenu')) {return;}
+  var tr = target.closest('.hascontextmenu').first();
+  if (tr.length < 1) {return;}
   event.preventDefault();
   if (!contextMenuIsSelected(tr)) {
     contextMenuUnselectAll();
diff -u -r redmine-3.3.2/public/javascripts/gantt.js redmine-3.3.2_fix/public/javascripts/gantt.js
--- redmine-3.3.2/public/javascripts/gantt.js	2017-01-07 00:42:18.000000000 +0900
+++ redmine-3.3.2_fix/public/javascripts/gantt.js	2018-05-02 16:58:22.000000000 +0900
@@ -161,6 +161,36 @@
   }
 }
 
+function drawAssignedToNames(){
+  if ($("#draw_assigned_to_names").prop('checked')){
+    $('td.gantt_assigned_to_names_column').show();
+    $('td.gantt_assigned_to_names_column').resizable({
+      alsoResize: ".gantt_assigned_to_names_container, .gantt_assigned_to_names_container>.gantt_hdr, .issue-assigned-name",
+      minWidth: 20,
+      handles: "e",
+      containment: "#content",
+      create: function( event, ui ) {
+        $(".ui-resizable-e").css("cursor","ew-resize");
+      }
+    }).on('resize', function (e) {
+        e.stopPropagation();
+    });
+    if(isMobile()) {
+      var width = Math.round($('.gantt_container').width()*0.15)
+      $('.gantt_assigned_to_names_container>.gantt_hdr').width(width-2);
+      $('.gantt_assigned_to_names_container, .gantt_assigned_to_names_column').width(width);
+      $('.issue-assigned-name').each(function(){
+        $(this).width($(".gantt_assigned_to_names_column").width());
+      });
+      $('td.gantt_assigned_to_names_column').resizable('disable');
+    }else{
+      $('td.gantt_assigned_to_names_column').resizable('enable');
+    };
+  }else{
+    $('td.gantt_assigned_to_names_column').hide();
+  }
+}
+
 function drawGanttHandler() {
   var folder = document.getElementById('gantt_draw_area');
   if(draw_gantt != null)
@@ -168,8 +198,31 @@
   else
     draw_gantt = Raphael(folder);
   setDrawArea();
+  drawAssignedToNames();
   if ($("#draw_progress_line").prop('checked'))
     drawGanttProgressLines();
   if ($("#draw_relations").prop('checked'))
     drawRelations();
 }
+
+function resizableSubjectColumn(){
+  $('.issue-subject, .project-name, .version-name').each(function(){
+    $(this).width($(".gantt_subjects_column").width()-$(this).position().left);
+  });
+  $('td.gantt_subjects_column').resizable({
+    alsoResize: '.gantt_subjects_container, .gantt_subjects_container>.gantt_hdr, .project-name, .issue-subject, .version-name',
+    minWidth: 100,
+    handles: 'e',
+    containment: '#content',
+    create: function( event, ui ) {
+      $('.ui-resizable-e').css('cursor','ew-resize');
+    }
+  }).on('resize', function (e) {
+      e.stopPropagation();
+  });
+  if(isMobile()) {
+    $('td.gantt_subjects_column').resizable('disable');
+  }else{
+    $('td.gantt_subjects_column').resizable('enable');
+  };
+}
diff -u -r redmine-3.3.2/public/stylesheets/application.css redmine-3.3.2_fix/public/stylesheets/application.css
--- redmine-3.3.2/public/stylesheets/application.css	2017-01-07 00:42:18.000000000 +0900
+++ redmine-3.3.2_fix/public/stylesheets/application.css	2018-05-02 17:00:50.000000000 +0900
@@ -1082,8 +1082,13 @@
 
 .gantt_hdr.nwday {background-color:#f1f1f1; color:#999;}
 
-.gantt_subjects { font-size: 0.8em; }
-.gantt_subjects div { line-height:16px;height:16px;overflow:hidden;white-space:nowrap;text-overflow: ellipsis; }
+.gantt_subjects, .issue-assigned-name { font-size: 0.8em; }
+.gantt_subjects div, .issue-assigned-name { line-height:16px;height:16px;overflow:hidden;white-space:nowrap;text-overflow: ellipsis; }
+.gantt_subjects div.issue-subject:hover { background-color:#ffffdd; }
+.issue-assigned-name { padding: 0px 3px; width: 100px; }
+.gantt_subjects .issue-subject img.icon-gravatar, .issue-assigned-name img.icon-gravatar {
+  margin: 2px 5px 0px 2px;
+}
 
 .task {
   position: absolute;
diff -u -r redmine-3.3.2/public/stylesheets/context_menu.css redmine-3.3.2_fix/public/stylesheets/context_menu.css
--- redmine-3.3.2/public/stylesheets/context_menu.css	2017-01-07 00:42:18.000000000 +0900
+++ redmine-3.3.2_fix/public/stylesheets/context_menu.css	2018-05-02 16:41:57.000000000 +0900
@@ -54,3 +54,6 @@
 .context-menu-selection { background-color:#507AAA !important; color:#f8f8f8 !important; }
 .context-menu-selection a, .context-menu-selection a:hover { color:#f8f8f8 !important; }
 .context-menu-selection:hover { background-color:#507AAA !important; color:#f8f8f8  !important; }
+div#gantt_area .context-menu-selection { background-color: rgba(80, 122, 170, 0.48) !important; }
+div#gantt_area .context-menu-selection:hover { background-color: rgba(80, 122, 170, 0.48) !important; }
+div#gantt_area .context-menu-selection a { color: #169 !important; }
diff -u -r redmine-3.3.2/public/stylesheets/responsive.css redmine-3.3.2_fix/public/stylesheets/responsive.css
--- redmine-3.3.2/public/stylesheets/responsive.css	2017-01-07 00:42:18.000000000 +0900
+++ redmine-3.3.2_fix/public/stylesheets/responsive.css	2018-05-02 17:06:16.000000000 +0900
@@ -676,6 +676,33 @@
     padding-top: 1em;
   }
 
+  /* Gantt charts */
+  /*
+   * [1] override inline styles with important
+   * [2] keep border between subjects and gantt area
+   * [3] remove whitespace between subjects and gantt area
+   * [4] maintain width due to [3]
+   */
+  .gantt_subjects_column {
+    width: 50% !important; /* [1] */
+  }
+  
+  .gantt_subjects_container {
+    width: 100% !important;
+    overflow: hidden;
+  }
+  
+  .gantt_subjects_column .gantt_hdr {
+    width: 100% !important;
+    border-right: 1px solid #c0c0c0; /* [2] */
+    right: 0 !important; /* [2] */
+  }
+  
+  #gantt_area {
+    left: -2px; /* [3] */
+    margin-right: -2px; /* [4] */
+  }  
+
   /*----------------------------------------*\
     G) FORMS
   \*----------------------------------------*/

パッチの当て方(Linux)

※ パッチかけられない方は、変更前後のソースも添付していますので、参考にしてください。

1)redmineルートディレクトリに、gantt_for_ver332.patch を保存

CONTRIBUTING.md  Rakefile      config      extra                  log       test
Gemfile          app           config.ru  files                   plugins   tmp
Gemfile.lock     appveyor.yml  db         gantt_for_ver332.patch  public
README.rdoc      bin           doc        lib                     script

2)$ sudo patch -p1 < gantt_for_ver332.patch を実行

patching file app/models/issue_query.rb
patching file app/views/gantts/show.html.erb
patching file app/views/queries/_form.html.erb
patching file config/locales/ja.yml
patching file lib/redmine/helpers/gantt.rb
patching file public/javascripts/context_menu.js
patching file public/javascripts/gantt.js
patching file public/stylesheets/application.css
patching file public/stylesheets/context_menu.css
patching file public/stylesheets/responsive.css

3)$ sudo rm gantt_for_ver332.patch でパッチを削除

修正したところ概略

※ 全体的に編集行のズレを修正しました。その他は、下記になります。

①ガントチャートをドラッグでリサイズ

■下記の様に、ガントチャート要素にクラスを追加。(Ver3.4.0を参考に修正)

/app/views/gantts/show.html.erb
<%= content_tag(:div, :style => style, :class => "gantt_subjects_container") do %>

<table style="width:100%; border:0; border-collapse: collapse;", class="gantt_container">

<td style="width:<%= subject_width %>px; padding:0px;" class="gantt_subjects_column">

②ガントチャートでコンテキストメニューを表示

■Ver3.3.2では、context_menuに引数が必要。(チケット一覧などと同じ表記に)

/app/views/gantts/show.html.erb
<%= context_menu issues_context_menu_path %>

■右クリックした時に、hascontextmenuクラスが見つからずリターンされる。(Ver3.4.0を参考に修正)

public/javascripts/context_menu.js
  var tr = target.parents('tr').first();
  if (!tr.hasClass('hascontextmenu')) {return;}

    

  var tr = target.closest('.hascontextmenu').first();
  if (tr.length < 1) {return;}

③ガントチャートに担当者を表示

■下記ガントcssの記述が元々なかったので、追記。
(Ver3.4.0を参考に修正)

/public/stylesheets/responsive.css
679   /* Gantt charts */
680   /*
681    * [1] override inline styles with important
682    * [2] keep border between subjects and gantt area
683    * [3] remove whitespace between subjects and gantt area
684    * [4] maintain width due to [3]
685    */
686   .gantt_subjects_column {
687     width: 50% !important; /* [1] */
688   }
689 
690   .gantt_subjects_container {
691     width: 100% !important;
692     overflow: hidden;
693   }
694 
695   .gantt_subjects_column .gantt_hdr {
696     width: 100% !important;
697     border-right: 1px solid #c0c0c0; /* [2] */
698     right: 0 !important; /* [2] */
699   }
700 
701   #gantt_area {
702     left: -2px; /* [3] */
703     margin-right: -2px; /* [4] */
704   }
705 
706   /*----------------------------------------*\
707     G) FORMS
708   \*----------------------------------------*/

■カスタムクエリの日本語がないので、登録

/redmine_co/config/locales/ja.yml
label_gantt_assigned_to_names: 担当者
3
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?