additional_tags 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/brakeman.yml +38 -0
  3. data/.github/workflows/linters.yml +41 -0
  4. data/.github/workflows/tests.yml +139 -0
  5. data/.gitignore +8 -0
  6. data/.rubocop.yml +75 -0
  7. data/.slim-lint.yml +25 -0
  8. data/.stylelintrc.json +163 -0
  9. data/LICENSE +339 -0
  10. data/README.md +126 -0
  11. data/Rakefile +11 -0
  12. data/additional_tags.gemspec +28 -0
  13. data/app/controllers/additional_tags_controller.rb +80 -0
  14. data/app/controllers/issue_tags_controller.rb +48 -0
  15. data/app/helpers/additional_tags_helper.rb +197 -0
  16. data/app/helpers/additional_tags_issues_helper.rb +43 -0
  17. data/app/helpers/additional_tags_wiki_helper.rb +17 -0
  18. data/app/jobs/additional_tags_job.rb +3 -0
  19. data/app/jobs/additional_tags_remove_unused_tag_job.rb +5 -0
  20. data/app/models/migrate_tag.rb +4 -0
  21. data/app/models/migrate_tagging.rb +5 -0
  22. data/app/views/additional_tags/_html_head.html.slim +6 -0
  23. data/app/views/additional_tags/_tag_list.html.slim +35 -0
  24. data/app/views/additional_tags/context_menu.html.slim +16 -0
  25. data/app/views/additional_tags/edit.html.slim +14 -0
  26. data/app/views/additional_tags/merge.html.slim +17 -0
  27. data/app/views/additional_tags/settings/_general.html.slim +45 -0
  28. data/app/views/additional_tags/settings/_manage_tags.html.slim +35 -0
  29. data/app/views/additional_tags/settings/_settings.html.slim +9 -0
  30. data/app/views/auto_completes/_additional_tag_list.slim +1 -0
  31. data/app/views/context_menus/_issues_tags.html.slim +10 -0
  32. data/app/views/issue_tags/_edit_modal.html.slim +30 -0
  33. data/app/views/issue_tags/edit.js.erb +2 -0
  34. data/app/views/issues/_tags.html.slim +8 -0
  35. data/app/views/issues/_tags_bulk_edit.html.slim +7 -0
  36. data/app/views/issues/_tags_form.html.slim +11 -0
  37. data/app/views/issues/_tags_form_details.html.slim +7 -0
  38. data/app/views/issues/_tags_sidebar.html.slim +5 -0
  39. data/app/views/reports/_tags_simple.html.slim +11 -0
  40. data/app/views/wiki/_tags_form.html.slim +7 -0
  41. data/app/views/wiki/_tags_form_bottom.html.slim +5 -0
  42. data/app/views/wiki/_tags_show.html.slim +9 -0
  43. data/app/views/wiki/_tags_sidebar.html.slim +4 -0
  44. data/app/views/wiki/tag_index.html.slim +21 -0
  45. data/assets/javascripts/tags.js +19 -0
  46. data/assets/stylesheets/tags.css +119 -0
  47. data/config/locales/bg.yml +33 -0
  48. data/config/locales/cs.yml +33 -0
  49. data/config/locales/de.yml +33 -0
  50. data/config/locales/en.yml +33 -0
  51. data/config/locales/es.yml +33 -0
  52. data/config/locales/fr.yml +33 -0
  53. data/config/locales/it.yml +33 -0
  54. data/config/locales/ja.yml +33 -0
  55. data/config/locales/ko.yml +33 -0
  56. data/config/locales/pl.yml +33 -0
  57. data/config/locales/pt-BR.yml +33 -0
  58. data/config/locales/ru.yml +33 -0
  59. data/config/routes.rb +31 -0
  60. data/config/settings.yml +9 -0
  61. data/db/migrate/20201116145429_acts_as_taggable_migration.rb +40 -0
  62. data/db/migrate/20201123093214_migrate_existing_tags.rb +39 -0
  63. data/doc/images/additional-tags-framework.png +0 -0
  64. data/doc/images/additional-tags.gif +0 -0
  65. data/init.rb +31 -0
  66. data/lib/additional_tags.rb +107 -0
  67. data/lib/additional_tags/hooks.rb +63 -0
  68. data/lib/additional_tags/patches/agile_boards_controller_patch.rb +14 -0
  69. data/lib/additional_tags/patches/agile_query_patch.rb +41 -0
  70. data/lib/additional_tags/patches/agile_versions_controller_patch.rb +14 -0
  71. data/lib/additional_tags/patches/agile_versions_query_patch.rb +11 -0
  72. data/lib/additional_tags/patches/auto_completes_controller_patch.rb +42 -0
  73. data/lib/additional_tags/patches/calendars_controller_patch.rb +14 -0
  74. data/lib/additional_tags/patches/dashboard_async_blocks_controller_patch.rb +11 -0
  75. data/lib/additional_tags/patches/dashboards_controller_patch.rb +11 -0
  76. data/lib/additional_tags/patches/gantts_controller_patch.rb +14 -0
  77. data/lib/additional_tags/patches/imports_controller_patch.rb +12 -0
  78. data/lib/additional_tags/patches/issue_patch.rb +95 -0
  79. data/lib/additional_tags/patches/issue_query_patch.rb +55 -0
  80. data/lib/additional_tags/patches/issues_controller_patch.rb +14 -0
  81. data/lib/additional_tags/patches/journal_patch.rb +18 -0
  82. data/lib/additional_tags/patches/my_controller_patch.rb +14 -0
  83. data/lib/additional_tags/patches/queries_helper_patch.rb +40 -0
  84. data/lib/additional_tags/patches/reports_controller_patch.rb +32 -0
  85. data/lib/additional_tags/patches/settings_controller_patch.rb +12 -0
  86. data/lib/additional_tags/patches/time_entry_patch.rb +19 -0
  87. data/lib/additional_tags/patches/time_entry_query_patch.rb +53 -0
  88. data/lib/additional_tags/patches/time_report_patch.rb +46 -0
  89. data/lib/additional_tags/patches/timelog_controller_patch.rb +14 -0
  90. data/lib/additional_tags/patches/wiki_controller_patch.rb +63 -0
  91. data/lib/additional_tags/patches/wiki_page_patch.rb +39 -0
  92. data/lib/additional_tags/tags.rb +134 -0
  93. data/lib/additional_tags/version.rb +3 -0
  94. metadata +177 -0
@@ -0,0 +1,14 @@
1
+ module AdditionalTags
2
+ module Patches
3
+ module TimelogControllerPatch
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ helper :additional_tags
8
+ helper :additional_tags_issues
9
+
10
+ include AdditionalTagsHelper
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,63 @@
1
+ module AdditionalTags
2
+ module Patches
3
+ module WikiControllerPatch
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ prepend InstanceOverwriteMethods
8
+ helper :additional_tags
9
+ helper :additional_tags_wiki
10
+
11
+ before_action :find_page_for_update_tags, only: :update_tags
12
+ end
13
+
14
+ module InstanceOverwriteMethods
15
+ def update_tags
16
+ @page.safe_attributes = [:tag_list]
17
+ @page.tag_list = params[:wiki_page][:tag_list].to_a.reject(&:empty?)
18
+ flash[:notice] = if @page.save
19
+ l :notice_successful_update
20
+ else
21
+ t :notice_failed_to_add_tags
22
+ end
23
+ redirect_to project_wiki_page_path(@page.project, @page.title)
24
+ end
25
+
26
+ def index
27
+ @tag = params[:tag]
28
+ return super unless AdditionalTags.setting?(:active_wiki_tags) && @tag.present?
29
+
30
+ load_pages_for_index_with_tag
31
+
32
+ respond_to do |format|
33
+ format.html do
34
+ render template: 'wiki/tag_index'
35
+ end
36
+ format.api
37
+ end
38
+ end
39
+
40
+ private
41
+
42
+ def load_pages_for_index_with_tag
43
+ pattern = "%#{@tag.to_s.strip}%"
44
+ @pages = @wiki.pages
45
+ .joins(AdditionalTags::Tags.tag_to_joins(WikiPage))
46
+ .where("LOWER(#{ActiveRecord::Base.connection.quote_table_name(ActsAsTaggableOn.tags_table)}.name) LIKE LOWER(:p)",
47
+ p: pattern)
48
+ .with_updated_on
49
+ .distinct
50
+ .reorder("#{WikiPage.table_name}.title")
51
+ .includes(wiki: :project)
52
+ .includes(:parent).to_a
53
+ end
54
+
55
+ # find_existing_page can not be used from wiki_controller, because it would be disable index only rule
56
+ def find_page_for_update_tags
57
+ @page = @wiki.find_page params[:id]
58
+ render_404 if @page.nil?
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,39 @@
1
+ module AdditionalTags
2
+ module Patches
3
+ module WikiPagePatch
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ include InstanceMethods
8
+
9
+ acts_as_ordered_taggable
10
+
11
+ safe_attributes 'tag_list'
12
+ before_save :sort_tag_list
13
+ end
14
+
15
+ class_methods do
16
+ def project_joins
17
+ ["JOIN wikis ON wikis.id = #{table_name}.wiki_id",
18
+ "JOIN #{Project.table_name} ON wikis.project_id = #{Project.table_name}.id"]
19
+ end
20
+
21
+ def available_tags(options = {})
22
+ options[:project_join] = project_joins
23
+ options[:permission] = :view_wiki_pages
24
+ AdditionalTags::Tags.available_tags self, options
25
+ end
26
+ end
27
+
28
+ module InstanceMethods
29
+ private
30
+
31
+ def sort_tag_list
32
+ return unless tag_list.present? && tag_list_changed?
33
+
34
+ self.tag_list = AdditionalTags::Tags.sort_tags tag_list
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,134 @@
1
+ module AdditionalTags
2
+ class Tags
3
+ class << self
4
+ def available_tags(klass, options = {})
5
+ user = options[:user].presence || User.current
6
+
7
+ scope = ActsAsTaggableOn::Tag.where({})
8
+ scope = scope.where("#{Project.table_name}.id = ?", options[:project]) if options[:project]
9
+ if options[:permission]
10
+ scope = scope.where(tag_access(options[:permission], user))
11
+ elsif options[:visible_condition]
12
+ scope = scope.where(klass.visible_condition(user))
13
+ end
14
+ scope = scope.where("LOWER(#{TAG_TABLE_NAME}.name) LIKE ?", "%#{options[:name_like].downcase}%") if options[:name_like]
15
+ scope = scope.where("#{TAG_TABLE_NAME}.name=?", options[:name]) if options[:name]
16
+ scope = scope.where("#{TAGGING_TABLE_NAME}.taggable_id!=?", options[:exclude_id]) if options[:exclude_id]
17
+ scope = scope.where(options[:where_field] => options[:where_value]) if options[:where_field].present? && options[:where_value]
18
+
19
+ columns = ["#{TAG_TABLE_NAME}.id",
20
+ "#{TAG_TABLE_NAME}.name",
21
+ "#{TAG_TABLE_NAME}.taggings_count",
22
+ "COUNT(DISTINCT #{TAGGING_TABLE_NAME}.taggable_id) AS count"]
23
+
24
+ columns << "MIN(#{TAGGING_TABLE_NAME}.created_at) AS last_created" if options[:sort_by] == 'last_created'
25
+
26
+ scope.select(columns.join(', '))
27
+ .joins(tag_for_joins(klass, options))
28
+ .group("#{TAG_TABLE_NAME}.id, #{TAG_TABLE_NAME}.name, #{TAG_TABLE_NAME}.taggings_count").having('COUNT(*) > 0')
29
+ .order(build_order_sql(options[:sort_by], options[:order]))
30
+ end
31
+
32
+ def all_type_tags(klass, options = {})
33
+ ActsAsTaggableOn::Tag.where({})
34
+ .joins(tag_for_joins(klass, options))
35
+ .distinct
36
+ .order("#{TAG_TABLE_NAME}.name")
37
+ end
38
+
39
+ def tag_to_joins(klass)
40
+ table_name = klass.table_name
41
+
42
+ joins = ["JOIN #{TAGGING_TABLE_NAME} ON #{TAGGING_TABLE_NAME}.taggable_id = #{table_name}.id" \
43
+ " AND #{TAGGING_TABLE_NAME}.taggable_type = '#{klass}'"]
44
+ joins << "JOIN #{TAG_TABLE_NAME} ON #{TAGGING_TABLE_NAME}.tag_id = #{TAG_TABLE_NAME}.id"
45
+
46
+ joins
47
+ end
48
+
49
+ def remove_unused_tags
50
+ ActsAsTaggableOn::Tag.where.not(id: ActsAsTaggableOn::Tagging.select(:tag_id).distinct)
51
+ .each(&:destroy)
52
+ end
53
+
54
+ def merge(tag_name, tags_to_merge)
55
+ return if tag_name.blank? || tags_to_merge.none?
56
+
57
+ ActsAsTaggableOn::Tagging.transaction do
58
+ tag = ActsAsTaggableOn::Tag.find_by(name: tag_name) || ActsAsTaggableOn::Tag.create(name: tag_name)
59
+ # Update old tagging with new tag
60
+ ActsAsTaggableOn::Tagging.where(tag_id: tags_to_merge.map(&:id)).update_all tag_id: tag.id
61
+ # remove old (merged) tags
62
+ tags_to_merge.reject { |t| t.id == tag.id }.each(&:destroy)
63
+ # remove duplicate taggings
64
+ dup_scope = ActsAsTaggableOn::Tagging.where(tag_id: tag.id)
65
+ valid_ids = dup_scope.group(:tag_id, :taggable_id, :taggable_type, :tagger_id, :tagger_type, :context).pluck(Arel.sql('MIN(id)'))
66
+ dup_scope.where.not(id: valid_ids).destroy_all if valid_ids.any?
67
+ # recalc count for new tag
68
+ ActsAsTaggableOn::Tag.reset_counters tag.id, :taggings
69
+ end
70
+ end
71
+
72
+ # sort tags alphabetically with special characters support
73
+ def sort_tags(tags)
74
+ tags.sort! do |a, b|
75
+ ActiveSupport::Inflector.transliterate(a.downcase) <=> ActiveSupport::Inflector.transliterate(b.downcase)
76
+ end
77
+ end
78
+
79
+ # sort tag_list alphabetically with special characters support
80
+ def sort_tag_list(tag_list)
81
+ tag_list.to_a.sort! do |a, b|
82
+ ActiveSupport::Inflector.transliterate(a.name.downcase) <=> ActiveSupport::Inflector.transliterate(b.name.downcase)
83
+ end
84
+ end
85
+
86
+ private
87
+
88
+ def build_order_sql(sort_by, order)
89
+ order = order.present? && order == 'DESC' ? 'DESC' : 'ASC'
90
+
91
+ sql = case sort_by
92
+ when 'last_created'
93
+ "last_created #{order}, #{TAG_TABLE_NAME}.name ASC"
94
+ when 'count'
95
+ "count #{order}, #{TAG_TABLE_NAME}.name ASC"
96
+ else
97
+ "#{TAG_TABLE_NAME}.name #{order}"
98
+ end
99
+
100
+ Arel.sql sql
101
+ end
102
+
103
+ def tag_for_joins(klass, options = {})
104
+ table_name = klass.table_name
105
+
106
+ joins = ["JOIN #{TAGGING_TABLE_NAME} ON #{TAGGING_TABLE_NAME}.tag_id = #{TAG_TABLE_NAME}.id"]
107
+ joins << "JOIN #{table_name} " \
108
+ "ON #{table_name}.id = #{TAGGING_TABLE_NAME}.taggable_id AND #{TAGGING_TABLE_NAME}.taggable_type = '#{klass}'"
109
+
110
+ if options[:project_join]
111
+ joins << options[:project_join]
112
+ elsif options[:project] || !options[:without_projects]
113
+ joins << "JOIN #{Project.table_name} ON #{table_name}.project_id = #{Project.table_name}.id"
114
+ end
115
+
116
+ joins
117
+ end
118
+
119
+ def tag_access(permission, user)
120
+ projects_allowed = if permission.nil?
121
+ Project.visible.ids
122
+ else
123
+ Project.where(Project.allowed_to_condition(user, permission)).ids
124
+ end
125
+
126
+ if projects_allowed.present?
127
+ "#{Project.table_name}.id IN (#{projects_allowed.join ','})" unless projects_allowed.empty?
128
+ else
129
+ '1=0'
130
+ end
131
+ end
132
+ end
133
+ end
134
+ end
@@ -0,0 +1,3 @@
1
+ module AdditionalTags
2
+ VERSION = '1.0.0'.freeze
3
+ end
metadata ADDED
@@ -0,0 +1,177 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: additional_tags
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - AlphaNodes
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2020-12-25 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: acts-as-taggable-on
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '6.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '6.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: Redmine plugin for adding tag functionality
56
+ email:
57
+ - alex@alphanodes.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - ".github/workflows/brakeman.yml"
63
+ - ".github/workflows/linters.yml"
64
+ - ".github/workflows/tests.yml"
65
+ - ".gitignore"
66
+ - ".rubocop.yml"
67
+ - ".slim-lint.yml"
68
+ - ".stylelintrc.json"
69
+ - LICENSE
70
+ - README.md
71
+ - Rakefile
72
+ - additional_tags.gemspec
73
+ - app/controllers/additional_tags_controller.rb
74
+ - app/controllers/issue_tags_controller.rb
75
+ - app/helpers/additional_tags_helper.rb
76
+ - app/helpers/additional_tags_issues_helper.rb
77
+ - app/helpers/additional_tags_wiki_helper.rb
78
+ - app/jobs/additional_tags_job.rb
79
+ - app/jobs/additional_tags_remove_unused_tag_job.rb
80
+ - app/models/migrate_tag.rb
81
+ - app/models/migrate_tagging.rb
82
+ - app/views/additional_tags/_html_head.html.slim
83
+ - app/views/additional_tags/_tag_list.html.slim
84
+ - app/views/additional_tags/context_menu.html.slim
85
+ - app/views/additional_tags/edit.html.slim
86
+ - app/views/additional_tags/merge.html.slim
87
+ - app/views/additional_tags/settings/_general.html.slim
88
+ - app/views/additional_tags/settings/_manage_tags.html.slim
89
+ - app/views/additional_tags/settings/_settings.html.slim
90
+ - app/views/auto_completes/_additional_tag_list.slim
91
+ - app/views/context_menus/_issues_tags.html.slim
92
+ - app/views/issue_tags/_edit_modal.html.slim
93
+ - app/views/issue_tags/edit.js.erb
94
+ - app/views/issues/_tags.html.slim
95
+ - app/views/issues/_tags_bulk_edit.html.slim
96
+ - app/views/issues/_tags_form.html.slim
97
+ - app/views/issues/_tags_form_details.html.slim
98
+ - app/views/issues/_tags_sidebar.html.slim
99
+ - app/views/reports/_tags_simple.html.slim
100
+ - app/views/wiki/_tags_form.html.slim
101
+ - app/views/wiki/_tags_form_bottom.html.slim
102
+ - app/views/wiki/_tags_show.html.slim
103
+ - app/views/wiki/_tags_sidebar.html.slim
104
+ - app/views/wiki/tag_index.html.slim
105
+ - assets/javascripts/tags.js
106
+ - assets/stylesheets/tags.css
107
+ - config/locales/bg.yml
108
+ - config/locales/cs.yml
109
+ - config/locales/de.yml
110
+ - config/locales/en.yml
111
+ - config/locales/es.yml
112
+ - config/locales/fr.yml
113
+ - config/locales/it.yml
114
+ - config/locales/ja.yml
115
+ - config/locales/ko.yml
116
+ - config/locales/pl.yml
117
+ - config/locales/pt-BR.yml
118
+ - config/locales/ru.yml
119
+ - config/routes.rb
120
+ - config/settings.yml
121
+ - db/migrate/20201116145429_acts_as_taggable_migration.rb
122
+ - db/migrate/20201123093214_migrate_existing_tags.rb
123
+ - doc/images/additional-tags-framework.png
124
+ - doc/images/additional-tags.gif
125
+ - init.rb
126
+ - lib/additional_tags.rb
127
+ - lib/additional_tags/hooks.rb
128
+ - lib/additional_tags/patches/agile_boards_controller_patch.rb
129
+ - lib/additional_tags/patches/agile_query_patch.rb
130
+ - lib/additional_tags/patches/agile_versions_controller_patch.rb
131
+ - lib/additional_tags/patches/agile_versions_query_patch.rb
132
+ - lib/additional_tags/patches/auto_completes_controller_patch.rb
133
+ - lib/additional_tags/patches/calendars_controller_patch.rb
134
+ - lib/additional_tags/patches/dashboard_async_blocks_controller_patch.rb
135
+ - lib/additional_tags/patches/dashboards_controller_patch.rb
136
+ - lib/additional_tags/patches/gantts_controller_patch.rb
137
+ - lib/additional_tags/patches/imports_controller_patch.rb
138
+ - lib/additional_tags/patches/issue_patch.rb
139
+ - lib/additional_tags/patches/issue_query_patch.rb
140
+ - lib/additional_tags/patches/issues_controller_patch.rb
141
+ - lib/additional_tags/patches/journal_patch.rb
142
+ - lib/additional_tags/patches/my_controller_patch.rb
143
+ - lib/additional_tags/patches/queries_helper_patch.rb
144
+ - lib/additional_tags/patches/reports_controller_patch.rb
145
+ - lib/additional_tags/patches/settings_controller_patch.rb
146
+ - lib/additional_tags/patches/time_entry_patch.rb
147
+ - lib/additional_tags/patches/time_entry_query_patch.rb
148
+ - lib/additional_tags/patches/time_report_patch.rb
149
+ - lib/additional_tags/patches/timelog_controller_patch.rb
150
+ - lib/additional_tags/patches/wiki_controller_patch.rb
151
+ - lib/additional_tags/patches/wiki_page_patch.rb
152
+ - lib/additional_tags/tags.rb
153
+ - lib/additional_tags/version.rb
154
+ homepage: https://github.com/alphanodes/additional_tags
155
+ licenses:
156
+ - GPL-2.0
157
+ metadata: {}
158
+ post_install_message:
159
+ rdoc_options: []
160
+ require_paths:
161
+ - lib
162
+ required_ruby_version: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '2.4'
167
+ required_rubygems_version: !ruby/object:Gem::Requirement
168
+ requirements:
169
+ - - ">="
170
+ - !ruby/object:Gem::Version
171
+ version: '0'
172
+ requirements: []
173
+ rubygems_version: 3.0.3
174
+ signing_key:
175
+ specification_version: 4
176
+ summary: Redmine plugin for adding tag functionality
177
+ test_files: []