additional_tags 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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: []