ファーエンドテクノロジーさんの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
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を参考に修正)
<%= 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に引数が必要。(チケット一覧などと同じ表記に)
<%= context_menu issues_context_menu_path %>
■右クリックした時に、hascontextmenuクラスが見つからずリターンされる。(Ver3.4.0を参考に修正)
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を参考に修正)
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 \*----------------------------------------*/
■カスタムクエリの日本語がないので、登録
label_gantt_assigned_to_names: 担当者