diff --git a/.gitignore b/.gitignore
index 021d4b6..ddee74a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,3 +11,6 @@ Desktop.ini
# Application
.rvmrc
/tmp
+
+# vsCode
+.vscode
\ No newline at end of file
diff --git a/app/controllers/tags_controller.rb b/app/controllers/tags_controller.rb
index 86c650e..75a3d5d 100755
--- a/app/controllers/tags_controller.rb
+++ b/app/controllers/tags_controller.rb
@@ -1,7 +1,9 @@
class TagsController < ApplicationController
- before_action :require_admin
+ before_action :require_admin, except: [:add_tag, :delete_tag, :update_tag]
+ before_action :require_login, only: [:update_tag]
before_action :find_tag, only: [:edit, :update]
before_action :bulk_find_tags, only: [:context_menu, :merge, :destroy]
+ before_action :find_project_by_project_id, only: [:add_tag, :delete_tag]
helper :issues_tags
@@ -54,6 +56,55 @@ def merge
end
end
+ def add_tag
+ @issue_ids = params[:issue_ids]
+ @back_url = params[:back_url]
+ end
+
+ def delete_tag
+ @issue_ids = params[:issue_ids]
+ @back_url = params[:back_url]
+
+ issues = Issue.where(id: @issue_ids)
+ @candidate_tags = ActsAsTaggableOn::Tag
+ .joins(:taggings)
+ .where(taggings: {taggable_type: 'Issue', taggable_id: issues})
+ .distinct
+ .order('tags.name')
+ end
+
+ def update_tag
+ operate = params[:operate]
+ return if operate.blank?
+
+ sabun = params[:tag_list]
+ return if sabun.blank?
+
+ sabun = sabun.split(ActsAsTaggableOn.delimiter) unless sabun.is_a?(Array)
+
+ Issue.where(id: params[:issue_ids]).each do |issue|
+ old_tags = issue.tag_list.to_s
+
+ case operate
+ when 'add'
+ issue.tag_list |= sabun
+ when 'delete'
+ issue.tag_list -= sabun
+ end
+
+ new_tags = issue.tag_list.to_s
+ unless old_tags == new_tags
+ issue.save_tags
+ issue.init_journal(User.current) if issue.current_journal.blank?
+ issue.current_journal.details << JournalDetail.new(
+ property: 'attr', prop_key: 'tag_list', old_value: old_tags, value: new_tags)
+ issue.current_journal.save
+ end
+ end
+ Issue.remove_unused_tags!
+ # redirect_to params[:back_url] if params[:back_url]
+ end
+
private
def bulk_find_tags
diff --git a/app/views/issues/_add_context_menu.html.erb b/app/views/issues/_add_context_menu.html.erb
new file mode 100644
index 0000000..235c262
--- /dev/null
+++ b/app/views/issues/_add_context_menu.html.erb
@@ -0,0 +1,13 @@
+<% if @can[:edit] %>
+
+
+
+ - <%= context_menu_link(l(:button_add),
+ tags_add_path(issue_ids: @issue_ids, project_id: @project, back_url: back),
+ remote: true) %>
+ - <%= context_menu_link(l(:button_delete),
+ tags_delete_path(issue_ids: @issue_ids, project_id: @project, back_url: back),
+ remote: true) %>
+
+
+<% end %>
diff --git a/app/views/tags/_add_tag.html.erb b/app/views/tags/_add_tag.html.erb
new file mode 100644
index 0000000..868a76b
--- /dev/null
+++ b/app/views/tags/_add_tag.html.erb
@@ -0,0 +1,43 @@
+<%= l(:label_add_tag) %>
+
+<%= form_tag({action: :update_tag},
+ remote: true,
+ method: :patch,
+ id: 'add-tag-form') do %>
+
+ <%= hidden_field_tag 'project_id', @project.id if @project %>
+ <% @issue_ids.each do |i| %>
+ <%= hidden_field_tag 'issue_ids[]', i %>
+ <% end %>
+ <%= hidden_field_tag 'back_url', @back_url if @back_url %>
+ <%= hidden_field_tag 'operate', 'add' %>
+
+
+
+ <%= text_field_tag :tag_list, '', size: 30, id: 'add-issue-tag-list' %>
+
+
+
+ <%= submit_tag l(:button_add), name: nil, onclick: "hideModal(this); setTimeout(function(){location.reload(false);},300);" %>
+ <%= link_to_function l(:button_cancel), "hideModal(this);" %>
+
+<% end %>
+
+<%= javascript_tag "$('#add-issue-tag-list').tagit({
+ tagSource: function(search, showChoices) {
+ var that = this;
+ $.ajax({
+ url: '#{escape_javascript auto_complete_issue_tags_path(@project)}',
+ data: {q: search.term},
+ success: function(choices) {
+ showChoices(that._subtractArray(jQuery.parseJSON(choices.replace(//g, '')), that.assignedTags()));
+ }
+ });
+ },
+ allowSpaces: true,
+ placeholderText: '#{l(:issue_tags_label_add_tag)}',
+ caseSensitive: false,
+ removeConfirmation: true,
+ showAutocompleteOnFocus: true,
+ autocomplete: {delay: 1, minLength: 1},
+});" %>
diff --git a/app/views/tags/_delete_tag.html.erb b/app/views/tags/_delete_tag.html.erb
new file mode 100644
index 0000000..49e9818
--- /dev/null
+++ b/app/views/tags/_delete_tag.html.erb
@@ -0,0 +1,28 @@
+<%= l(:label_delete_tag) %>
+
+<%= form_tag({action: :update_tag},
+ remote: true,
+ method: :patch,
+ id: 'delete-tag-form') do %>
+
+ <%= hidden_field_tag 'project_id', @project.id if @project %>
+ <% @issue_ids.each do |i| %>
+ <%= hidden_field_tag 'issue_ids[]', i %>
+ <% end %>
+ <%= hidden_field_tag 'back_url', @back_url if @back_url %>
+ <%= hidden_field_tag 'operate', 'delete' %>
+
+
+ <% @candidate_tags.each do |t| %>
+
+ <% end %>
+
+
+
+ <%= submit_tag l(:button_delete), name: nil, onclick: "hideModal(this); setTimeout(function(){location.reload(false);},300);" %>
+ <%= link_to_function l(:button_cancel), "hideModal(this);" %>
+
+<% end %>
diff --git a/app/views/tags/add_tag.js.erb b/app/views/tags/add_tag.js.erb
new file mode 100644
index 0000000..55992de
--- /dev/null
+++ b/app/views/tags/add_tag.js.erb
@@ -0,0 +1,9 @@
+$('#ajax-modal').html('<%= escape_javascript(render partial: 'tags/add_tag') %>');
+showModal('ajax-modal', '400px');
+$('#ajax-modal').addClass('add-tag');
+
+var ac = $('.ui-autocomplete');
+<%# モーダルダイアログでオートコンプリート候補表示を手前に チケット画面で呼ばれるとissues_catalog.cssが読み込まれないのでここで指定 %>
+ac.css('z-index', '2147483647');
+<%# ダイアログの子になる場合があり、そうなると表示がクリップされるのでbody直下に移動 %>
+$('body').append(ac);
diff --git a/app/views/tags/delete_tag.js.erb b/app/views/tags/delete_tag.js.erb
new file mode 100644
index 0000000..cf38113
--- /dev/null
+++ b/app/views/tags/delete_tag.js.erb
@@ -0,0 +1,3 @@
+$('#ajax-modal').html('<%= escape_javascript(render partial: 'tags/delete_tag') %>');
+showModal('ajax-modal', '400px');
+$('#ajax-modal').addClass('delete-tag');
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 2e3eace..8601adc 100755
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -26,3 +26,6 @@ en:
issue_tags_tag: Tag
issue_tags_button_merge: Merge
issue_tags_label_merge: Merge selected tags
+
+ label_add_tag: "Add Tag"
+ label_delete_tag: "Delete Tag"
diff --git a/config/locales/ja.yml b/config/locales/ja.yml
index d4f3871..138ad1f 100644
--- a/config/locales/ja.yml
+++ b/config/locales/ja.yml
@@ -26,3 +26,6 @@ ja:
issue_tags_tag: タグ
issue_tags_button_merge: 結合
issue_tags_label_merge: 選択したタグを結合します
+
+ label_add_tag: "タグを追加"
+ label_delete_tag: "タグを削除"
diff --git a/config/routes.rb b/config/routes.rb
index bfae960..c3d8804 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -5,6 +5,9 @@
match '/tags/context_menu', to: 'tags#context_menu', as: 'tags_context_menu',
via: [:get, :post]
delete '/tags', controller: 'tags', action: 'destroy'
+get '/tags/add_tag', to: 'tags#add_tag', as: 'tags_add'
+get '/tags/delete_tag', to: 'tags#delete_tag', as: 'tags_delete'
+patch '/tags/update_tag', to: 'tags#update_tag'
resources :tags, only: [:edit, :update] do
collection do
diff --git a/lib/redmine_tags/hooks/views_issues_hook.rb b/lib/redmine_tags/hooks/views_issues_hook.rb
index 08dd916..53236ea 100755
--- a/lib/redmine_tags/hooks/views_issues_hook.rb
+++ b/lib/redmine_tags/hooks/views_issues_hook.rb
@@ -6,6 +6,7 @@ class ViewsIssuesHook < Redmine::Hook::ViewListener
render_on :view_issues_sidebar_planning_bottom, partial: 'issues/tags_sidebar'
render_on :view_issues_bulk_edit_details_bottom, partial: 'issues/bulk_edit_tags'
render_on :view_layouts_base_html_head, partial: 'tags/header_assets'
+ render_on :view_issues_context_menu_start, partial: 'issues/add_context_menu'
end
end
end