From 9380721ea5ed24e260ab158ff48dd28fee11f679 Mon Sep 17 00:00:00 2001 From: Akiko Takano Date: Wed, 1 Jan 2020 21:31:45 +0900 Subject: [PATCH 01/30] Add column to store related (sample) link for the template. --- ...20200101204020_add_related_link_to_issue_templates.rb | 9 +++++++++ ...1204220_add_related_link_to_global_issue_templates.rb | 9 +++++++++ 2 files changed, 18 insertions(+) create mode 100644 db/migrate/20200101204020_add_related_link_to_issue_templates.rb create mode 100644 db/migrate/20200101204220_add_related_link_to_global_issue_templates.rb diff --git a/db/migrate/20200101204020_add_related_link_to_issue_templates.rb b/db/migrate/20200101204020_add_related_link_to_issue_templates.rb new file mode 100644 index 00000000..76abcbae --- /dev/null +++ b/db/migrate/20200101204020_add_related_link_to_issue_templates.rb @@ -0,0 +1,9 @@ +class AddRelatedLinkToIssueTemplates < ActiveRecord::Migration[5.2] + def self.up + add_column :issue_templates, :related_link, :text + end + + def self.down + remove_column :issue_templates, :related_link + end +end diff --git a/db/migrate/20200101204220_add_related_link_to_global_issue_templates.rb b/db/migrate/20200101204220_add_related_link_to_global_issue_templates.rb new file mode 100644 index 00000000..7b1e32c8 --- /dev/null +++ b/db/migrate/20200101204220_add_related_link_to_global_issue_templates.rb @@ -0,0 +1,9 @@ +class AddRelatedLinkToGlobalIssueTemplates < ActiveRecord::Migration[5.2] + def self.up + add_column :global_issue_templates, :related_link, :text + end + + def self.down + remove_column :global_issue_templates, :related_link + end +end From 5246148df50ef331eb287c712cbadf82b39fea52 Mon Sep 17 00:00:00 2001 From: Akiko Takano Date: Wed, 1 Jan 2020 22:35:50 +0900 Subject: [PATCH 02/30] Add basic validation and spec for new column. --- .rubocop_todo.yml | 10 ++------- app/models/concerns/issue_template/common.rb | 1 + app/models/global_issue_template.rb | 3 ++- app/models/issue_template.rb | 14 ++++++++++-- ...020_add_related_link_to_issue_templates.rb | 2 ++ ..._related_link_to_global_issue_templates.rb | 2 ++ spec/models/global_issue_template_spec.rb | 22 +++++++++++++++++++ spec/models/issue_template_spec.rb | 21 +++++++++++++++++- 8 files changed, 63 insertions(+), 12 deletions(-) create mode 100644 spec/models/global_issue_template_spec.rb diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 8d0b95ee..40d9dfd9 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -13,13 +13,13 @@ Metrics/AbcSize: # Offense count: 1 # Configuration parameters: CountComments. Metrics/ClassLength: - Max: 200 + Max: 200 Exclude: - 'spec/**/*' - 'test/**/*' # "Line is too long"を無効 -Metrics/LineLength: +Layout/LineLength: Enabled: false # Offense count: 1 @@ -51,9 +51,3 @@ EndOfLine: Metrics/ModuleLength: Max: 120 -# NOTE: Follow Redmine's model definition -Rails/ApplicationRecord: - Enabled: false - -Rails/InverseOf: - Enabled: false diff --git a/app/models/concerns/issue_template/common.rb b/app/models/concerns/issue_template/common.rb index 19159e65..b69b643b 100644 --- a/app/models/concerns/issue_template/common.rb +++ b/app/models/concerns/issue_template/common.rb @@ -17,6 +17,7 @@ module Common validates :title, presence: true validates :tracker, presence: true + validates :related_link, format: { with: URI::DEFAULT_PARSER.make_regexp }, allow_blank: true scope :enabled, -> { where(enabled: true) } scope :sorted, -> { order(:position) } diff --git a/app/models/global_issue_template.rb b/app/models/global_issue_template.rb index eaadc4c6..3c05f606 100644 --- a/app/models/global_issue_template.rb +++ b/app/models/global_issue_template.rb @@ -16,7 +16,8 @@ class GlobalIssueTemplate < ActiveRecord::Base 'project_ids', 'position', 'author_id', - 'checklist_json' + 'checklist_json', + 'related_link' # for intermediate table assosciations scope :search_by_project, lambda { |project_id| diff --git a/app/models/issue_template.rb b/app/models/issue_template.rb index 42355dc5..898d436a 100644 --- a/app/models/issue_template.rb +++ b/app/models/issue_template.rb @@ -7,8 +7,18 @@ class IssueTemplate < ActiveRecord::Base acts_as_positioned scope: %i[project_id tracker_id] # author and project should be stable. - safe_attributes 'title', 'description', 'tracker_id', 'note', 'enabled', 'issue_title', 'is_default', - 'enabled_sharing', 'visible_children', 'position', 'checklist_json' + safe_attributes 'title', + 'description', + 'tracker_id', + 'note', + 'enabled', + 'issue_title', + 'is_default', + 'enabled_sharing', + 'visible_children', + 'position', + 'checklist_json', + 'related_link' scope :enabled_sharing, -> { where(enabled_sharing: true) } scope :search_by_project, lambda { |prolect_id| diff --git a/db/migrate/20200101204020_add_related_link_to_issue_templates.rb b/db/migrate/20200101204020_add_related_link_to_issue_templates.rb index 76abcbae..034ffccf 100644 --- a/db/migrate/20200101204020_add_related_link_to_issue_templates.rb +++ b/db/migrate/20200101204020_add_related_link_to_issue_templates.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class AddRelatedLinkToIssueTemplates < ActiveRecord::Migration[5.2] def self.up add_column :issue_templates, :related_link, :text diff --git a/db/migrate/20200101204220_add_related_link_to_global_issue_templates.rb b/db/migrate/20200101204220_add_related_link_to_global_issue_templates.rb index 7b1e32c8..9ae6b09a 100644 --- a/db/migrate/20200101204220_add_related_link_to_global_issue_templates.rb +++ b/db/migrate/20200101204220_add_related_link_to_global_issue_templates.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class AddRelatedLinkToGlobalIssueTemplates < ActiveRecord::Migration[5.2] def self.up add_column :global_issue_templates, :related_link, :text diff --git a/spec/models/global_issue_template_spec.rb b/spec/models/global_issue_template_spec.rb new file mode 100644 index 00000000..bc014ebc --- /dev/null +++ b/spec/models/global_issue_template_spec.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +require_relative '../spec_helper' + +describe GlobalIssueTemplate do + describe '#valid?' do + let(:instance) { GlobalIssueTemplate.new(tracker_id: tracker.id, title: 'sample') } + let(:tracker) { FactoryBot.create(:tracker, :with_default_status) } + subject { instance.valid? } + + it 'related_link in invalid format' do + instance.related_link = 'non url format string' + is_expected.to be_falsey + expect(instance.errors.messages.key?(:related_link)).to be_truthy + end + + it 'related_link in valid format' do + instance.related_link = 'https://valid.example.com/links.html' + is_expected.to be_truthy + end + end +end diff --git a/spec/models/issue_template_spec.rb b/spec/models/issue_template_spec.rb index 6e670dc8..0b092e05 100644 --- a/spec/models/issue_template_spec.rb +++ b/spec/models/issue_template_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require_relative '../spec_helper' describe IssueTemplate do @@ -12,7 +14,8 @@ describe 'scope .orphaned' do subject { IssueTemplate.orphaned.count } before do - issue_template.update_attribute(:tracker_id, 0) + # Remove related tracker + issue_template.tracker.delete end it { is_expected.to eq 1 } end @@ -51,4 +54,20 @@ end end end + + describe '#valid?' do + let(:instance) { described_class.new(tracker_id: tracker.id, project_id: project.id, title: 'sample') } + subject { instance.valid? } + + it 'related_link in invalid format' do + instance.related_link = 'non url format string' + is_expected.to be_falsey + expect(instance.errors.messages.key?(:related_link)).to be_truthy + end + + it 'related_link in valid format' do + instance.related_link = 'https://valid.example.com/links.html' + is_expected.to be_truthy + end + end end From b1d6d641634a219877c2fd75a5df3efac4c7a73a Mon Sep 17 00:00:00 2001 From: Akiko Takano Date: Fri, 3 Jan 2020 12:30:48 +0900 Subject: [PATCH 03/30] Update related files and translations. --- .rubocop.yml | 3 +++ .rubocop_todo.yml | 1 - assets/stylesheets/issue_templates.css | 4 ++++ config/locales/en.yml | 4 ++++ config/locales/ja.yml | 4 ++++ .../global_issue_templates_controller_spec.rb | 11 +++++++++++ 6 files changed, 26 insertions(+), 1 deletion(-) diff --git a/.rubocop.yml b/.rubocop.yml index 803a6275..b389cd4c 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,3 +1,6 @@ inherit_from: .rubocop_todo.yml AllCops: TargetRubyVersion: 2.3 + Exclude: + - 'db/**/*' + diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 40d9dfd9..eeacf4ca 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -50,4 +50,3 @@ EndOfLine: Metrics/ModuleLength: Max: 120 - diff --git a/assets/stylesheets/issue_templates.css b/assets/stylesheets/issue_templates.css index 33f1a8a2..058daa5d 100644 --- a/assets/stylesheets/issue_templates.css +++ b/assets/stylesheets/issue_templates.css @@ -94,6 +94,10 @@ option.global { background: url("../images/issue_templates.png") no-repeat 3px center; } +.icon-hint { + background: url("../images/ticket.png") no-repeat 3px center; +} + a.template_tooltip { background-image: url("../images/preview.png"); background-repeat: no-repeat; diff --git a/config/locales/en.yml b/config/locales/en.yml index eb4fb9ed..17815fb8 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -74,3 +74,7 @@ en: please_select_at_least_one_role: "Please select at least one role." issue_templates_settings: Issue Templates Setting issue_templates_optional_settings: Templates Optional Settings + issue_template_related_link: Related Link + issue_template_link_title: Related Link Title + label_related_link_help_message: "If there are some example pages or sample issues which using issue template, please specify the link. So operator can see them as an usage or example for template." + label_link_title_help_message: "You can customize the title of the related link. (Default: Related Link)" diff --git a/config/locales/ja.yml b/config/locales/ja.yml index aa3d2e88..0df2d430 100644 --- a/config/locales/ja.yml +++ b/config/locales/ja.yml @@ -75,3 +75,7 @@ ja: please_select_at_least_one_role: 1つ以上のロールを指定してください。 issue_templates_settings: チケットテンプレート設定 issue_templates_optional_settings: テンプレートオプション設定 + issue_template_related_link: 関連リンク + issue_template_link_title: 関連リンクのタイトル + label_related_link_help_message: "テンプレートの利用例や、説明のページがあれば、関連リンクを設定できます。" + label_link_title_help_message: "関連リンクのタイトルは任意で設定できます(デフォルト: 関連リンク)" diff --git a/spec/controllers/global_issue_templates_controller_spec.rb b/spec/controllers/global_issue_templates_controller_spec.rb index 64342129..97937560 100644 --- a/spec/controllers/global_issue_templates_controller_spec.rb +++ b/spec/controllers/global_issue_templates_controller_spec.rb @@ -71,6 +71,17 @@ expect(global_issue_template.projects.count).to eq projects.count end end + + context 'POST with invalid url' do + let(:project_ids) { [] } + include_examples 'Right response', 302 + before do + create_params.merge!(related_link: 'bad format url') + end + it do + expect(global_issue_template.present?).to be_truthy + end + end end # PATCH GlobalIssueTemplatesController#edit From 0b849704e2ae386459e570c93e23f23802ba33d4 Mon Sep 17 00:00:00 2001 From: Akiko Takano Date: Fri, 3 Jan 2020 12:54:18 +0900 Subject: [PATCH 04/30] Add link_title and attribute. --- app/controllers/global_issue_templates_controller.rb | 4 +++- app/controllers/issue_templates_controller.rb | 5 ++++- app/models/concerns/issue_template/common.rb | 1 + app/models/global_issue_template.rb | 11 +++++++---- app/models/issue_template.rb | 9 ++++++--- ...0200102204815_add_link_title_to_issue_templates.rb | 11 +++++++++++ ...205044_add_link_title_to_global_issue_templates.rb | 11 +++++++++++ 7 files changed, 43 insertions(+), 9 deletions(-) create mode 100644 db/migrate/20200102204815_add_link_title_to_issue_templates.rb create mode 100644 db/migrate/20200102205044_add_link_title_to_global_issue_templates.rb diff --git a/app/controllers/global_issue_templates_controller.rb b/app/controllers/global_issue_templates_controller.rb index 11d4904d..b5994995 100644 --- a/app/controllers/global_issue_templates_controller.rb +++ b/app/controllers/global_issue_templates_controller.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # noinspection RubocopInspection class GlobalIssueTemplatesController < ApplicationController layout 'base' @@ -106,7 +108,7 @@ def save_and_flash(message, action_on_failure) def template_params params.require(:global_issue_template) .permit(:title, :tracker_id, :issue_title, :description, :note, :is_default, :enabled, - :author_id, :position, project_ids: [], checklists: []) + :author_id, :position, :related_link, :link_title, project_ids: [], checklists: []) end def render_form_params diff --git a/app/controllers/issue_templates_controller.rb b/app/controllers/issue_templates_controller.rb index ef220463..8b6d0c91 100644 --- a/app/controllers/issue_templates_controller.rb +++ b/app/controllers/issue_templates_controller.rb @@ -1,4 +1,5 @@ # frozen_string_literal: true + # noinspection ALL class IssueTemplatesController < ApplicationController layout 'base' @@ -194,7 +195,9 @@ def inherit_templates def template_params params.require(:issue_template).permit(:tracker_id, :title, :note, :issue_title, :description, :is_default, - :enabled, :author_id, :position, :enabled_sharing, checklists: []) + :enabled, :author_id, :position, :enabled_sharing, + :related_link, :link_title, + checklists: []) end def templates_exist? diff --git a/app/models/concerns/issue_template/common.rb b/app/models/concerns/issue_template/common.rb index b69b643b..d8605e0f 100644 --- a/app/models/concerns/issue_template/common.rb +++ b/app/models/concerns/issue_template/common.rb @@ -73,6 +73,7 @@ def template_json def generate_json result = attributes + result[:link_title] = link_title.presence || I18n.t(:issue_template_related_link, default: 'Related Link') result[:checklist] = checklist result.except('checklist_json') end diff --git a/app/models/global_issue_template.rb b/app/models/global_issue_template.rb index 3c05f606..37f8f7c0 100644 --- a/app/models/global_issue_template.rb +++ b/app/models/global_issue_template.rb @@ -1,10 +1,12 @@ +# frozen_string_literal: true + class GlobalIssueTemplate < ActiveRecord::Base include Redmine::SafeAttributes include Concerns::IssueTemplate::Common - validates_uniqueness_of :title, scope: :tracker_id + validates :title, uniqueness: { scope: :tracker_id } has_and_belongs_to_many :projects - acts_as_positioned :scope => [:tracker_id] + acts_as_positioned scope: [:tracker_id] safe_attributes 'title', 'description', @@ -17,7 +19,8 @@ class GlobalIssueTemplate < ActiveRecord::Base 'position', 'author_id', 'checklist_json', - 'related_link' + 'related_link', + 'link_title' # for intermediate table assosciations scope :search_by_project, lambda { |project_id| @@ -25,7 +28,7 @@ class GlobalIssueTemplate < ActiveRecord::Base } module Config - JSON_OBJECT_NAME = 'global_issue_template'.freeze + JSON_OBJECT_NAME = 'global_issue_template' end Config.freeze diff --git a/app/models/issue_template.rb b/app/models/issue_template.rb index 898d436a..8e7bdc31 100644 --- a/app/models/issue_template.rb +++ b/app/models/issue_template.rb @@ -1,9 +1,11 @@ +# frozen_string_literal: true + class IssueTemplate < ActiveRecord::Base include Redmine::SafeAttributes include Concerns::IssueTemplate::Common belongs_to :project validates :project_id, presence: true - validates_uniqueness_of :title, scope: :project_id + validates :title, uniqueness: { scope: :project_id } acts_as_positioned scope: %i[project_id tracker_id] # author and project should be stable. @@ -18,7 +20,8 @@ class IssueTemplate < ActiveRecord::Base 'visible_children', 'position', 'checklist_json', - 'related_link' + 'related_link', + 'link_title' scope :enabled_sharing, -> { where(enabled_sharing: true) } scope :search_by_project, lambda { |prolect_id| @@ -26,7 +29,7 @@ class IssueTemplate < ActiveRecord::Base } module Config - JSON_OBJECT_NAME = 'issue_template'.freeze + JSON_OBJECT_NAME = 'issue_template' end Config.freeze diff --git a/db/migrate/20200102204815_add_link_title_to_issue_templates.rb b/db/migrate/20200102204815_add_link_title_to_issue_templates.rb new file mode 100644 index 00000000..97539e9b --- /dev/null +++ b/db/migrate/20200102204815_add_link_title_to_issue_templates.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +class AddLinkTitleToIssueTemplates < ActiveRecord::Migration[5.2] + def self.up + add_column :issue_templates, :link_title, :text + end + + def self.down + remove_column :issue_templates, :link_title + end +end diff --git a/db/migrate/20200102205044_add_link_title_to_global_issue_templates.rb b/db/migrate/20200102205044_add_link_title_to_global_issue_templates.rb new file mode 100644 index 00000000..c5a34e8e --- /dev/null +++ b/db/migrate/20200102205044_add_link_title_to_global_issue_templates.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +class AddLinkTitleToGlobalIssueTemplates < ActiveRecord::Migration[5.2] + def self.up + add_column :global_issue_templates, :link_title, :text + end + + def self.down + remove_column :global_issue_templates, :link_title + end +end From 26fab1ddc3330b46e86ab05a6cef155d35559c75 Mon Sep 17 00:00:00 2001 From: Akiko Takano Date: Fri, 3 Jan 2020 13:28:01 +0900 Subject: [PATCH 05/30] Add form to input related_link and link_title. --- .../global_issue_templates/_form.html.erb | 29 +++++++++++++++++-- app/views/issue_templates/_form.html.erb | 29 +++++++++++++++++++ app/views/issue_templates/show.html.erb | 4 +++ 3 files changed, 59 insertions(+), 3 deletions(-) diff --git a/app/views/global_issue_templates/_form.html.erb b/app/views/global_issue_templates/_form.html.erb index 0f5dbfbe..789ad152 100644 --- a/app/views/global_issue_templates/_form.html.erb +++ b/app/views/global_issue_templates/_form.html.erb @@ -57,6 +57,27 @@ label: l(:issue_template_note), style: 'overflow:auto;' %>

+

<%= f.text_field :related_link, type: 'url', + size: 70, label: l(:issue_template_related_link, default: 'Related link') %> + + <%= l(:help_for_this_field) %> + + +

+ + +

<%= f.check_box :is_default, label: l(:field_is_default) %>

diff --git a/app/views/issue_templates/_issue_select_form.html.erb b/app/views/issue_templates/_issue_select_form.html.erb index de56c7f8..27b598f1 100644 --- a/app/views/issue_templates/_issue_select_form.html.erb +++ b/app/views/issue_templates/_issue_select_form.html.erb @@ -7,6 +7,7 @@ + <%=h l(:display_and_filter_issue_templates_in_dialog, default: 'Filter Templates') %> diff --git a/app/views/issue_templates/_list_templates.html.erb b/app/views/issue_templates/_list_templates.html.erb index a1160a91..a57f9170 100644 --- a/app/views/issue_templates/_list_templates.html.erb +++ b/app/views/issue_templates/_list_templates.html.erb @@ -22,6 +22,11 @@
<%= template.title %> <%= textilizable(template.description) %> + <% if template.related_link.present? %> +
+ <%= link_to template.link_title.present? ? template.link_title : l(:issue_template_related_link, default: 'Related link'), + template.related_link, target: '_blank', rel: 'nofollow noopener', class: 'external' %> + <% end %>
@@ -46,6 +51,11 @@
<%= template.title %> <%= textilizable(template.description) %> + <% if template.related_link.present? %> +
+ <%= link_to template.link_title.present? ? template.link_title : l(:issue_template_related_link, default: 'Related link'), + template.related_link, target: '_blank', rel: 'nofollow noopener', class: 'external' %> + <% end %>
@@ -70,6 +80,11 @@
<%= template.issue_title %> <%= textilizable(template.description) %> + <% if template.related_link.present? %> +
+ <%= link_to template.link_title.present? ? template.link_title : l(:issue_template_related_link, default: 'Related link'), + template.related_link, target: '_blank', rel: 'nofollow noopener', class: 'external' %> + <% end %>
diff --git a/app/views/issue_templates/index.html.erb b/app/views/issue_templates/index.html.erb index cdc8da1d..356d8480 100644 --- a/app/views/issue_templates/index.html.erb +++ b/app/views/issue_templates/index.html.erb @@ -61,6 +61,11 @@
<%= issue_template.title %> <%= textilizable(issue_template.description) %> + <% if issue_template.related_link.present? %> +
+ <%= link_to issue_template.link_title.present? ? issue_template.link_title : l(:issue_template_related_link, default: 'Related link'), + issue_template.related_link, target: '_blank', rel: 'nofollow noopener', class: 'external' %> + <% end %>
@@ -129,6 +134,11 @@
<%=h issue_template.title %> <%=h textilizable(issue_template.description) %> + <% if issue_template.related_link.present? %> +
+ <%= link_to issue_template.link_title.present? ? issue_template.link_title : l(:issue_template_related_link, default: 'Related link'), + issue_template.related_link, target: '_blank', rel: 'nofollow noopener', class: 'external' %> + <% end %>
@@ -140,7 +150,13 @@ -
<%=h textilizable(issue_template.description) %>
+
<%=h textilizable(issue_template.description) %> +
+ <%= link_to_if issue_template.related_link.present?, + issue_template.link_title.present? ? issue_template.link_title : l(:issue_template_related_link, default: 'Related link'), + issue_template.related_link, target: '_blank', rel: 'nofollow noopener', class: 'external' %> +
+ <% end %> @@ -201,6 +217,11 @@
<%=h issue_template.title %> <%=h textilizable(issue_template.description) %> + <% if issue_template.related_link.present? %> +
+ <%= link_to issue_template.link_title.present? ? issue_template.link_title : l(:issue_template_related_link, default: 'Related link'), + issue_template.related_link, target: '_blank', rel: 'nofollow noopener', class: 'external' %> + <% end %>
@@ -219,7 +240,14 @@ -
<%=h textilizable(issue_template.description) %>
+
<%=h textilizable(issue_template.description) %> + <% if issue_template.related_link.present? %> +
+ <%= link_to issue_template.link_title.present? ? issue_template.link_title : l(:issue_template_related_link, default: 'Related link'), + issue_template.related_link, target: '_blank', rel: 'nofollow noopener', class: 'external' %> + <% end %> +
+ <% end %> diff --git a/assets/javascripts/issue_templates.js b/assets/javascripts/issue_templates.js index 6c9355cc..c2a2a889 100644 --- a/assets/javascripts/issue_templates.js +++ b/assets/javascripts/issue_templates.js @@ -136,6 +136,16 @@ ISSUE_TEMPLATE.prototype = { if ($('#original_subject').text().length > 0 || $('#original_description').text().length > 0) { $('#revert_template').removeClass('disabled'); } + + if (obj.related_link !== '') { + let related_link = $('#issue_template_related_link'); + related_link.attr('href', obj.related_link); + related_link.css('display', 'inline'); + related_link.text(obj.link_title); + } else { + let related_link = $('#issue_template_related_link'); + related_link.css('display', 'none'); + } } } }); From b1b8270c0a8b07c725bbe7ccecb4a927cb9d8b5d Mon Sep 17 00:00:00 2001 From: Akiko Takano Date: Fri, 3 Jan 2020 14:35:12 +0900 Subject: [PATCH 07/30] Update test code. --- .../global_issue_templates_controller_spec.rb | 35 ++++++++++++++++--- .../issue_templates_controller_spec.rb | 4 +-- spec/features/issue_template_popup_spec.rb | 10 +++++- 3 files changed, 42 insertions(+), 7 deletions(-) diff --git a/spec/controllers/global_issue_templates_controller_spec.rb b/spec/controllers/global_issue_templates_controller_spec.rb index 97937560..35321119 100644 --- a/spec/controllers/global_issue_templates_controller_spec.rb +++ b/spec/controllers/global_issue_templates_controller_spec.rb @@ -74,12 +74,39 @@ context 'POST with invalid url' do let(:project_ids) { [] } - include_examples 'Right response', 302 - before do - create_params.merge!(related_link: 'bad format url') + let(:create_params) do + { global_issue_template: + { title: 'Global Template newtitle for creation test', + note: 'Global note for creation test', + description: 'Global Template description for creation test', + tracker_id: tracker.id, + enabled: 1, + author_id: user.id, + project_ids: project_ids }.merge(related_link: 'bad format url') } end + + include_examples 'Right response', 200 it do - expect(global_issue_template.present?).to be_truthy + expect(global_issue_template.present?).to be_falsy + end + + context 'POST with valid url' do + let(:project_ids) { [] } + let(:create_params) do + { global_issue_template: + { title: 'Global Template newtitle for creation test', + note: 'Global note for creation test', + description: 'Global Template description for creation test', + tracker_id: tracker.id, + enabled: 1, + author_id: user.id, + project_ids: project_ids }.merge(related_link: 'http://example.com/sample/index.html') } + end + + include_examples 'Right response', 302 + it do + expect(global_issue_template.present?).to be_truthy + end end end end diff --git a/spec/controllers/issue_templates_controller_spec.rb b/spec/controllers/issue_templates_controller_spec.rb index 4ed05a4b..e7b05a21 100644 --- a/spec/controllers/issue_templates_controller_spec.rb +++ b/spec/controllers/issue_templates_controller_spec.rb @@ -137,10 +137,10 @@ include_examples 'Right response', 200 # # TODO: This example should be request spec. - #it 'Render new form filled with copied template values' do + # it 'Render new form filled with copied template values' do # issue_template = assigns(:issue_template) # expect(issue_template.id).to be_nil # expect(issue_template.title).to eq "copy_of_#{original_template.title}" - #end + # end end end diff --git a/spec/features/issue_template_popup_spec.rb b/spec/features/issue_template_popup_spec.rb index 17561db6..9c0c5627 100644 --- a/spec/features/issue_template_popup_spec.rb +++ b/spec/features/issue_template_popup_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require_relative '../spec_helper' require_relative '../rails_helper' require_relative '../support/login_helper' @@ -26,8 +28,11 @@ given(:first_template) { IssueTemplate.first } given(:second_template) { IssueTemplate.second } + given(:related_link) { page.find('#issue_template_related_link') } + background do - FactoryBot.create_list(:issue_template, 2, project_id: project.id, tracker_id: tracker.id) + FactoryBot.create_list(:issue_template, 2, project_id: project.id, tracker_id: tracker.id, + related_link: 'http://example.com/template/wiki#usage') project.trackers << tracker assign_template_priv(role, add_permission: :show_issue_templates) @@ -50,6 +55,9 @@ expect(issue_description.value).not_to eq '' expect(issue_description.value).to eq first_template.description expect(issue_subject.value).to eq first_template.issue_title + + expect(related_link.text).to eq 'Related Link' + expect(related_link['href']).to eq 'http://example.com/template/wiki#usage' end context 'Has default template' do From 4dc1db68deb6f00043072200d3af92a2f0d33e6d Mon Sep 17 00:00:00 2001 From: Akiko Takano Date: Fri, 3 Jan 2020 21:48:32 +0900 Subject: [PATCH 08/30] Add migration: ready for supporting built-in fields. --- ...3213630_add_builtin_fields_json_to_issue_templates.rb | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 db/migrate/20200103213630_add_builtin_fields_json_to_issue_templates.rb diff --git a/db/migrate/20200103213630_add_builtin_fields_json_to_issue_templates.rb b/db/migrate/20200103213630_add_builtin_fields_json_to_issue_templates.rb new file mode 100644 index 00000000..b224ec69 --- /dev/null +++ b/db/migrate/20200103213630_add_builtin_fields_json_to_issue_templates.rb @@ -0,0 +1,9 @@ +class AddBuiltinFieldsJsonToIssueTemplates < ActiveRecord::Migration[5.2] + def self.up + add_column :issue_templates, :builtin_fields_json, :text + end + + def self.down + remove_column :issue_templates, :builtin_fields_json + end +end From 44c467fd510ae7160de193e2084fa98e30631a49 Mon Sep 17 00:00:00 2001 From: Akiko Takano Date: Sat, 4 Jan 2020 23:16:33 +0900 Subject: [PATCH 09/30] Enabled to store values through serialize. --- app/models/concerns/issue_template/common.rb | 2 + assets/javascripts/issue_templates.js | 712 ++++++++++--------- 2 files changed, 366 insertions(+), 348 deletions(-) diff --git a/app/models/concerns/issue_template/common.rb b/app/models/concerns/issue_template/common.rb index d8605e0f..ab501372 100644 --- a/app/models/concerns/issue_template/common.rb +++ b/app/models/concerns/issue_template/common.rb @@ -42,6 +42,8 @@ module Common after_destroy do |template| logger.info("[Destroy] #{self.class}: #{template.inspect}") end + + serialize :builtin_fields_json, Hash end # diff --git a/assets/javascripts/issue_templates.js b/assets/javascripts/issue_templates.js index c2a2a889..507d0454 100644 --- a/assets/javascripts/issue_templates.js +++ b/assets/javascripts/issue_templates.js @@ -4,386 +4,402 @@ */ // For namespace setting. -var ISSUE_TEMPLATE = ISSUE_TEMPLATE || function () {}; +var ISSUE_TEMPLATE = ISSUE_TEMPLATE || function () {} ISSUE_TEMPLATE.prototype = { - eraseSubjectAndDescription: function () { - $('#issue_description').val(''); - $('#issue_subject').val(''); - - try { - if (CKEDITOR.instances.issue_description) - CKEDITOR.instances.issue_description.setData(''); - } catch (e) { - // do nothing. - } - }, - openDialog: function (url, title) { - // Open dialog (modal window) to display selectable templates list. - $.ajax({ - url: url, - success: function (data) { - $("#filtered_templates_list").html(data); - $("#issue_template_dialog").dialog({ - modal: true, - dialogClass: "modal overflow_dialog", - draggable: true, - title: title, - minWidth: 400, - width: 'auto', - maxWidth: 'auto' - }); - } + eraseSubjectAndDescription: function () { + $('#issue_description').val('') + $('#issue_subject').val('') + + try { + if (CKEDITOR.instances.issue_description) + CKEDITOR.instances.issue_description.setData('') + } catch (e) { + // do nothing. + } + }, + openDialog: function (url, title) { + // Open dialog (modal window) to display selectable templates list. + $.ajax({ + url: url, + success: function (data) { + $("#filtered_templates_list").html(data); + $("#issue_template_dialog").dialog({ + modal: true, + dialogClass: "modal overflow_dialog", + draggable: true, + title: title, + minWidth: 400, + width: 'auto', + maxWidth: 'auto' }); - }, - revertAppliedTemplate: function () { + } + }); + }, + revertAppliedTemplate: function () { + var issue_subject = $('#issue_subject'); + var issue_description = $('#issue_description'); + var old_subject = $('#original_subject'); + var old_description = $('#original_description'); + var templateNS = this; + + issue_subject.val(templateNS.unescapeHTML(old_subject.text())); + issue_description.val(templateNS.unescapeHTML(old_description.text())); + + try { + if (CKEDITOR.instances.issue_description) + CKEDITOR.instances.issue_description.setData(templateNS.unescapeHTML(old_description.text())); + } catch (e) { + // do nothing. + } + old_description.text = ''; + old_description.text = ''; + $('#revert_template').addClass('disabled'); + }, + load_template: function (target_url, confirm_msg, should_replaced, + confirm_to_replace, confirmation, general_text_Yes, general_text_No) { + var selected_template = $('#issue_template'); + var ns = this; + if (selected_template.val() !== '') { + var template_type = ''; + if (selected_template.find('option:selected').hasClass('global')) { + template_type = 'global'; + } + $.ajax({ + url: target_url, + async: true, + type: 'post', + data: $.param({ + template_id: selected_template.val(), + template_type: template_type + }) + }).done(function (data) { + // NOTE: Workaround for GiHub Issue, to prevent overwrite with default template + // when operator submits new issue form without required field and returns + // with error message. If flash message #errorExplanation exists, not overwrited. + // (https://github.com/akiko-pusu/redmine_issue_templates/issues/50) + if ($('#errorExplanation')[0]) return; + + var oldSubj = ''; + var oldVal = ''; var issue_subject = $('#issue_subject'); var issue_description = $('#issue_description'); - var old_subject = $('#original_subject'); - var old_description = $('#original_description'); - var templateNS = this; - - issue_subject.val(templateNS.unescapeHTML(old_subject.text())); - issue_description.val(templateNS.unescapeHTML(old_description.text())); - - try { - if (CKEDITOR.instances.issue_description) - CKEDITOR.instances.issue_description.setData(templateNS.unescapeHTML(old_description.text())); - } catch (e) { - // do nothing. + + var template = JSON.parse(data); + + if (issue_description.val() !== '' && should_replaced === 'false') { + oldVal = issue_description.val() + '\n\n'; } - old_description.text = ''; - old_description.text = ''; - $('#revert_template').addClass('disabled'); - }, - load_template: function (target_url, confirm_msg, should_replaced, - confirm_to_replace, confirmation, general_text_Yes, general_text_No) { - var selected_template = $('#issue_template'); - var ns = this; - if (selected_template.val() !== '') { - var template_type = ''; - if (selected_template.find('option:selected').hasClass('global')) { - template_type = 'global'; - } - $.ajax({ - url: target_url, - async: true, - type: 'post', - data: $.param({ - template_id: selected_template.val(), - template_type: template_type - }) - }).done(function (data) { - // NOTE: Workaround for GiHub Issue, to prevent overwrite with default template - // when operator submits new issue form without required field and returns - // with error message. If flash message #errorExplanation exists, not overwrited. - // (https://github.com/akiko-pusu/redmine_issue_templates/issues/50) - if ($('#errorExplanation')[0]) return; - - var oldSubj = ''; - var oldVal = ''; - var issue_subject = $('#issue_subject'); - var issue_description = $('#issue_description'); - - var template = JSON.parse(data); - - if (issue_description.val() !== '' && should_replaced === 'false') { - oldVal = issue_description.val() + '\n\n'; - } - if (issue_subject.val() !== '' && should_replaced === 'false') { - oldSubj = issue_subject.val() + ' '; - } - $('#original_subject').text(ns.escapeHTML(issue_subject.val())); - $('#original_description').text(ns.escapeHTML(issue_description.val())); - - for (var issue_template in template) { - if ({}.hasOwnProperty.call(template, issue_template)) { - - var obj = template[issue_template]; - obj.description = (obj.description === null) ? '' : obj.description; - obj.issue_title = (obj.issue_title === null) ? '' : obj.issue_title; - - if (confirm_to_replace !== true && should_replaced === 'true' && (issue_description.val() !== '' || issue_subject.val() !== '')) { - if (oldVal !== obj.description || oldSubj !== obj.issue_title) { - var hide_confirm_flag = ns.hideOverwiteConfirm(); - if (hide_confirm_flag == false) { - ns.confirmToReplace(target_url, confirm_msg, should_replaced, confirmation, general_text_Yes, general_text_No); - return; - } - } - } - - issue_description.attr('original_description', $('
').text(issue_description.val()).html()); - issue_subject.attr('original_title', $('
').text(issue_subject.val()).html()); - - if (oldVal.replace(/(?:\r\n|\r|\n)/g, '').trim() != obj.description.replace(/(?:\r\n|\r|\n)/g, '').trim()) - issue_description.val(oldVal + obj.description); - if (oldSubj.trim() != obj.issue_title.trim()) - issue_subject.val(oldSubj + obj.issue_title); - - try { - if (CKEDITOR.instances.issue_description) - CKEDITOR.instances.issue_description.setData(oldVal + template[issue_template].description); - } catch (e) { - // do nothing. - } - // show message just after default template loaded. - if (confirm_msg) - ns.show_loaded_message(confirm_msg, issue_description); - ns.addCheckList(obj); - - if ($('#original_subject').text().length > 0 || $('#original_description').text().length > 0) { - $('#revert_template').removeClass('disabled'); - } - - if (obj.related_link !== '') { - let related_link = $('#issue_template_related_link'); - related_link.attr('href', obj.related_link); - related_link.css('display', 'inline'); - related_link.text(obj.link_title); - } else { - let related_link = $('#issue_template_related_link'); - related_link.css('display', 'none'); - } - } - } - }); + if (issue_subject.val() !== '' && should_replaced === 'false') { + oldSubj = issue_subject.val() + ' '; } - }, - confirmToReplace: function (target_url, confirm_msg, should_replaced, - confirmation, general_text_Yes, general_text_No) { - var ns = this; - $("#issue_template_confirm_to_replace_dialog").dialog({ - modal: true, - dialogClass: "modal overflow_dialog", - draggable: true, - title: confirmation, - width: 400, - buttons: [{ - text: general_text_Yes, - click: function () { - $(this).dialog("close"); - ns.load_template(target_url, confirm_msg, should_replaced, true, confirmation, general_text_Yes, general_text_No) - } - }, - { - text: general_text_No, - click: function () { - $(this).dialog("close"); - } + $('#original_subject').text(ns.escapeHTML(issue_subject.val())); + $('#original_description').text(ns.escapeHTML(issue_description.val())); + + for (var issue_template in template) { + if ({}.hasOwnProperty.call(template, issue_template)) { + + var obj = template[issue_template]; + obj.description = (obj.description === null) ? '' : obj.description; + obj.issue_title = (obj.issue_title === null) ? '' : obj.issue_title; + + if (confirm_to_replace !== true && should_replaced === 'true' && (issue_description.val() !== '' || issue_subject.val() !== '')) { + if (oldVal !== obj.description || oldSubj !== obj.issue_title) { + var hide_confirm_flag = ns.hideOverwiteConfirm(); + if (hide_confirm_flag == false) { + ns.confirmToReplace(target_url, confirm_msg, should_replaced, confirmation, general_text_Yes, general_text_No); + return; } - ] + } + } + + issue_description.attr('original_description', $('
').text(issue_description.val()).html()); + issue_subject.attr('original_title', $('
').text(issue_subject.val()).html()); + + if (oldVal.replace(/(?:\r\n|\r|\n)/g, '').trim() != obj.description.replace(/(?:\r\n|\r|\n)/g, '').trim()) + issue_description.val(oldVal + obj.description); + if (oldSubj.trim() != obj.issue_title.trim()) + issue_subject.val(oldSubj + obj.issue_title); + + try { + if (CKEDITOR.instances.issue_description) + CKEDITOR.instances.issue_description.setData(oldVal + template[issue_template].description); + } catch (e) { + // do nothing. + } + // show message just after default template loaded. + if (confirm_msg) + ns.show_loaded_message(confirm_msg, issue_description); + ns.addCheckList(obj); + + if ($('#original_subject').text().length > 0 || $('#original_description').text().length > 0) { + $('#revert_template').removeClass('disabled'); + } + + if (obj.related_link !== '') { + let related_link = $('#issue_template_related_link'); + related_link.attr('href', obj.related_link); + related_link.css('display', 'inline'); + related_link.text(obj.link_title); + } else { + let related_link = $('#issue_template_related_link'); + related_link.css('display', 'none'); + } + + ns.builtin_fields(obj) + } + } + }); + } + }, + confirmToReplace: function (target_url, confirm_msg, should_replaced, + confirmation, general_text_Yes, general_text_No) { + var ns = this; + $("#issue_template_confirm_to_replace_dialog").dialog({ + modal: true, + dialogClass: "modal overflow_dialog", + draggable: true, + title: confirmation, + width: 400, + buttons: [{ + text: general_text_Yes, + click: function () { + $(this).dialog("close"); + ns.load_template(target_url, confirm_msg, should_replaced, true, confirmation, general_text_Yes, general_text_No) + } + }, + { + text: general_text_No, + click: function () { + $(this).dialog("close"); + } + } + ] + }); + }, + show_loaded_message: function (confirm_msg, target) { + var template_status_area = $('#template_status-area'); + template_status_area.insertBefore(target); + template_status_area.issueTemplate('flash_message', { + text: confirm_msg, + how: 'append' + }); + }, + set_pulldown: function (tracker, target_url) { + var allow_overwrite = $('#allow_overwrite_description').prop('checked'); + $.ajax({ + url: target_url, + async: true, + type: 'post', + data: $.param({ + issue_tracker_id: tracker + }) + }).done(function (data) { + $('#issue_template').html(data); + $('#allow_overwrite_description').attr('checked', allow_overwrite); + }); + }, + addCheckList: function (obj) { + var list = obj.checklist; + if (list === undefined) return false; + if ($('#checklist_form').length === 0) return; + + // remove exists checklist items + var oldList = $('span.checklist-item.show:visible span.checklist-show-only.checklist-remove > a.icon.icon-del'); + oldList.each(function () { + oldList.click(); + }); + + for (var i = 0; i < list.length; i++) { + $('span.checklist-new.checklist-edit-box > input.edit-box').val(list[i]); + $("span.checklist-item.new > span.icon.icon-add.save-new-by-button").click(); + } + }, + escapeHTML: function (val) { + return $('
').text(val).html(); + }, + unescapeHTML: function (val) { + return $('
').html(val).text(); + }, + replaceCkeContent: function () { + return CKEDITOR.instances.issue_description.setData($('#issue_description').val()); + }, + hideOverwiteConfirm: function () { + var cookie_array = []; + if (document.cookie != '') { + var tmp = document.cookie.split('; '); + for (var i = 0; i < tmp.length; i++) { + var data = tmp[i].split('='); + cookie_array[data[0]] = decodeURIComponent(data[1]); + } + } + var confirmation_cookie = cookie_array['issue_template_confirm_to_replace_hide_dialog']; + if (confirmation_cookie == undefined || parseInt(confirmation_cookie) == 0) { + return false; + } + return true; + }, + // support built-in field update + builtin_fields: function (issue_template) { + let builtin_fields_json = issue_template.builtin_fields_json + if (builtin_fields_json === undefined) return false + Object.keys(builtin_fields_json).forEach(function (key) { + let value = builtin_fields_json[key] + let element = $('#' + key) + if (element.prop('tagName').toLowerCase() === 'select') { + $('#' + key + ' option:contains(' + value + ')').attr('selected', 'selected') + } else { + element.val(value) + } + }) + } +}; + +// jQuery plugin for issue template +(function ($) { + var methods = { + init: function (options) {}, + updateTemplateSelect: function (options) { + options = $.extend({ + target: '#issue_template', + template_id: 'data-issue-template-id' + }, options); + return $(this).each(function () { + $(this).click(function () { + var obj = $(options.target); + var id = $(this).attr(options.template_id); + obj.attr("selected", false); + // has template-global class? + if ($(this).hasClass('template-global')) { + obj.find('option[value="' + id + '"][class="global"]').prop('selected', true); + } else { + obj.val(id); + } + obj.trigger('change'); }); + }); }, - show_loaded_message: function (confirm_msg, target) { - var template_status_area = $('#template_status-area'); - template_status_area.insertBefore(target); - template_status_area.issueTemplate('flash_message', { - text: confirm_msg, - how: 'append' + displayTooltip: function (options) { + options = $.extend({ + tooltip_body_id: 'data-tooltip-content', + tooltip_target_id: 'data-tooltip-area' + }, options); + return $(this).each(function () { + $(this).hover(function () { + var content = $(this).attr(options.tooltip_body_id); + var target = $(this).attr(options.tooltip_target_id); + var obj = $(content); + if (obj.length) + $(target).html(obj); + obj.toggle(); }); + }); }, - set_pulldown: function (tracker, target_url) { - var allow_overwrite = $('#allow_overwrite_description').prop('checked'); - $.ajax({ - url: target_url, - async: true, - type: 'post', - data: $.param({ - issue_tracker_id: tracker - }) - }).done(function (data) { - $('#issue_template').html(data); - $('#allow_overwrite_description').attr('checked', allow_overwrite); + expandHelp: function (options) { + options = $.extend({ + attr_name: 'data-template-help-target' + }, options); + return $(this).each(function () { + $(this).click(function () { + var target = $(this).attr(options.attr_name); + var obj = $(target); + if (obj.length) + obj.toggle(); }); + }); }, - addCheckList: function (obj) { - var list = obj.checklist; - if (list === undefined) return false; - if ($('#checklist_form').length === 0) return; - - // remove exists checklist items - var oldList = $('span.checklist-item.show:visible span.checklist-show-only.checklist-remove > a.icon.icon-del'); - oldList.each(function () { - oldList.click(); + flash_message: function (options) { + // default + options = $.extend({ + text: 'Done', + time: 3000, + how: 'before', + class_name: '' + }, options); + + return $(this).each(function () { + if ($(this).parent().find('.flash_message').get(0)) return; + + var message = $('
', { + 'class': 'flash_message ' + options.class_name, + html: options.text + // display with fade in + }).hide().fadeIn('fast'); + + $(this)[options.how](message); + //delay and fadeout + message.delay(options.time).fadeOut('normal', function () { + $(this).remove(); }); - for (var i = 0; i < list.length; i++) { - $('span.checklist-new.checklist-edit-box > input.edit-box').val(list[i]); - $("span.checklist-item.new > span.icon.icon-add.save-new-by-button").click(); - } - }, - escapeHTML: function (val) { - return $('
').text(val).html(); - }, - unescapeHTML: function (val) { - return $('
').html(val).text(); - }, - replaceCkeContent: function () { - return CKEDITOR.instances.issue_description.setData($('#issue_description').val()); + }); }, - hideOverwiteConfirm: function () { - var cookie_array = []; - if (document.cookie != '') { - var tmp = document.cookie.split('; '); - for (var i = 0; i < tmp.length; i++) { - var data = tmp[i].split('='); - cookie_array[data[0]] = decodeURIComponent(data[1]); - } - } - var confirmation_cookie = cookie_array['issue_template_confirm_to_replace_hide_dialog']; - if (confirmation_cookie == undefined || parseInt(confirmation_cookie) == 0) { + disabled_link: function (options) { + options = $.extend({}, options); + return $(this).each(function () { + $(this).click(function (event) { + title = event.target.title; + if (title.length && event.target.hasAttribute('disabled')) { + event.stopPropagation(); + alert(title); return false; - } - return true; + } + }); + }); } -}; + }; -// jQuery plugin for issue template -(function ($) { - var methods = { - init: function (options) {}, - updateTemplateSelect: function (options) { - options = $.extend({ - target: '#issue_template', - template_id: 'data-issue-template-id' - }, options); - return $(this).each(function () { - $(this).click(function () { - var obj = $(options.target); - var id = $(this).attr(options.template_id); - obj.attr("selected", false); - // has template-global class? - if ($(this).hasClass('template-global')) { - obj.find('option[value="' + id + '"][class="global"]').prop('selected', true); - } else { - obj.val(id); - } - obj.trigger('change'); - }); - }); - }, - displayTooltip: function (options) { - options = $.extend({ - tooltip_body_id: 'data-tooltip-content', - tooltip_target_id: 'data-tooltip-area' - }, options); - return $(this).each(function () { - $(this).hover(function () { - var content = $(this).attr(options.tooltip_body_id); - var target = $(this).attr(options.tooltip_target_id); - var obj = $(content); - if (obj.length) - $(target).html(obj); - obj.toggle(); - }); - }); - }, - expandHelp: function (options) { - options = $.extend({ - attr_name: 'data-template-help-target' - }, options); - return $(this).each(function () { - $(this).click(function () { - var target = $(this).attr(options.attr_name); - var obj = $(target); - if (obj.length) - obj.toggle(); - }); - }); - }, - flash_message: function (options) { - // default - options = $.extend({ - text: 'Done', - time: 3000, - how: 'before', - class_name: '' - }, options); - - return $(this).each(function () { - if ($(this).parent().find('.flash_message').get(0)) return; - - var message = $('
', { - 'class': 'flash_message ' + options.class_name, - html: options.text - // display with fade in - }).hide().fadeIn('fast'); - - $(this)[options.how](message); - //delay and fadeout - message.delay(options.time).fadeOut('normal', function () { - $(this).remove(); - }); - - }); - }, - disabled_link: function (options) { - options = $.extend({}, options); - return $(this).each(function () { - $(this).click(function (event) { - title = event.target.title; - if (title.length && event.target.hasAttribute('disabled')) { - event.stopPropagation(); - alert(title); - return false; - } - }); - }); - } - }; + $.fn.issueTemplate = function (method) { - $.fn.issueTemplate = function (method) { - - // Method dispatch logic - if (methods[method]) { - return methods[method].apply(this, Array.prototype.slice.call(arguments, 1)); - } else if (typeof method === 'object' || !method) { - return methods.init.apply(this, arguments); - } else { - $.error('Method ' + method + ' does not exist on jQuery.issueTemplate'); - } - }; + // Method dispatch logic + if (methods[method]) { + return methods[method].apply(this, Array.prototype.slice.call(arguments, 1)); + } else if (typeof method === 'object' || !method) { + return methods.init.apply(this, arguments); + } else { + $.error('Method ' + method + ' does not exist on jQuery.issueTemplate'); + } + }; })(jQuery); $(function () { - // set plugin - $('a.template-help').issueTemplate('displayTooltip'); - $('a.template-help.collapsible').issueTemplate('expandHelp'); - $('a.template-help.collapsible').click(function () { - $(this).toggleClass('collapsed'); - }); - - $('a.template-disabled-link').issueTemplate('disabled_link'); - - // display orphaned template list - $('#orphaned_template_link').on({ - 'ajax:success': (function (_this) { - return function (e, data) { - $('#orphaned_templates').toggle(); - return $('#orphaned_templates').html(data); - }; - })(this) - }); + // set plugin + $('a.template-help').issueTemplate('displayTooltip'); + $('a.template-help.collapsible').issueTemplate('expandHelp'); + $('a.template-help.collapsible').click(function () { + $(this).toggleClass('collapsed'); + }); + + $('a.template-disabled-link').issueTemplate('disabled_link'); + + // display orphaned template list + $('#orphaned_template_link').on({ + 'ajax:success': (function (_this) { + return function (e, data) { + $('#orphaned_templates').toggle(); + return $('#orphaned_templates').html(data); + }; + })(this) + }); }); // for IE11 compatibility (IE11 does not support native Element.closest) // Ref. https://developer.mozilla.org/en-US/docs/Web/API/Element/closest#Polyfill // Ref. https://github.com/akiko-pusu/redmine_issue_templates/issues/270 if (!Element.prototype.matches) { - Element.prototype.matches = Element.prototype.msMatchesSelector || - Element.prototype.webkitMatchesSelector; + Element.prototype.matches = Element.prototype.msMatchesSelector || + Element.prototype.webkitMatchesSelector; } if (!Element.prototype.closest) { - Element.prototype.closest = function(s) { - var el = this; - - do { - if (el.matches(s)) return el; - el = el.parentElement || el.parentNode; - } while (el !== null && el.nodeType === 1); - return null; - }; + Element.prototype.closest = function(s) { + var el = this; + + do { + if (el.matches(s)) return el; + el = el.parentElement || el.parentNode; + } while (el !== null && el.nodeType === 1); + return null; + }; } From 8ddb018b474fba5f7556020fd66a4d494a9876ad Mon Sep 17 00:00:00 2001 From: Akiko Takano Date: Sun, 5 Jan 2020 09:57:08 +0900 Subject: [PATCH 10/30] Change not to use jQuery but to use ES6. --- app/views/common/_nodata.html.erb | 2 +- app/views/common/_orphaned.html.erb | 20 +- app/views/common/_template_links.html.erb | 10 +- .../global_issue_templates/_form.html.erb | 197 ++++---- .../global_issue_templates/index.html.erb | 38 +- app/views/global_issue_templates/new.html.erb | 4 +- .../global_issue_templates/show.html.erb | 6 +- app/views/issue_templates/_form.html.erb | 186 ++++--- .../_issue_select_form.html.erb | 246 ++++----- .../issue_templates/_list_templates.api.rsb | 2 +- .../issue_templates/_list_templates.html.erb | 56 +- app/views/issue_templates/_note_form.html.erb | 104 ++-- .../_template_pulldown.html.erb | 52 +- app/views/issue_templates/index.html.erb | 90 ++-- app/views/issue_templates/new.html.erb | 10 +- app/views/issue_templates/show.html.erb | 32 +- .../issue_templates_settings/index.html.erb | 34 +- app/views/note_templates/_form.html.erb | 48 +- .../_list_note_templates.html.erb | 21 +- app/views/note_templates/index.html.erb | 40 +- app/views/note_templates/new.html.erb | 8 +- app/views/note_templates/show.html.erb | 28 +- .../_redmine_issue_templates.html.erb | 22 +- assets/javascripts/issue_templates.js | 478 +++++++++--------- assets/stylesheets/issue_templates.css | 4 + spec/features/issue_template_spec.rb | 6 +- 26 files changed, 914 insertions(+), 830 deletions(-) diff --git a/app/views/common/_nodata.html.erb b/app/views/common/_nodata.html.erb index e0e0316b..eaaa378a 100644 --- a/app/views/common/_nodata.html.erb +++ b/app/views/common/_nodata.html.erb @@ -1,5 +1,5 @@ <% if trackers.blank? %> -
+
<%= simple_format(l(:text_no_tracker_enabled)) %>
<% end %> \ No newline at end of file diff --git a/app/views/common/_orphaned.html.erb b/app/views/common/_orphaned.html.erb index 68645a85..675ffc34 100644 --- a/app/views/common/_orphaned.html.erb +++ b/app/views/common/_orphaned.html.erb @@ -1,5 +1,5 @@

<%= l(:orphaned_template) %>

- +
@@ -12,7 +12,7 @@ <% orphaned_templates.each do |issue_template| %> - + issue_template issue'> - + - - + + <% end %> diff --git a/app/views/common/_template_links.html.erb b/app/views/common/_template_links.html.erb index bb679bb0..b93373df 100644 --- a/app/views/common/_template_links.html.erb +++ b/app/views/common/_template_links.html.erb @@ -1,6 +1,6 @@ -
-
#
<%= link_to h(issue_template.id), { controller: controller.controller_name, action: 'show', @@ -27,21 +27,21 @@ { title: "#{html_escape(issue_template.note) }"} %> -
- -
- <%= issue_template.title %> +
+ +
+ <%= issue_template.title %> <%= textilizable(issue_template.description) %>
<%= "ID: #{issue_template.tracker_id}" %> <%=h issue_template.author %><%= format_time(issue_template.updated_on)%> <%= format_time(issue_template.updated_on) %>
+
@@ -34,10 +34,10 @@ <% template_map[tracker].sorted.each do |issue_template| %> - + issue_template issue'> - - + + - @@ -76,12 +76,12 @@ <%= javascript_tag do %> - $(function() { $("table.table-sortable tbody").positionedItems(); }); + $(function() { $('table.table-sortable tbody').positionedItems() }) <% end %> <% end %> <%= link_to l(:orphaned_templates, default: 'Orphaned Templates'), orphaned_templates_global_issue_templates_path, remote: true, id: 'orphaned_template_link', class: 'orphaned_template_link collapsible collapsed template-help' %> - + diff --git a/app/views/global_issue_templates/new.html.erb b/app/views/global_issue_templates/new.html.erb index 12e198ad..ce335db3 100644 --- a/app/views/global_issue_templates/new.html.erb +++ b/app/views/global_issue_templates/new.html.erb @@ -1,8 +1,8 @@ -
+
<%= link_to(l(:label_list_templates), { controller: 'global_issue_templates', action: 'index' }, class: 'icon icon-template') %>
-

<%=h "#{l(:issue_templates)} / #{l(:button_add)}" %>

+

<%=h "#{l(:issue_templates)} / #{l(:button_add)}" %>

<%= labelled_form_for :global_issue_template, issue_template, url: { controller: 'global_issue_templates', action: 'create' }, diff --git a/app/views/global_issue_templates/show.html.erb b/app/views/global_issue_templates/show.html.erb index be1f01bc..bb43bb14 100644 --- a/app/views/global_issue_templates/show.html.erb +++ b/app/views/global_issue_templates/show.html.erb @@ -1,14 +1,14 @@ -
+
<%= link_to l(:button_delete), { controller: 'global_issue_templates', action: 'destroy', id: issue_template }, - data: { confirm: l(:text_are_you_sure)}, + data: { confirm: l(:text_are_you_sure) }, title: l(:enabled_template_cannot_destroy, default: 'Only disabled template can be destroyed.'), disabled: issue_template.enabled?, method: 'delete', class: 'icon icon-del template-disabled-link' %> <%= link_to(l(:label_list_templates), { controller: 'global_issue_templates', action: 'index' }, class: 'icon icon-template') %>
-

+

<%= l(:global_issue_templates) %>: #<%= issue_template.id %> <%= issue_template.title %> <%= avatar(issue_template.author, size: '24') %>

diff --git a/app/views/issue_templates/_form.html.erb b/app/views/issue_templates/_form.html.erb index a3af9b48..7c9889ae 100644 --- a/app/views/issue_templates/_form.html.erb +++ b/app/views/issue_templates/_form.html.erb @@ -1,9 +1,9 @@ <%= error_messages_for 'issue_template' %> -
+

<%= f.text_field :title, required: true, size: 80, label: l(:issue_template_name) %>

-
- <%= l(:label_applied_for_issue) %> +
+ <%= l(:label_applied_for_issue) %>

<% if issue_template.tracker.blank? %> <%= f.select :tracker_id, template_target_trackers(project, issue_template), @@ -20,11 +20,11 @@ <% end %>

<%= f.text_field :issue_title, required: false, size: 80, label: l(:issue_title) %> - + <%= l(:help_for_this_field) %> - +

@@ -35,21 +35,21 @@ <% if checklist_enabled %> -

+

- - + +

    <% issue_template.checklist.each_with_index do |content, i| %> -
  • - +
  • + <%= content %> - + - +
  • <% end %>
@@ -64,50 +64,50 @@

<%= f.text_field :related_link, type: 'url', size: 70, label: l(:issue_template_related_link, default: 'Related link') %> - + <%= l(:help_for_this_field) %> - +

<%= f.check_box :is_default, label: l(:field_is_default) %> - + <%= l(:help_for_this_field) %> - +

<%= f.check_box :enabled, label: l(:label_enabled) %> - + <%= l(:help_for_this_field) %> - +

<%= f.check_box :enabled_sharing, label: l(:label_enabled_sharing) %> - + <%= l(:help_for_this_field) %> - +

@@ -116,74 +116,134 @@ <%= wikitoolbar_for 'issue_template_description' %> -
#
<%= link_to h(issue_template.id), { controller: 'global_issue_templates', id: issue_template.id, action: 'show' }, - { title: issue_template.title} %> + { title: issue_template.title } %> <%= link_to h(issue_template.title), { controller: 'global_issue_templates', @@ -45,10 +45,10 @@ { title: "#{html_escape(issue_template.note)}" } %> -
- -
- <%= issue_template.title %> +
+ +
+ <%= issue_template.title %> <%= textilizable(issue_template.description) %> <% if issue_template.related_link.present? %>
@@ -62,10 +62,10 @@
<%=h issue_template.tracker.name %> <%=h issue_template.author %> <%= format_time(issue_template.updated_on)%> <%= checked_image issue_template.is_default? %><%= checked_image issue_template.enabled? %><%= checked_image issue_template.is_default? %><%= checked_image issue_template.enabled? %> + <%= reorder_handle(issue_template, :url => url_for({ controller: 'global_issue_templates', id: issue_template.id, action: 'update' })) %>
+
@@ -9,7 +9,7 @@ <% issue_templates.each do |template| %> - + template_data'> @@ -17,10 +17,10 @@ <%= template.issue_title %> - + <% end %> @@ -46,9 +47,9 @@ <%= template.issue_title %> - + <% end %> <% global_issue_templates.each do |template| %> - + template_data'> @@ -75,10 +77,10 @@ <%= template.issue_title %> - + <% end %>
<%=h l(:issue_template_name) %>
<%= template.title %> -
- -
- <%= template.title %> +
+ +
+ <%= template.title %> <%= textilizable(template.description) %> <% if template.related_link.present? %>
@@ -30,10 +30,11 @@
<%= checked_image template.is_default? %><%= checked_image template.is_default? %> - +
-
- -
+
+ +
<%= template.title %> <%= textilizable(template.description) %> <% if template.related_link.present? %> @@ -59,15 +60,16 @@
<%= checked_image template == default_template %><%= checked_image template == default_template %> - +
<%= template.title %> -
- -
- <%= template.issue_title %> +
+ +
+ <%= template.issue_title %> <%= textilizable(template.description) %> <% if template.related_link.present? %>
@@ -88,17 +90,13 @@
<%= checked_image template == default_template %><%= checked_image template == default_template %> - +
- + diff --git a/app/views/issue_templates/_note_form.html.erb b/app/views/issue_templates/_note_form.html.erb index 67135f66..bb2a5a6d 100644 --- a/app/views/issue_templates/_note_form.html.erb +++ b/app/views/issue_templates/_note_form.html.erb @@ -6,19 +6,19 @@ project_id = issue&.project_id tracker_id = issue&.tracker_id %> -
-