additional_tags 1.0.6 → 1.0.7

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 (54) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/codeql-analysis.yml +6 -6
  3. data/.github/workflows/linters.yml +15 -3
  4. data/.github/workflows/tests.yml +7 -4
  5. data/.gitignore +2 -0
  6. data/.rubocop.yml +5 -0
  7. data/.stylelintrc.json +12 -155
  8. data/README.md +26 -7
  9. data/app/controllers/additional_tags_controller.rb +9 -9
  10. data/app/controllers/issue_tags_controller.rb +3 -1
  11. data/app/helpers/additional_tags_helper.rb +39 -30
  12. data/app/helpers/additional_tags_issues_helper.rb +1 -3
  13. data/app/helpers/additional_tags_wiki_helper.rb +6 -5
  14. data/app/models/additional_tag.rb +98 -0
  15. data/app/views/additional_tags/_tag_list.html.slim +10 -4
  16. data/app/views/additional_tags/merge.html.slim +0 -1
  17. data/app/views/additional_tags/settings/_general.html.slim +7 -1
  18. data/app/views/additional_tags/settings/_manage_tags.html.slim +8 -1
  19. data/app/views/context_menus/_issues_tags.html.slim +7 -1
  20. data/app/views/dashboards/blocks/_issue_tags.html.slim +3 -1
  21. data/app/views/issue_tags/_edit_modal.html.slim +7 -7
  22. data/app/views/issues/_tags.html.slim +6 -1
  23. data/app/views/issues/_tags_bulk_edit.html.slim +2 -1
  24. data/app/views/wiki/_tags_form.html.slim +4 -0
  25. data/app/views/wiki/_tags_show.html.slim +1 -1
  26. data/assets/javascripts/tags.js +3 -3
  27. data/assets/stylesheets/tags.css +41 -16
  28. data/config/locales/bg.yml +18 -11
  29. data/config/locales/cs.yml +19 -12
  30. data/config/locales/de.yml +19 -12
  31. data/config/locales/en.yml +19 -12
  32. data/config/locales/es.yml +19 -12
  33. data/config/locales/fr.yml +19 -12
  34. data/config/locales/it.yml +19 -12
  35. data/config/locales/ja.yml +18 -11
  36. data/config/locales/ko.yml +18 -11
  37. data/config/locales/pl.yml +19 -12
  38. data/config/locales/pt-BR.yml +18 -11
  39. data/config/locales/ru.yml +34 -27
  40. data/config/settings.yml +5 -5
  41. data/doc/images/additional-tags-framework.png +0 -0
  42. data/doc/images/additional-tags.gif +0 -0
  43. data/doc/images/tag-overview.png +0 -0
  44. data/lib/additional_tags/hooks/view_hook.rb +15 -2
  45. data/lib/additional_tags/patches/issue_patch.rb +53 -7
  46. data/lib/additional_tags/patches/issue_query_patch.rb +18 -0
  47. data/lib/additional_tags/patches/queries_helper_patch.rb +1 -3
  48. data/lib/additional_tags/patches/query_patch.rb +22 -1
  49. data/lib/additional_tags/patches/wiki_page_patch.rb +6 -1
  50. data/lib/additional_tags/plugin_version.rb +1 -1
  51. data/lib/additional_tags/tags.rb +34 -12
  52. data/lib/additional_tags.rb +5 -0
  53. data/package.json +8 -0
  54. metadata +6 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 05656ef90d3c7c796b1bd3d510f6a349fc77c8b1adbf43ca6135af8e48ca705e
4
- data.tar.gz: a22552cd9d096d6963dd9349c207da4755b1e3ba1d8dec560d404b3207ee1458
3
+ metadata.gz: d187e05dfc7655030d62f26afc35392085f43cbac4bb4b4d654da66c643cd58f
4
+ data.tar.gz: 88901a08b7c4b38df8b65fd20282f5e4aae393866e5accf7c3a096d7c82f4788
5
5
  SHA512:
6
- metadata.gz: 2eb18bdccf79be5d1f1b4327c11c3c838e434c8737edcfa096d62d61925170e53be4a6ae45fb486e0c5b3a008933dc30e3bd2c36de9db929fe1fb1e71225adf2
7
- data.tar.gz: 97fd974a51bcdf337c0246cd95c0313639d43aa73f19ac9a81f0f9efa87da0e3c02736970b5c8148566ded5bb3e670a2fb332a03f99d85f59caed219d12410be
6
+ metadata.gz: 34e99763fad968c1f2498dc73b662fb756c4b436e0a4521acd731674c3bebdf8bee52cc55e995457908d39816b61a230fd29d4ac1f3a1662a3e12bb886e6d60e
7
+ data.tar.gz: 4aeb8f7bcbaad55d596dd6f816cedbde29f1c707748ff9ce275f7f131959df258c10e2683ce279cf4c26f8a459d53992ae6e4202d9ff9963a637d88b85661c98
@@ -13,10 +13,10 @@ name: "CodeQL"
13
13
 
14
14
  on:
15
15
  push:
16
- branches: [ master ]
16
+ branches: [ main ]
17
17
  pull_request:
18
18
  # The branches below must be a subset of the branches above
19
- branches: [ master ]
19
+ branches: [ main ]
20
20
  schedule:
21
21
  - cron: '35 20 * * 2'
22
22
 
@@ -38,11 +38,11 @@ jobs:
38
38
 
39
39
  steps:
40
40
  - name: Checkout repository
41
- uses: actions/checkout@v2
41
+ uses: actions/checkout@v3
42
42
 
43
43
  # Initializes the CodeQL tools for scanning.
44
44
  - name: Initialize CodeQL
45
- uses: github/codeql-action/init@v1
45
+ uses: github/codeql-action/init@v2
46
46
  with:
47
47
  languages: ${{ matrix.language }}
48
48
  # If you wish to specify custom queries, you can do so here or in a config file.
@@ -53,7 +53,7 @@ jobs:
53
53
  # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
54
54
  # If this step fails, then you should remove it and run the build manually (see below)
55
55
  - name: Autobuild
56
- uses: github/codeql-action/autobuild@v1
56
+ uses: github/codeql-action/autobuild@v2
57
57
 
58
58
  # ℹ️ Command-line programs to run using the OS shell.
59
59
  # 📚 https://git.io/JvXDl
@@ -67,4 +67,4 @@ jobs:
67
67
  # make release
68
68
 
69
69
  - name: Perform CodeQL Analysis
70
- uses: github/codeql-action/analyze@v1
70
+ uses: github/codeql-action/analyze@v2
@@ -8,7 +8,7 @@ jobs:
8
8
  runs-on: ubuntu-latest
9
9
 
10
10
  steps:
11
- - uses: actions/checkout@v2
11
+ - uses: actions/checkout@v3
12
12
 
13
13
  - name: Install package dependencies
14
14
  run: >
@@ -23,9 +23,8 @@ jobs:
23
23
  uses: ruby/setup-ruby@v1
24
24
  with:
25
25
  ruby-version: 3.1
26
- bundler-cache: true
27
26
 
28
- - name: Setup gems
27
+ - name: Run bundle install
29
28
  run: |
30
29
  bundle install --jobs 4 --retry 3
31
30
 
@@ -41,3 +40,16 @@ jobs:
41
40
  - name: Run Brakeman
42
41
  run: |
43
42
  bundle exec brakeman -5
43
+
44
+ - name: Setup node
45
+ uses: actions/setup-node@v2
46
+ with:
47
+ node-version: '16'
48
+
49
+ - run: yarn install
50
+
51
+ - name: Run Stylelint
52
+ run: node_modules/.bin/stylelint assets/stylesheets/
53
+
54
+ - name: Run ESLint
55
+ run: node_modules/.bin/eslint assets/javascripts/
@@ -10,9 +10,12 @@ jobs:
10
10
 
11
11
  strategy:
12
12
  matrix:
13
- ruby: ['2.7', '3.0', '3.1']
13
+ ruby: ['2.7', '3.0', '3.1', '3.2']
14
14
  redmine: ['5.0-stable', 'master']
15
15
  db: ['postgres', 'mysql']
16
+ exclude:
17
+ - ruby: '3.2'
18
+ redmine: 5.0-stable
16
19
  fail-fast: false
17
20
 
18
21
  services:
@@ -51,20 +54,20 @@ jobs:
51
54
  if: matrix.db == 'mysql'
52
55
 
53
56
  - name: Checkout Redmine
54
- uses: actions/checkout@v2
57
+ uses: actions/checkout@v3
55
58
  with:
56
59
  repository: redmine/redmine
57
60
  ref: ${{ matrix.redmine }}
58
61
  path: redmine
59
62
 
60
63
  - name: Checkout additionals
61
- uses: actions/checkout@v2
64
+ uses: actions/checkout@v3
62
65
  with:
63
66
  repository: AlphaNodes/additionals
64
67
  path: redmine/plugins/additionals
65
68
 
66
69
  - name: Checkout additional_tags
67
- uses: actions/checkout@v2
70
+ uses: actions/checkout@v3
68
71
  with:
69
72
  path: redmine/plugins/additional_tags
70
73
 
data/.gitignore CHANGED
@@ -8,4 +8,6 @@ Gemfile.lock
8
8
  .enable_*
9
9
  ._*
10
10
  *.gem
11
+ /node_modules
12
+ /yarn.lock
11
13
  rails_best_practices_output.html
data/.rubocop.yml CHANGED
@@ -131,3 +131,8 @@ Layout/LineContinuationLeadingSpace:
131
131
  # redmine does not use load_defaults: https://rails.rubystyle.guide/#config-defaults
132
132
  Rails/RedundantPresenceValidationOnBelongsTo:
133
133
  Enabled: false
134
+
135
+ # see https://github.com/rubocop/rubocop-rails/issues/825
136
+ # should be removed and corrected in code, if this bug is fixed
137
+ Rails/ActionControllerFlashBeforeRender:
138
+ Enabled: false
data/.stylelintrc.json CHANGED
@@ -1,163 +1,20 @@
1
1
  {
2
+ "extends": "stylelint-config-standard",
2
3
  "rules": {
3
- "at-rule-no-unknown": true,
4
- "block-no-empty": true,
5
- "color-no-invalid-hex": true,
6
- "comment-no-empty": true,
7
- "declaration-block-no-duplicate-properties": [
4
+ "string-quotes": "single",
5
+ "selector-class-pattern": "^([a-z][a-z0-9]*)(-[a-z0-9]+)*$|block_column",
6
+ "font-family-no-missing-generic-family-keyword": [
8
7
  true,
9
8
  {
10
- "ignore": [
11
- "consecutive-duplicates-with-different-values"
9
+ "ignoreFontFamilies": [
10
+ "Font Awesome 5 Free",
11
+ "Font Awesome 5 Brands",
12
+ "Apple Color Emoji",
13
+ "Segoe UI Emoji",
14
+ "Segoe UI Symbol",
15
+ "Noto Color Emoji"
12
16
  ]
13
17
  }
14
- ],
15
- "declaration-block-no-redundant-longhand-properties": true,
16
- "declaration-block-no-shorthand-property-overrides": true,
17
- "font-family-no-duplicate-names": true,
18
- "font-family-no-missing-generic-family-keyword": true,
19
- "function-calc-no-unspaced-operator": true,
20
- "function-linear-gradient-no-nonstandard-direction": true,
21
- "keyframe-declaration-no-important": true,
22
- "media-feature-name-no-unknown": true,
23
- "no-descending-specificity": true,
24
- "no-duplicate-at-import-rules": true,
25
- "no-duplicate-selectors": true,
26
- "no-empty-source": true,
27
- "no-extra-semicolons": true,
28
- "no-invalid-double-slash-comments": true,
29
- "property-no-unknown": true,
30
- "selector-pseudo-class-no-unknown": true,
31
- "selector-pseudo-element-no-unknown": true,
32
- "selector-type-no-unknown": true,
33
- "string-no-newline": true,
34
- "unit-no-unknown": true,
35
- "at-rule-empty-line-before": [
36
- "always",
37
- {
38
- "except": [
39
- "blockless-after-same-name-blockless",
40
- "first-nested"
41
- ],
42
- "ignore": [
43
- "after-comment"
44
- ]
45
- }
46
- ],
47
- "at-rule-name-case": "lower",
48
- "at-rule-name-space-after": "always-single-line",
49
- "at-rule-semicolon-newline-after": "always",
50
- "block-closing-brace-empty-line-before": "never",
51
- "block-closing-brace-newline-after": "always",
52
- "block-closing-brace-newline-before": "always-multi-line",
53
- "block-closing-brace-space-before": "always-single-line",
54
- "block-opening-brace-newline-after": "always-multi-line",
55
- "block-opening-brace-space-after": "always-single-line",
56
- "block-opening-brace-space-before": "always",
57
- "color-hex-case": "lower",
58
- "color-hex-length": "short",
59
- "comment-empty-line-before": [
60
- "always",
61
- {
62
- "except": [
63
- "first-nested"
64
- ],
65
- "ignore": [
66
- "stylelint-commands"
67
- ]
68
- }
69
- ],
70
- "comment-whitespace-inside": "always",
71
- "custom-property-empty-line-before": [
72
- "always",
73
- {
74
- "except": [
75
- "after-custom-property",
76
- "first-nested"
77
- ],
78
- "ignore": [
79
- "after-comment",
80
- "inside-single-line-block"
81
- ]
82
- }
83
- ],
84
- "declaration-bang-space-after": "never",
85
- "declaration-bang-space-before": "always",
86
- "declaration-block-semicolon-newline-after": "always-multi-line",
87
- "declaration-block-semicolon-space-after": "always-single-line",
88
- "declaration-block-semicolon-space-before": "never",
89
- "declaration-block-single-line-max-declarations": 1,
90
- "declaration-block-trailing-semicolon": "always",
91
- "declaration-colon-newline-after": "always-multi-line",
92
- "declaration-colon-space-after": "always-single-line",
93
- "declaration-colon-space-before": "never",
94
- "declaration-empty-line-before": [
95
- "always",
96
- {
97
- "except": [
98
- "after-declaration",
99
- "first-nested"
100
- ],
101
- "ignore": [
102
- "after-comment",
103
- "inside-single-line-block"
104
- ]
105
- }
106
- ],
107
- "function-comma-newline-after": "always-multi-line",
108
- "function-comma-space-after": "always-single-line",
109
- "function-comma-space-before": "never",
110
- "function-max-empty-lines": 0,
111
- "function-name-case": "lower",
112
- "function-parentheses-newline-inside": "always-multi-line",
113
- "function-parentheses-space-inside": "never-single-line",
114
- "function-whitespace-after": "always",
115
- "indentation": 2,
116
- "length-zero-no-unit": true,
117
- "max-empty-lines": 1,
118
- "media-feature-colon-space-after": "always",
119
- "media-feature-colon-space-before": "never",
120
- "media-feature-name-case": "lower",
121
- "media-feature-parentheses-space-inside": "never",
122
- "media-feature-range-operator-space-after": "always",
123
- "media-feature-range-operator-space-before": "always",
124
- "media-query-list-comma-newline-after": "always-multi-line",
125
- "media-query-list-comma-space-after": "always-single-line",
126
- "media-query-list-comma-space-before": "never",
127
- "no-eol-whitespace": true,
128
- "no-missing-end-of-source-newline": true,
129
- "number-leading-zero": "always",
130
- "number-no-trailing-zeros": true,
131
- "property-case": "lower",
132
- "rule-empty-line-before": [
133
- "always-multi-line",
134
- {
135
- "except": [
136
- "first-nested"
137
- ],
138
- "ignore": [
139
- "after-comment"
140
- ]
141
- }
142
- ],
143
- "selector-attribute-brackets-space-inside": "never",
144
- "selector-attribute-operator-space-after": "never",
145
- "selector-attribute-operator-space-before": "never",
146
- "selector-combinator-space-after": "always",
147
- "selector-combinator-space-before": "always",
148
- "selector-descendant-combinator-no-non-space": true,
149
- "selector-list-comma-newline-after": "always",
150
- "selector-list-comma-space-before": "never",
151
- "selector-max-empty-lines": 0,
152
- "selector-pseudo-class-case": "lower",
153
- "selector-pseudo-class-parentheses-space-inside": "never",
154
- "selector-pseudo-element-case": "lower",
155
- "selector-pseudo-element-colon-notation": "double",
156
- "selector-type-case": "lower",
157
- "unit-case": "lower",
158
- "value-list-comma-newline-after": "always-multi-line",
159
- "value-list-comma-space-after": "always-single-line",
160
- "value-list-comma-space-before": "never",
161
- "value-list-max-empty-lines": 0
18
+ ]
162
19
  }
163
20
  }
data/README.md CHANGED
@@ -4,16 +4,35 @@
4
4
 
5
5
  ## Features
6
6
 
7
- - Tags for issues
8
- - Tags for wiki pages
7
+ - Tags for issues. To use them you need to:
8
+ - *Activate issue tags* in the plugin configuration
9
+ - and update your role permissions in the Redmine administration *Roles & permissions / Issue tracking*.
10
+ - Tags for wiki pages. To use them you need to:
11
+ - *Activate wiki tags* in the plugin configuration
12
+ - and update your role permissions in the Redmine administration *Roles & permissions / Wiki*
13
+ - Available role permissions for issue tags (section *Issue tracking*):
14
+ - Add issue tags
15
+ - Edit issue tags
16
+ - Display issue tags
17
+ - Available role permissions wiki tags (section *Wiki*):
18
+ - Add wiki tags
19
+ - Managing tags centrally in the plugin settings (edit, delete, merge)-
20
+ - Grouped tags.
21
+ - Grouping of tags possible, when using a colon in tag (all tags with same base name get the same color). Typo example: ``Plugin:HRM``
22
+ - Scoped tags:
23
+ - Grouping of tags via *Scoped tags* possible, when using two colons in tag. Typo example: ``Product::Sprint 1``
24
+ - Only one tag of the same base name is allowed for an entity
25
+ - Base name and tag value are displayed seperatly
9
26
  - Accented and non-latin characters supported for tag order
10
- - View, edit and create tag permissions for issues
11
- - Create permission for wiki tags
12
- - Managing tags
13
- - Custom tags and tagging tables (additional_tags and additional_taggings). If a other plugin
14
- used tags or tagging tables for issue or wiki tagging, there tags will be migrated automatically
27
+ - Color theme selection possible
28
+ - Custom tags and tagging tables (additional_tags and additional_taggings). If another plugin
29
+ used tags or tagging tables for issue or wiki tagging, tags will be migrated automatically there
15
30
  - Based on the very popular [acts-as-taggable-on](https://github.com/mbleigh/acts-as-taggable-on)
16
31
 
32
+ ![screenshot](https://raw.githubusercontent.com/AlphaNodes/additional_tags/master/doc/images/tag-overview.png)
33
+
34
+ The screenshot shows: regular tags, grouped tags and scoped tags. The colors are assigned randomly. But you can change the color by choosing a *Color theme* in the plugin settings.
35
+
17
36
  ![screenshot](https://raw.githubusercontent.com/AlphaNodes/additional_tags/master/doc/images/additional-tags.gif)
18
37
 
19
38
  Other plugins use additional_tags as framework in order to support tags for their entities.
@@ -33,15 +33,6 @@ class AdditionalTagsController < ApplicationController
33
33
 
34
34
  def edit; end
35
35
 
36
- def destroy
37
- @tags.each do |tag|
38
- tag.reload.destroy!
39
- rescue ::ActiveRecord::RecordNotFound, ::ActiveRecord::RecordNotDestroyed
40
- Rails.logger.warn "Tag #{tag} could not be deleted"
41
- end
42
- redirect_back_or_default @tag_list_path
43
- end
44
-
45
36
  def update
46
37
  @tag.name = params[:tag][:name] if params[:tag]
47
38
  if @tag.save
@@ -59,6 +50,15 @@ class AdditionalTagsController < ApplicationController
59
50
  end
60
51
  end
61
52
 
53
+ def destroy
54
+ @tags.each do |tag|
55
+ tag.reload.destroy!
56
+ rescue ::ActiveRecord::RecordNotFound, ::ActiveRecord::RecordNotDestroyed
57
+ Rails.logger.warn "Tag #{tag} could not be deleted"
58
+ end
59
+ redirect_back_or_default @tag_list_path
60
+ end
61
+
62
62
  def context_menu
63
63
  @tag = @tags.first if @tags.size == 1
64
64
  @back = back_url
@@ -19,6 +19,7 @@ class IssueTagsController < ApplicationController
19
19
 
20
20
  @issue_tags.sort!
21
21
  @most_used_tags = Issue.available_tags.most_used 10
22
+ @append = params[:append] == 'true'
22
23
  end
23
24
 
24
25
  def update
@@ -33,7 +34,8 @@ class IssueTagsController < ApplicationController
33
34
 
34
35
  Issue.transaction do
35
36
  @issues.each do |issue|
36
- issue.tag_list = tags
37
+ # add tags added in placeholder for a single/multiple issue or overwrite tags for single issue
38
+ params[:append] == 'true' ? issue.tag_list << tags : issue.tag_list = tags
37
39
  issue.save!
38
40
  end
39
41
  end
@@ -66,7 +66,11 @@ module AdditionalTagsHelper
66
66
  def render_tags_list(tags, **options)
67
67
  return if tags.blank?
68
68
 
69
- style = options.delete :style
69
+ options[:show_count] = AdditionalTags.setting? :show_with_count unless options.key? :show_count
70
+ options[:color_theme] = AdditionalTags.setting :tags_color_theme unless options.key? :color_theme
71
+ options[:use_colors] = AdditionalTags.use_colors? unless options.key? :use_colors
72
+
73
+ style = options.key?(:style) ? options.delete(:style) : AdditionalTags.setting(:tags_sidebar).to_sym
70
74
  tags = tags.all.to_a if tags.respond_to? :all
71
75
  tags = sort_tags_for_list tags
72
76
 
@@ -94,22 +98,36 @@ module AdditionalTagsHelper
94
98
  content_tag(list_el, content, class: 'tags-cloud', style: (style == :simple_cloud ? 'text-align: left;' : ''))
95
99
  end
96
100
 
97
- def additional_tag_link(tag_object, link: nil, link_wiki_tag: false, show_count: false, use_colors: nil, name: nil, **options)
98
- tag_name = []
99
- tag_name << if name.nil?
100
- tag_object.name
101
- else
102
- name
103
- end
104
-
101
+ def additional_tag_link(tag_object,
102
+ link: nil,
103
+ link_wiki_tag: false,
104
+ show_count: false,
105
+ use_colors: nil,
106
+ name: nil,
107
+ color_theme: nil,
108
+ **options)
105
109
  options[:project] = @project if options[:project].blank? && @project.present?
106
- use_colors = AdditionalTags.setting? :use_colors if use_colors.nil?
110
+ if !options.key?(:display_type) && @query && @query.display_type != @query.default_display_type
111
+ options[:display_type] = @query.display_type
112
+ end
113
+
114
+ use_colors = AdditionalTags.use_colors? if use_colors.nil?
115
+ color_theme = AdditionalTags.setting :tags_color_theme if color_theme.nil?
116
+
117
+ tag_info = AdditionalTag.new name: name.nil? ? tag_object.name : name,
118
+ disable_grouping: !use_colors,
119
+ color_theme: color_theme
120
+ tag_name = [tag_info.tag_name]
121
+
122
+ tag_style = "background-color: #{tag_info.tag_bg_color}; color: #{tag_info.tag_fg_color}" if use_colors
107
123
 
108
- tag_style = if use_colors
109
- tag_bg_color = additional_tag_color tag_object.name
110
- tag_fg_color = additional_tag_fg_color tag_bg_color
111
- "background-color: #{tag_bg_color}; color: #{tag_fg_color}"
112
- end
124
+ if tag_info.scoped?
125
+ tag_name << if show_count
126
+ tag.span tag_info.group_value, class: 'tag-group-value'
127
+ else
128
+ tag.span tag_info.group_value, class: 'tag-group-value tag-group-nocount'
129
+ end
130
+ end
113
131
 
114
132
  tag_name << tag.span(tag_object.count, class: 'tag-count') if show_count
115
133
 
@@ -139,20 +157,6 @@ module AdditionalTagsHelper
139
157
  tag.span content, **style
140
158
  end
141
159
 
142
- def additional_tag_color(tag_name)
143
- "##{Digest::SHA256.hexdigest(tag_name)[0..5]}"
144
- end
145
-
146
- def additional_tag_fg_color(bg_color)
147
- # calculate contrast text color according to YIQ method
148
- # https://24ways.org/2010/calculating-color-contrast/
149
- # https://stackoverflow.com/questions/3942878/how-to-decide-font-color-in-white-or-black-depending-on-background-color
150
- r = bg_color[1..2].hex
151
- g = bg_color[3..4].hex
152
- b = bg_color[5..6].hex
153
- (r * 299 + g * 587 + b * 114) >= 128_000 ? 'black' : 'white'
154
- end
155
-
156
160
  # plain list of tags
157
161
  def additional_plain_tag_list(tags, sep: nil)
158
162
  sep ||= "#{Query.additional_csv_separator} "
@@ -177,6 +181,10 @@ module AdditionalTagsHelper
177
181
  unsorted = options.delete :unsorted
178
182
  tag_list = AdditionalTags::Tags.sort_tag_list tag_list unless unsorted
179
183
 
184
+ # set defaults if not defined
185
+ options[:use_colors] = AdditionalTags.use_colors? unless options.key? :use_colors
186
+ options[:color_theme] = AdditionalTags.setting :tags_color_theme unless options.key? :color_theme
187
+
180
188
  safe_join tag_list.map { |tag| additional_tag_link tag, **options },
181
189
  additional_tag_sep(use_colors: options[:use_colors])
182
190
  end
@@ -208,7 +216,7 @@ module AdditionalTagsHelper
208
216
 
209
217
  private
210
218
 
211
- def tag_url(tag_name, filter: nil, tag_action: nil, tag_controller: nil, project: nil)
219
+ def tag_url(tag_name, filter: nil, tag_action: nil, tag_controller: nil, project: nil, display_type: nil)
212
220
  action = tag_action.presence || (controller_name == 'hrm_user_resources' ? 'show' : 'index')
213
221
 
214
222
  fields = [:tags]
@@ -225,6 +233,7 @@ module AdditionalTagsHelper
225
233
  { controller: tag_controller.presence || controller_name,
226
234
  action: action,
227
235
  set_filter: 1,
236
+ display_type: display_type,
228
237
  project_id: project,
229
238
  f: fields,
230
239
  v: values,
@@ -44,9 +44,7 @@ module AdditionalTagsIssuesHelper
44
44
  end
45
45
 
46
46
  def render_sidebar_tags
47
- options = { show_count: AdditionalTags.setting?(:show_with_count),
48
- filter: AdditionalTags.setting?(:open_issues_only) ? { field: :status_id, operator: 'o' } : nil,
49
- style: AdditionalTags.setting(:tags_sidebar).to_sym,
47
+ options = { filter: AdditionalTags.setting?(:open_issues_only) ? { field: :status_id, operator: 'o' } : nil,
50
48
  project: @project }
51
49
 
52
50
  options[:tag_action] = 'show' if %w[gantts calendars].include? controller_name
@@ -10,9 +10,7 @@ module AdditionalTagsWikiHelper
10
10
  end
11
11
 
12
12
  def render_sidebar_tags
13
- options = { show_count: AdditionalTags.setting?(:show_with_count),
14
- style: AdditionalTags.setting(:tags_sidebar).to_sym,
15
- link_wiki_tag: true,
13
+ options = { link_wiki_tag: true,
16
14
  project: @project }
17
15
 
18
16
  render_tags_list sidebar_tags, **options
@@ -20,12 +18,15 @@ module AdditionalTagsWikiHelper
20
18
 
21
19
  def render_wiki_index_title(project: nil, name: nil, tag: nil, title: :label_wiki)
22
20
  if tag.present?
21
+ tag_object = ActsAsTaggableOn::Tag.new name: tag
22
+
23
23
  if project
24
- t :label_wiki_index_for_tag_html, tag: tag
24
+ safe_join [l(:label_wiki_index_for_tag), additional_tag_link(tag_object, link: '#')], ' '
25
25
  else
26
26
  title = [link_to(l(title), wiki_index_path)]
27
27
  title << Additionals::LIST_SEPARATOR
28
- title << t(:label_wiki_index_for_tag_html, tag: tag)
28
+ title << l(:label_wiki_index_for_tag)
29
+ title << additional_tag_link(tag_object, link: '#')
29
30
  safe_join title, ' '
30
31
  end
31
32
  elsif name.present?
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AdditionalTag
4
+ GROUP_SEP = ':'
5
+ SCOPE_SEP = '::'
6
+
7
+ class << self
8
+ def valid_mutually_exclusive_tag(tag_list)
9
+ return true if tag_list.blank?
10
+
11
+ tags = tag_list.select { |t| t.include? SCOPE_SEP }
12
+ return true if tags.blank?
13
+
14
+ groups = tags.map { |t| new(name: t).group_name }
15
+ groups == groups.uniq
16
+ end
17
+ end
18
+
19
+ # NOTE: only use bg_color parameter, if background color should not
20
+ # calculated by AdditionalTag - if you want to assign manual color
21
+ def initialize(name:, disable_grouping: false, color_theme: nil, bg_color: nil)
22
+ @tag_name = name.to_s
23
+ @disable_grouping = disable_grouping
24
+ @color_theme = color_theme.to_s
25
+ @bg_color = bg_color
26
+ end
27
+
28
+ def name_for_color
29
+ # different colors for non-grouped, grouped and scoped tag
30
+ name = if scoped? || grouped?
31
+ "#{group_name}#{sep}"
32
+ else
33
+ tag_name
34
+ end
35
+
36
+ if @color_theme.present? && @color_theme != '0' && @color_theme != '1'
37
+ "#{name}#{@color_theme}"
38
+ else
39
+ name
40
+ end
41
+ end
42
+
43
+ def tag_bg_color
44
+ @tag_bg_color ||= @bg_color || "##{Digest::SHA256.hexdigest(name_for_color)[0..5]}"
45
+ end
46
+
47
+ # calculate contrast text color according to YIQ method
48
+ # https://24ways.org/2010/calculating-color-contrast/
49
+ # https://stackoverflow.com/questions/3942878/how-to-decide-font-color-in-white-or-black-depending-on-background-color
50
+ def tag_fg_color
51
+ @tag_fg_color ||= begin
52
+ r = tag_bg_color[1..2].hex
53
+ g = tag_bg_color[3..4].hex
54
+ b = tag_bg_color[5..6].hex
55
+ (r * 299 + g * 587 + b * 114) >= 128_000 ? 'black' : 'white'
56
+ end
57
+ end
58
+
59
+ def sep
60
+ scoped? ? SCOPE_SEP : GROUP_SEP
61
+ end
62
+
63
+ def tag_name
64
+ scoped? ? group_name : @tag_name
65
+ end
66
+
67
+ def labels
68
+ @labels ||= scoped? ? scope_labels : group_labels
69
+ end
70
+
71
+ def scope_labels
72
+ @scope_labels ||= @tag_name.split(SCOPE_SEP).map(&:strip)
73
+ end
74
+
75
+ def group_labels
76
+ @group_labels ||= @tag_name.split(GROUP_SEP).map(&:strip)
77
+ end
78
+
79
+ def group_name
80
+ if labels.length > 2
81
+ labels[0...-1].join sep
82
+ else
83
+ labels.first
84
+ end
85
+ end
86
+
87
+ def group_value
88
+ labels.last
89
+ end
90
+
91
+ def scoped?
92
+ !@disable_grouping && scope_labels.length > 1
93
+ end
94
+
95
+ def grouped?
96
+ !@disable_grouping && group_labels.length > 1
97
+ end
98
+ end