From f0580d2bba0f5da049c1ef0568d18a4feaadfb4a Mon Sep 17 00:00:00 2001 From: Faizaan Gagan Date: Mon, 7 Feb 2022 11:10:52 +0530 Subject: [PATCH] FEATURE: allow tags from tag field to be confined to a tag group (#175) * FEATURE: allow tag field to be confined to a tag group * fixed linting * bump minor version * moved monkeypatch to a separate module * use snake case for variable names * added specs --- .../components/wizard-custom-field.hbs | 14 ++++++ .../components/wizard-tag-chooser.js.es6 | 12 ++++++ .../templates/components/wizard-field-tag.hbs | 8 +++- config/locales/client.en.yml | 1 + controllers/custom_wizard/admin/wizard.rb | 1 + extensions/discourse_tagging.rb | 17 ++++++++ extensions/tags_controller.rb | 8 ++++ lib/custom_wizard/builder.rb | 2 +- lib/custom_wizard/field.rb | 5 ++- plugin.rb | 11 ++++- .../custom_wizard/wizard_field_serializer.rb | 5 +++ spec/extensions/tags_controller_spec.rb | 43 +++++++++++++++++++ 12 files changed, 123 insertions(+), 4 deletions(-) create mode 100644 assets/javascripts/wizard/components/wizard-tag-chooser.js.es6 create mode 100644 extensions/discourse_tagging.rb create mode 100644 extensions/tags_controller.rb create mode 100644 spec/extensions/tags_controller_spec.rb diff --git a/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs b/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs index 39c02c1d..f51b9fbb 100644 --- a/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs +++ b/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs @@ -208,6 +208,20 @@ {{/if}} +{{#if isTag}} +
+
+ +
+ +
+ {{tag-group-chooser + tagGroups=field.tag_groups + }} +
+
+{{/if}} + {{#if showAdvanced}} {{wizard-advanced-toggle showAdvanced=field.showAdvanced}} diff --git a/assets/javascripts/wizard/components/wizard-tag-chooser.js.es6 b/assets/javascripts/wizard/components/wizard-tag-chooser.js.es6 new file mode 100644 index 00000000..32a1fd6a --- /dev/null +++ b/assets/javascripts/wizard/components/wizard-tag-chooser.js.es6 @@ -0,0 +1,12 @@ +import TagChooser from "select-kit/components/tag-chooser"; + +export default TagChooser.extend({ + searchTags(url, data, callback) { + if (this.tagGroups) { + let tagGroupsString = this.tagGroups.join(","); + data.tag_groups = tagGroupsString; + } + + return this._super(url, data, callback); + }, +}); diff --git a/assets/javascripts/wizard/templates/components/wizard-field-tag.hbs b/assets/javascripts/wizard/templates/components/wizard-field-tag.hbs index 8ebc56eb..1916f3d1 100644 --- a/assets/javascripts/wizard/templates/components/wizard-field-tag.hbs +++ b/assets/javascripts/wizard/templates/components/wizard-field-tag.hbs @@ -1 +1,7 @@ -{{tag-chooser tags=field.value maximum=field.limit tabindex=field.tabindex everyTag=true}} +{{wizard-tag-chooser + tags=field.value + maximum=field.limit + tabindex=field.tabindex + tagGroups=field.tag_groups + everyTag=true +}} diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index ff500ee2..b51421a7 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -188,6 +188,7 @@ en: property: "Property" prefill: "Prefill" content: "Content" + tag_groups: "Tag Groups" date_time_format: label: "Format" instructions: "Moment.js format" diff --git a/controllers/custom_wizard/admin/wizard.rb b/controllers/custom_wizard/admin/wizard.rb index 09471680..0a59e02b 100644 --- a/controllers/custom_wizard/admin/wizard.rb +++ b/controllers/custom_wizard/admin/wizard.rb @@ -117,6 +117,7 @@ class CustomWizard::AdminWizardController < CustomWizard::AdminController condition: mapped_params, index: mapped_params, validations: {}, + tag_groups: [], ] ], actions: [ diff --git a/extensions/discourse_tagging.rb b/extensions/discourse_tagging.rb new file mode 100644 index 00000000..6c4d6040 --- /dev/null +++ b/extensions/discourse_tagging.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +module CustomWizardDiscourseTagging + def filter_allowed_tags(guardian, opts = {}) + if tag_groups = RequestStore.store[:tag_groups] + tag_group_array = tag_groups.split(",") + filtered_tags = TagGroup.includes(:tags).where(name: tag_group_array).map do |tag_group| + tag_group.tags.pluck(:name) + end.flatten + + opts[:only_tag_names] ||= [] + opts[:only_tag_names].push(*filtered_tags) + end + + super + end +end diff --git a/extensions/tags_controller.rb b/extensions/tags_controller.rb new file mode 100644 index 00000000..e9618733 --- /dev/null +++ b/extensions/tags_controller.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +module CustomWizardTagsController + def search + RequestStore.store[:tag_groups] = params[:tag_groups] if params[:tag_groups].present? + super + end +end diff --git a/lib/custom_wizard/builder.rb b/lib/custom_wizard/builder.rb index aede16a2..681bc459 100644 --- a/lib/custom_wizard/builder.rb +++ b/lib/custom_wizard/builder.rb @@ -86,7 +86,7 @@ class CustomWizard::Builder required: field_template['required'] } - %w(label description image key validations min_length max_length char_counter).each do |key| + %w(label description image key validations min_length max_length char_counter tag_groups).each do |key| params[key.to_sym] = field_template[key] if field_template[key] end diff --git a/lib/custom_wizard/field.rb b/lib/custom_wizard/field.rb index eb4af65d..65f29504 100644 --- a/lib/custom_wizard/field.rb +++ b/lib/custom_wizard/field.rb @@ -21,6 +21,7 @@ class CustomWizard::Field :limit, :property, :content, + :tag_groups, :preview_template, :placeholder @@ -46,6 +47,7 @@ class CustomWizard::Field @limit = attrs[:limit] @property = attrs[:property] @content = attrs[:content] + @tag_groups = attrs[:tag_groups] @preview_template = attrs[:preview_template] @placeholder = attrs[:placeholder] end @@ -111,7 +113,8 @@ class CustomWizard::Field tag: { limit: nil, prefill: nil, - content: nil + content: nil, + tag_groups: nil }, category: { limit: 1, diff --git a/plugin.rb b/plugin.rb index aaf5c685..fdbd4843 100644 --- a/plugin.rb +++ b/plugin.rb @@ -1,12 +1,14 @@ # frozen_string_literal: true # name: discourse-custom-wizard # about: Create custom wizards -# version: 1.17.3 +# version: 1.18.0 # authors: Angus McLeod # url: https://github.com/paviliondev/discourse-custom-wizard # contact emails: angus@thepavilion.io gem 'liquid', '5.0.1', require: true +## ensure compatibility with category lockdown plugin +gem 'request_store', '1.5.0', require: true register_asset 'stylesheets/common/wizard-admin.scss' register_asset 'stylesheets/common/wizard-mapper.scss' @@ -110,9 +112,11 @@ after_initialize do ../extensions/invites_controller.rb ../extensions/guardian.rb ../extensions/users_controller.rb + ../extensions/tags_controller.rb ../extensions/custom_field/preloader.rb ../extensions/custom_field/serializer.rb ../extensions/custom_field/extension.rb + ../extensions/discourse_tagging.rb ].each do |path| load File.expand_path(path, __FILE__) end @@ -249,5 +253,10 @@ after_initialize do "#{serializer_klass}_serializer".classify.constantize.prepend CustomWizardCustomFieldSerializer end + reloadable_patch do |plugin| + ::TagsController.prepend CustomWizardTagsController + ::DiscourseTagging.singleton_class.prepend CustomWizardDiscourseTagging + end + DiscourseEvent.trigger(:custom_wizard_ready) end diff --git a/serializers/custom_wizard/wizard_field_serializer.rb b/serializers/custom_wizard/wizard_field_serializer.rb index 37f7a80f..9aa750e5 100644 --- a/serializers/custom_wizard/wizard_field_serializer.rb +++ b/serializers/custom_wizard/wizard_field_serializer.rb @@ -16,6 +16,7 @@ class CustomWizard::FieldSerializer < ::ApplicationSerializer :limit, :property, :content, + :tag_groups, :validations, :max_length, :char_counter, @@ -100,6 +101,10 @@ class CustomWizard::FieldSerializer < ::ApplicationSerializer object.content end + def content + object.tag_groups + end + def validations validations = {} object.validations&.each do |type, props| diff --git a/spec/extensions/tags_controller_spec.rb b/spec/extensions/tags_controller_spec.rb new file mode 100644 index 00000000..455990c6 --- /dev/null +++ b/spec/extensions/tags_controller_spec.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +require_relative '../plugin_helper' + +describe ::TagsController, type: :request do + fab!(:tag_1) { Fabricate(:tag, name: "Angus") } + fab!(:tag_2) { Fabricate(:tag, name: "Faizaan") } + fab!(:tag_3) { Fabricate(:tag, name: "Robert") } + fab!(:tag_4) { Fabricate(:tag, name: "Eli") } + fab!(:tag_5) { Fabricate(:tag, name: "Jeff") } + + fab!(:tag_group_1) { Fabricate(:tag_group, tags: [tag_1, tag_2]) } + fab!(:tag_group_2) { Fabricate(:tag_group, tags: [tag_3, tag_4]) } + + describe "#search" do + context "tag group param present" do + it "returns tags only only in the tag group" do + get "/tags/filter/search.json", params: { q: '', tag_groups: [tag_group_1.name, tag_group_2.name] } + expect(response.status).to eq(200) + results = response.parsed_body['results'] + names = results.map { |result| result['name'] } + + expected_tag_names = TagGroup + .includes(:tags) + .where(id: [tag_group_1.id, tag_group_2.id]) + .map { |tag_group| tag_group.tags.pluck(:name) }.flatten + expect(names).to contain_exactly(*expected_tag_names) + end + end + + context "tag group param not present" do + it "returns all tags" do + get "/tags/filter/search.json", params: { q: '' } + expect(response.status).to eq(200) + results = response.parsed_body['results'] + names = results.map { |result| result['name'] } + + all_tag_names = Tag.all.pluck(:name) + expect(names).to contain_exactly(*all_tag_names) + end + end + end +end