additional_tags 1.0.6 → 3.0.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +26 -7
  3. data/app/controllers/additional_tags_controller.rb +9 -9
  4. data/app/controllers/issue_tags_controller.rb +6 -1
  5. data/app/helpers/additional_tags_helper.rb +41 -32
  6. data/app/helpers/additional_tags_issues_helper.rb +13 -5
  7. data/app/helpers/additional_tags_wiki_helper.rb +6 -5
  8. data/app/models/additional_tag.rb +98 -0
  9. data/app/views/additional_tags/_tag_list.html.slim +10 -4
  10. data/app/views/additional_tags/merge.html.slim +0 -1
  11. data/app/views/additional_tags/settings/_general.html.slim +7 -1
  12. data/app/views/additional_tags/settings/_manage_tags.html.slim +8 -1
  13. data/app/views/context_menus/_issues_tags.html.slim +7 -1
  14. data/app/views/dashboards/blocks/_issue_tags.html.slim +3 -1
  15. data/app/views/issue_tags/_edit_modal.html.slim +7 -7
  16. data/app/views/issues/_tags.html.slim +6 -1
  17. data/app/views/issues/_tags_bulk_edit.html.slim +2 -1
  18. data/app/views/wiki/_tags_form.html.slim +4 -0
  19. data/app/views/wiki/_tags_show.html.slim +1 -1
  20. data/assets/javascripts/tags.js +3 -3
  21. data/assets/stylesheets/tags.css +41 -16
  22. data/config/locales/bg.yml +17 -11
  23. data/config/locales/cs.yml +18 -12
  24. data/config/locales/de.yml +18 -12
  25. data/config/locales/en.yml +18 -12
  26. data/config/locales/es.yml +18 -12
  27. data/config/locales/fr.yml +18 -12
  28. data/config/locales/it.yml +18 -12
  29. data/config/locales/ja.yml +17 -11
  30. data/config/locales/ko.yml +17 -11
  31. data/config/locales/pl.yml +18 -12
  32. data/config/locales/pt-BR.yml +17 -11
  33. data/config/locales/ru.yml +33 -27
  34. data/config/settings.yml +5 -5
  35. data/lib/additional_tags/hooks/view_hook.rb +17 -5
  36. data/lib/additional_tags/patches/issue_patch.rb +54 -9
  37. data/lib/additional_tags/patches/issue_query_patch.rb +18 -0
  38. data/lib/additional_tags/patches/queries_helper_patch.rb +13 -3
  39. data/lib/additional_tags/patches/query_patch.rb +23 -2
  40. data/lib/additional_tags/patches/wiki_page_patch.rb +6 -1
  41. data/lib/additional_tags/plugin_version.rb +1 -1
  42. data/lib/additional_tags/tags.rb +35 -13
  43. data/lib/additional_tags.rb +5 -0
  44. metadata +5 -44
  45. data/.eslintrc.yml +0 -17
  46. data/.github/workflows/codeql-analysis.yml +0 -70
  47. data/.github/workflows/linters.yml +0 -43
  48. data/.github/workflows/tests.yml +0 -134
  49. data/.gitignore +0 -11
  50. data/.rubocop.yml +0 -133
  51. data/.slim-lint.yml +0 -27
  52. data/.stylelintrc.json +0 -163
  53. data/Rakefile +0 -13
  54. data/additional_tags.gemspec +0 -32
  55. data/doc/images/additional-tags-framework.png +0 -0
  56. data/doc/images/additional-tags.gif +0 -0
@@ -1,38 +1,44 @@
1
1
  pl:
2
+ activerecord:
3
+ errors:
4
+ messages:
5
+ invalid_mutually_exclusive_tags: "używa wzajemnie wykluczających się tagów"
6
+ field_issue_tags: "Znaczniki emisji"
2
7
  field_tag_list: Tags
3
8
  field_tags: Tags
4
- field_issue_tags: Issue tags
5
9
  label_active_issue_tags: "Aktywacja znaczników emisji"
6
10
  label_active_wiki_tags: "Aktywacja tagów wiki"
7
11
  label_add_tags: "Dodaj znaczniki"
12
+ label_amount_entities_with_tags: "Kwota %{name} z Tags"
13
+ label_amount_tags: "Kwota Tags"
8
14
  label_edit_tags: "Edycja tagów"
9
15
  label_manage_tags: "Zarządzaj tagami"
10
16
  label_merge_selected_tags: "Łączenie wybranych tagów"
11
17
  label_open_issues_only: "Wyświetlaj tylko sprawy otwarte"
12
18
  label_show_with_count: "Wyświetlanie ilości na etykiecie"
19
+ label_tag_color_theme: "Kolorowy motyw %{value}"
20
+ label_tags_colors: Kolor
21
+ label_tags_sidebar: "Wyświetlać znaczniki na pasku bocznym jako"
22
+ label_tags_sort_by: "Sortuj znaczniki według"
23
+ label_tags_suggestion_order: "Zamówienie ofertowe"
13
24
  label_tags_tag: "Tag"
14
- label_use_colors: "Używaj kolorów"
15
- label_wiki_index_for_tag_html: "Strony Wiki z tagiem <em>%{tag}</em>"
25
+ label_tags_without_color: "Bez koloru"
26
+ label_use_colors: "Użyj kolorów"
27
+ label_wiki_index_for_tag: "Strony Wiki z tagiem"
28
+ label_with_chart: "Z wykresem"
29
+ label_with_table_of_values: "Z tabelą wartości"
16
30
  notice_failed_to_add_tags: "Nie dodano znaczników"
17
31
  notice_tags_added: "Tagi dodane"
18
32
  permission_add_wiki_tags: "Dodaj znaczniki wiki"
19
33
  permission_create_issue_tags: "Dodaj znaczniki wydania"
20
34
  permission_edit_issue_tags: "Edycja znaczników wydania"
21
35
  permission_view_issue_tags: "Wyświetlanie znaczników emisji"
22
- tags_order_by_last_created: "Ostatnio stworzony"
23
36
  tags_order_by_count: "Najczęściej używany"
37
+ tags_order_by_last_created: "Ostatnio stworzony"
24
38
  tags_order_by_name: "Nazwa"
25
39
  tags_sidebar_cloud: "Cloud"
26
40
  tags_sidebar_list: "Lista"
27
41
  tags_sidebar_none: "Brak"
28
42
  tags_sidebar_simple_cloud: "Simple cloud"
29
- label_tags_sidebar: "Wyświetlać znaczniki na pasku bocznym jako"
30
43
  tags_sort_by_count: "Hrabia"
31
44
  tags_sort_by_name: "Nazwa"
32
- label_tags_sort_by: "Sortuj znaczniki według"
33
- label_tags_suggestion_order: "Zamówienie ofertowe"
34
- label_with_table_of_values: "Z tabelą wartości"
35
- label_with_chart: "Z wykresem"
36
- label_amount_tags: "Kwota Tags"
37
- label_amount_entities_with_tags: "Kwota %{name} z Tags"
38
- label_quantity: "Ilość"
@@ -1,38 +1,44 @@
1
1
  pt-BR:
2
+ activerecord:
3
+ errors:
4
+ messages:
5
+ invalid_mutually_exclusive_tags: "utiliza etiquetas mutuamente exclusivas"
6
+ field_issue_tags: Issue tags
2
7
  field_tag_list: Tags
3
8
  field_tags: Tags
4
- field_issue_tags: Issue tags
5
9
  label_active_issue_tags: "Ativar as etiquetas de emissão"
6
10
  label_active_wiki_tags: "Ativar as etiquetas wiki"
7
11
  label_add_tags: "Adicionar etiquetas"
12
+ label_amount_entities_with_tags: "Quantidade %{name} com etiquetas"
13
+ label_amount_tags: "Valor Etiquetas"
8
14
  label_edit_tags: "Editar etiquetas"
9
15
  label_manage_tags: "Gerenciar etiquetas"
10
16
  label_merge_selected_tags: "Fundir tags selecionadas"
11
17
  label_open_issues_only: "Exibir apenas questões em aberto"
12
18
  label_show_with_count: "Mostrar quantidade na etiqueta"
19
+ label_tag_color_theme: "Tema da cor %{value}"
20
+ label_tags_colors: "Cor"
21
+ label_tags_sidebar: "Mostrar etiquetas na barra lateral como"
22
+ label_tags_sort_by: "Ordenar tags por"
23
+ label_tags_suggestion_order: "Pedido de sugestão"
13
24
  label_tags_tag: "Tag"
25
+ label_tags_without_color: "Sem cor"
14
26
  label_use_colors: "Usar cores"
15
- label_wiki_index_for_tag_html: "Páginas Wiki com tag <em>%{tag}</em>"
27
+ label_wiki_index_for_tag: "Páginas Wiki com tag"
28
+ label_with_chart: "Com gráfico"
29
+ label_with_table_of_values: "Com tabela de valores"
16
30
  notice_failed_to_add_tags: "Falha em adicionar tags"
17
31
  notice_tags_added: "Tags adicionadas"
18
32
  permission_add_wiki_tags: "Adicionar etiquetas wiki"
19
33
  permission_create_issue_tags: "Adicionar etiquetas de edição"
20
34
  permission_edit_issue_tags: "Editar etiquetas de edição"
21
35
  permission_view_issue_tags: "Exibir etiquetas de edição"
22
- tags_order_by_last_created: "Última criação"
23
36
  tags_order_by_count: "Mais utilizados"
37
+ tags_order_by_last_created: "Última criação"
24
38
  tags_order_by_name: "Nome"
25
39
  tags_sidebar_cloud: "Cloud"
26
40
  tags_sidebar_list: "Lista"
27
41
  tags_sidebar_none: "Nenhum"
28
42
  tags_sidebar_simple_cloud: "Simple cloud"
29
- label_tags_sidebar: "Mostrar etiquetas na barra lateral como"
30
43
  tags_sort_by_count: "Conde"
31
44
  tags_sort_by_name: "Nome"
32
- label_tags_sort_by: "Ordenar tags por"
33
- label_tags_suggestion_order: "Pedido de sugestão"
34
- label_with_table_of_values: "Com tabela de valores"
35
- label_with_chart: "Com gráfico"
36
- label_amount_tags: "Valor Etiquetas"
37
- label_amount_entities_with_tags: "Quantidade %{name} com etiquetas"
38
- label_quantity: "Quantidade"
@@ -1,38 +1,44 @@
1
1
  ru:
2
- field_tag_list: Теги
3
- field_tags: Теги
4
- field_issue_tags: Теги задач
5
- label_active_issue_tags: "Активировать теги задач"
6
- label_active_wiki_tags: "Активировать теги вики"
7
- label_add_tags: "Добавьте теги"
8
- label_edit_tags: "Редактировать теги"
9
- label_manage_tags: "Управление тегами"
10
- label_merge_selected_tags: "Объединить выбранные теги"
2
+ activerecord:
3
+ errors:
4
+ messages:
5
+ invalid_mutually_exclusive_tags: "использует взаимоисключающие метки"
6
+ field_issue_tags: "Метки задач"
7
+ field_tag_list: "Метки"
8
+ field_tags: "Метки"
9
+ label_active_issue_tags: "Активировать метки задач"
10
+ label_active_wiki_tags: "Активировать метки вики"
11
+ label_add_tags: "Добавьте метки"
12
+ label_amount_entities_with_tags: "Сумма %{name} с метками"
13
+ label_amount_tags: "Метки Сумма"
14
+ label_edit_tags: "Редактировать метки"
15
+ label_manage_tags: "Управление метками"
16
+ label_merge_selected_tags: "Объединить выбранные метки"
11
17
  label_open_issues_only: "Отображать только открытые задачи"
12
- label_show_with_count: "Отображать количество на теге"
13
- label_tags_tag: "Тег"
18
+ label_show_with_count: "Отображать количество на метке"
19
+ label_tag_color_theme: "Цветовая тема %{value}"
20
+ label_tags_colors: "Цвет"
21
+ label_tags_sidebar: "Способ отображения меток на боковой панели"
22
+ label_tags_sort_by: "Сортировать метки по"
23
+ label_tags_suggestion_order: "Порядок предложения при вводе"
24
+ label_tags_tag: "Метка"
25
+ label_tags_without_color: "Без цвета"
14
26
  label_use_colors: "Использовать цвета"
15
- label_wiki_index_for_tag_html: "Вики-страницы с тегом <em>%{tag}</em>"
16
- notice_failed_to_add_tags: "Не удалось добавить теги"
17
- notice_tags_added: "Теги добавлены"
18
- permission_add_wiki_tags: "Добавление тегов вики"
19
- permission_create_issue_tags: "Добавление тегов задач"
20
- permission_edit_issue_tags: "Редактирование тегов задач"
21
- permission_view_issue_tags: "Просмотр тегов задач"
22
- tags_order_by_last_created: "Недавно созданные"
27
+ label_wiki_index_for_tag: "Вики-страницы с меткой"
28
+ label_with_chart: "С графиком"
29
+ label_with_table_of_values: "С таблицей значений"
30
+ notice_failed_to_add_tags: "Не удалось добавить метки"
31
+ notice_tags_added: "Метки добавлены"
32
+ permission_add_wiki_tags: "Добавление меток вики"
33
+ permission_create_issue_tags: "Добавление меток задач"
34
+ permission_edit_issue_tags: "Редактирование меток задач"
35
+ permission_view_issue_tags: "Просмотр меток задач"
23
36
  tags_order_by_count: "Наиболее часто используемые"
37
+ tags_order_by_last_created: "Недавно созданные"
24
38
  tags_order_by_name: "Имя"
25
39
  tags_sidebar_cloud: "Облако"
26
40
  tags_sidebar_list: "Список"
27
41
  tags_sidebar_none: "Нет"
28
42
  tags_sidebar_simple_cloud: "Простое облако"
29
- label_tags_sidebar: "Способ отображения тегов на боковой панели"
30
43
  tags_sort_by_count: "Количеству"
31
44
  tags_sort_by_name: "Имени"
32
- label_tags_sort_by: "Сортировать теги по"
33
- label_tags_suggestion_order: "Порядок предложения при вводе"
34
- label_with_table_of_values: "С таблицей значений"
35
- label_with_chart: "С графиком"
36
- label_amount_tags: "Теги Сумма"
37
- label_amount_entities_with_tags: "Сумма %{name} с тегами"
38
- label_quantity: "Количество"
data/config/settings.yml CHANGED
@@ -1,9 +1,9 @@
1
- active_issue_tags: 0
2
- active_wiki_tags: 0
3
- open_issues_only: 0
4
- show_with_count: 0
1
+ active_issue_tags: '0'
2
+ active_wiki_tags: '0'
3
+ open_issues_only: '0'
4
+ show_with_count: '0'
5
+ tags_color_theme: '1'
5
6
  tags_sidebar: 'none'
6
7
  tags_sort_by: 'name'
7
8
  tags_sort_order: 'asc'
8
9
  tags_suggestion_order: 'name'
9
- use_colors: 1
@@ -63,16 +63,28 @@ module AdditionalTags
63
63
  def issues_bulk_tags_fix(issue, params)
64
64
  return unless params && params[:issue]
65
65
 
66
- old_tags = issue.tags.map(&:name)
67
- new_tags = Array(params[:issue][:tag_list]).reject(&:empty?)
68
- issue.tag_list = (old_tags + new_tags).uniq
66
+ common_tags = if params[:common_tags].present?
67
+ params[:common_tags].split(ActsAsTaggableOn.delimiter).map(&:strip)
68
+ else
69
+ []
70
+ end
71
+
72
+ current_tags = ActsAsTaggableOn::TagList.new issue.tags.to_a
73
+ new_tags = Array(params[:issue][:tag_list]).compact_blank
74
+
75
+ tags_to_add = new_tags - common_tags
76
+ tags_to_remove = common_tags - new_tags
77
+
78
+ current_tags.add tags_to_add
79
+ current_tags.remove tags_to_remove
80
+
81
+ issue.tag_list = current_tags
69
82
  end
70
83
 
71
84
  def tags_journal(issue, params)
72
85
  return unless params && params[:issue] && params[:issue][:tag_list]
73
86
 
74
- issue.tags_to_journal Issue.find_by(id: issue.id)&.tag_list&.to_s,
75
- issue.tag_list.to_s
87
+ issue.tags_to_journal issue.tag_list_was&.to_s, issue.tag_list.to_s
76
88
  end
77
89
  end
78
90
  end
@@ -13,6 +13,8 @@ module AdditionalTags
13
13
  before_save :prepare_save_tag_change
14
14
  before_save :sort_tag_list
15
15
 
16
+ validate :validate_tags, if: proc { AdditionalTags.setting?(:active_issue_tags) }
17
+
16
18
  after_commit :add_remove_unused_tags_job, on: %i[update destroy],
17
19
  if: proc { AdditionalTags.setting?(:active_issue_tags) }
18
20
 
@@ -29,21 +31,58 @@ module AdditionalTags
29
31
  tags.all? { |tag| allowed_tags.include? tag }
30
32
  end
31
33
 
32
- def group_by_status_with_tags(project = nil)
33
- visible(User.current, project: project).joins(:status)
34
- .joins(:tags)
35
- .group(:is_closed, 'tag_id')
36
- .count
34
+ def group_by_status_with_tags(project = nil, user = User.current)
35
+ scope = if project && Setting.display_subprojects_issues?
36
+ visible(user).where(AdditionalTags::Tags.subproject_sql(project))
37
+ else
38
+ visible user, project: project
39
+ end
40
+
41
+ scope.joins(:status)
42
+ .joins(:tags)
43
+ .group(:is_closed, 'tag_id')
44
+ .count
37
45
  end
38
46
 
39
47
  def available_tags(**options)
40
- options[:permission] = :view_issues
48
+ options[:permission] ||= :view_issue_tags
41
49
  tags = AdditionalTags::Tags.available_tags self, **options
42
50
  return tags unless options[:open_issues_only]
43
51
 
44
52
  tags.joins("JOIN #{IssueStatus.table_name} ON #{IssueStatus.table_name}.id = #{table_name}.status_id")
45
53
  .where(issue_statuses: { is_closed: false })
46
54
  end
55
+
56
+ def common_tag_list_from_issues(ids)
57
+ common_tags = ActsAsTaggableOn::Tag.joins(:taggings)
58
+ .select(
59
+ "#{ActiveRecord::Base.connection.quote_table_name ActsAsTaggableOn.tags_table}.id",
60
+ "#{ActiveRecord::Base.connection.quote_table_name ActsAsTaggableOn.tags_table}.name"
61
+ )
62
+ .where(taggings: { taggable_type: 'Issue', taggable_id: ids })
63
+ .group(
64
+ "#{ActiveRecord::Base.connection.quote_table_name ActsAsTaggableOn.tags_table}.id",
65
+ "#{ActiveRecord::Base.connection.quote_table_name ActsAsTaggableOn.tags_table}.name"
66
+ )
67
+ .having('count(*) = ?', ids.count).to_a
68
+
69
+ ActsAsTaggableOn::TagList.new common_tags
70
+ end
71
+
72
+ def load_visible_tags(issues, user = User.current)
73
+ return if issues.blank?
74
+
75
+ available_projects = Project.where(AdditionalTags::Tags.visible_condition(user)).ids
76
+
77
+ issues.each do |issue|
78
+ tags = if available_projects.include? issue.project_id
79
+ issue.tags
80
+ else
81
+ []
82
+ end
83
+ issue.instance_variable_set :@visible_tags, tags
84
+ end
85
+ end
47
86
  end
48
87
 
49
88
  module InstanceMethods
@@ -62,8 +101,6 @@ module AdditionalTags
62
101
  tags = attrs.delete :tag_list
63
102
  tags = Array(tags).reject(&:empty?)
64
103
 
65
- Additionals.debug "tags: #{tags.inspect} - project: #{project&.id} - access: #{user.allowed_to? :create_issue_tags, project}"
66
-
67
104
  if user.allowed_to?(:create_issue_tags, project) ||
68
105
  user.allowed_to?(:edit_issue_tags, project) && Issue.allowed_tags?(tags)
69
106
  attrs[:tag_list] = tags # required fix for journal details
@@ -76,7 +113,8 @@ module AdditionalTags
76
113
 
77
114
  copy_from_without_tags arg, **options
78
115
  issue = arg.is_a?(Issue) ? arg : Issue.visible.find(arg)
79
- self.tag_list = issue.tag_list
116
+ self.tags = issue.tags # required for bulk copy
117
+ self.tag_list = tags.map(&:name) # required for copy
80
118
  self
81
119
  end
82
120
 
@@ -88,6 +126,13 @@ module AdditionalTags
88
126
 
89
127
  self.tag_list = AdditionalTags::Tags.sort_tags tags
90
128
  end
129
+
130
+ def validate_tags
131
+ return if !User.current.allowed_to?(:create_issue_tags, project) &&
132
+ !(User.current.allowed_to?(:edit_issue_tags, project) && Issue.allowed_tags?(tags))
133
+
134
+ errors.add :tag_list, :invalid_mutually_exclusive_tags unless AdditionalTag.valid_mutually_exclusive_tag tag_list
135
+ end
91
136
  end
92
137
  end
93
138
  end
@@ -15,6 +15,9 @@ module AdditionalTags
15
15
 
16
16
  alias_method :available_columns_without_tags, :available_columns
17
17
  alias_method :available_columns, :available_columns_with_tags
18
+
19
+ alias_method :issues_without_tags, :issues
20
+ alias_method :issues, :issues_with_tags
18
21
  end
19
22
 
20
23
  module InstanceOverwriteMethods
@@ -29,9 +32,24 @@ module AdditionalTags
29
32
 
30
33
  self
31
34
  end
35
+
36
+ def sql_for_tags_field(field, _operator, values)
37
+ build_sql_for_tags_field_with_permission klass: queried_class,
38
+ operator: operator_for(field),
39
+ values: values,
40
+ permission: :view_issue_tags
41
+ end
32
42
  end
33
43
 
34
44
  module InstanceMethods
45
+ def issues_with_tags(**options)
46
+ issues = issues_without_tags(**options)
47
+ return issues unless has_column? :tags
48
+
49
+ Issue.load_visible_tags issues
50
+ issues
51
+ end
52
+
35
53
  def initialize_available_filters_with_tags
36
54
  initialize_available_filters_without_tags
37
55
 
@@ -15,9 +15,19 @@ module AdditionalTags
15
15
  module InstanceMethods
16
16
  def column_content_with_tags(column, item)
17
17
  if column.name == :issue_tags || item.is_a?(Issue) && column.name == :tags
18
- additional_tag_links column.value(item),
19
- tag_controller: 'issues',
20
- use_colors: AdditionalTags.setting?(:use_colors)
18
+ tags = if item.instance_variable_defined? :@visible_tags
19
+ item.instance_variable_get :@visible_tags
20
+ elsif Setting.display_subprojects_issues?
21
+ # permission check required (expensive)
22
+ return unless User.current.allowed_to? :view_issue_tags, item.project
23
+
24
+ column.value item
25
+ else
26
+ # no permission check required
27
+ column.value item
28
+ end
29
+
30
+ additional_tag_links tags, tag_controller: 'issues'
21
31
  else
22
32
  column_content_without_tags column, item
23
33
  end
@@ -11,7 +11,9 @@ module AdditionalTags
11
11
 
12
12
  module InstanceMethods
13
13
  def sql_for_tags_field(field, _operator, values)
14
- build_sql_for_tags_field klass: queried_class, operator: operator_for(field), values: values
14
+ build_sql_for_tags_field klass: queried_class,
15
+ operator: operator_for(field),
16
+ values: values
15
17
  end
16
18
 
17
19
  def initialize_tags_filter(position: nil)
@@ -60,6 +62,25 @@ module AdditionalTags
60
62
  end
61
63
  end
62
64
 
65
+ # NOTE: should be used, if tags required permission check
66
+ def build_sql_for_tags_field_with_permission(klass:, operator:, values:, permission:)
67
+ compare = ['=', '*'].include?(operator) ? 'in' : 'not_in'
68
+ case operator
69
+ when '=', '!'
70
+ ids_list = klass.tagged_with(values, any: true).ids
71
+ # special case: filter with deleted tag
72
+ return AdditionalsQuery::NO_RESULT_CONDITION if ids_list.blank? && values.present? && operator == '='
73
+ else
74
+ allowed_projects = Project.where(Project.allowed_to_condition(User.current, permission))
75
+ .select(:id)
76
+ ids_list = klass.tagged_with(klass.available_tags(skip_pre_condition: true), any: true)
77
+ .where(project_id: allowed_projects).ids
78
+ end
79
+
80
+ "(#{klass.arel_table[:id].send(compare, ids_list).to_sql})"
81
+ end
82
+
83
+ # NOTE: should be used, if tags do not require permission check
63
84
  def build_sql_for_tags_field(klass:, operator:, values:)
64
85
  compare = ['=', '*'].include?(operator) ? 'IN' : 'NOT IN'
65
86
  case operator
@@ -69,7 +90,7 @@ module AdditionalTags
69
90
  "(#{klass.table_name}.id #{compare} (#{ids_list.join ','}))"
70
91
  elsif values.present? && operator == '='
71
92
  # special case: filter with deleted tag
72
- '(1=0)'
93
+ AdditionalsQuery::NO_RESULT_CONDITION
73
94
  end
74
95
  else
75
96
  entries = ActsAsTaggableOn::Tagging.where taggable_type: klass.name
@@ -13,6 +13,7 @@ module AdditionalTags
13
13
  alias_method :safe_attributes_without_tags=, :safe_attributes=
14
14
  alias_method :safe_attributes=, :safe_attributes_with_tags=
15
15
 
16
+ validate :validate_tags
16
17
  before_save :sort_tag_list
17
18
  end
18
19
 
@@ -24,7 +25,7 @@ module AdditionalTags
24
25
 
25
26
  def available_tags(**options)
26
27
  options[:project_join] = project_joins
27
- options[:permission] = :view_wiki_pages
28
+ options[:permission] ||= :view_wiki_pages
28
29
  AdditionalTags::Tags.available_tags self, **options
29
30
  end
30
31
 
@@ -106,6 +107,10 @@ module AdditionalTags
106
107
 
107
108
  self.tag_list = AdditionalTags::Tags.sort_tags tag_list
108
109
  end
110
+
111
+ def validate_tags
112
+ errors.add :tag_list, :invalid_mutually_exclusive_tags unless AdditionalTag.valid_mutually_exclusive_tag tag_list
113
+ end
109
114
  end
110
115
  end
111
116
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module AdditionalTags
4
4
  module PluginVersion
5
- VERSION = '1.0.6' unless defined? VERSION
5
+ VERSION = '3.0.9' unless defined? AdditionalTags::PluginVersion::VERSION
6
6
  end
7
7
  end
@@ -3,13 +3,27 @@
3
3
  module AdditionalTags
4
4
  class Tags
5
5
  class << self
6
+ def visible_condition(user, **options)
7
+ permission = options[:permission] || :view_issue_tags
8
+ skip_pre_condition = options[:skip_pre_condition] || true
9
+
10
+ tag_access permission, user, skip_pre_condition: skip_pre_condition
11
+ end
12
+
6
13
  def available_tags(klass, **options)
7
14
  user = options[:user].presence || User.current
8
15
 
9
16
  scope = ActsAsTaggableOn::Tag.where({})
10
- scope = scope.where "#{Project.table_name}.id = ?", options[:project] if options[:project]
17
+ if options[:project]
18
+ scope = if Setting.display_subprojects_issues?
19
+ scope.where subproject_sql(options[:project])
20
+ else
21
+ scope.where projects: { id: options[:project] }
22
+ end
23
+ end
24
+
11
25
  if options[:permission]
12
- scope = scope.where tag_access(options[:permission], user)
26
+ scope = scope.where tag_access(options[:permission], user, skip_pre_condition: options[:skip_pre_condition])
13
27
  elsif options[:visible_condition]
14
28
  scope = scope.where klass.visible_condition(user)
15
29
  end
@@ -18,14 +32,7 @@ module AdditionalTags
18
32
  scope = scope.where "#{TAGGING_TABLE_NAME}.taggable_id!=?", options[:exclude_id] if options[:exclude_id]
19
33
  scope = scope.where options[:where_field] => options[:where_value] if options[:where_field].present? && options[:where_value]
20
34
 
21
- columns = ["#{TAG_TABLE_NAME}.id",
22
- "#{TAG_TABLE_NAME}.name",
23
- "#{TAG_TABLE_NAME}.taggings_count",
24
- "COUNT(DISTINCT #{TAGGING_TABLE_NAME}.taggable_id) AS count"]
25
-
26
- columns << "MIN(#{TAGGING_TABLE_NAME}.created_at) AS last_created" if options[:sort_by] == 'last_created'
27
-
28
- scope.select(columns.to_list)
35
+ scope.select(table_columns(options[:sort_by]))
29
36
  .joins(tag_for_joins(klass, **options.slice(:project_join, :project, :without_projects)))
30
37
  .group("#{TAG_TABLE_NAME}.id, #{TAG_TABLE_NAME}.name, #{TAG_TABLE_NAME}.taggings_count").having('COUNT(*) > 0')
31
38
  .order(build_order_sql(options[:sort_by], options[:order]))
@@ -125,8 +132,23 @@ module AdditionalTags
125
132
  counts
126
133
  end
127
134
 
135
+ def subproject_sql(project)
136
+ "#{Project.table_name}.lft >= #{project.lft} " \
137
+ "AND #{Project.table_name}.rgt <= #{project.rgt}"
138
+ end
139
+
128
140
  private
129
141
 
142
+ def table_columns(sort_by)
143
+ columns = ["#{TAG_TABLE_NAME}.id",
144
+ "#{TAG_TABLE_NAME}.name",
145
+ "#{TAG_TABLE_NAME}.taggings_count",
146
+ "COUNT(DISTINCT #{TAGGING_TABLE_NAME}.taggable_id) AS count"]
147
+
148
+ columns << "MIN(#{TAGGING_TABLE_NAME}.created_at) AS last_created" if sort_by == 'last_created'
149
+ columns.to_list
150
+ end
151
+
130
152
  def status_for_tag_value(scope:, tag_id:, group_id: nil, group_id_is_bool: false)
131
153
  value = if group_id_is_bool || group_id
132
154
  if group_id_is_bool
@@ -176,17 +198,17 @@ module AdditionalTags
176
198
  joins
177
199
  end
178
200
 
179
- def tag_access(permission, user)
201
+ def tag_access(permission, user, skip_pre_condition: false)
180
202
  projects_allowed = if permission.nil?
181
203
  Project.visible.ids
182
204
  else
183
- Project.where(Project.allowed_to_condition(user, permission)).ids
205
+ Project.where(Project.allowed_to_condition(user, permission, skip_pre_condition: skip_pre_condition)).ids
184
206
  end
185
207
 
186
208
  if projects_allowed.present?
187
209
  "#{Project.table_name}.id IN (#{projects_allowed.join ','})" unless projects_allowed.empty?
188
210
  else
189
- '1=0'
211
+ AdditionalsQuery::NO_RESULT_CONDITION
190
212
  end
191
213
  end
192
214
  end
@@ -14,6 +14,11 @@ module AdditionalTags
14
14
  setting(:tags_sidebar).present? && setting(:tags_sidebar) != 'none'
15
15
  end
16
16
 
17
+ # color is used by default (if setting is missing, too)
18
+ def use_colors?
19
+ setting(:tags_color_theme).to_s != '0'
20
+ end
21
+
17
22
  private
18
23
 
19
24
  def setup