- {{i18n "admin.wizard.field.validations.expected_instructions"}}
+ {{i18n
+ "admin.wizard.field.validations.expected_instructions"
+ }}
-
+
{{radio-button
@@ -123,9 +121,7 @@
-
+
{{/each}}
-
+
\ No newline at end of file
diff --git a/assets/javascripts/discourse/components/wizard-realtime-validations.js b/assets/javascripts/discourse/components/wizard-realtime-validations.js
index a89752d308..071b7e5323 100644
--- a/assets/javascripts/discourse/components/wizard-realtime-validations.js
+++ b/assets/javascripts/discourse/components/wizard-realtime-validations.js
@@ -50,8 +50,7 @@ export default Component.extend({
const validationBuffer = cloneJSON(this.get("field.validations"));
if (validationBuffer.similar_topics) {
- const bufferCategories =
- validationBuffer.similar_topics.categories || [];
+ const bufferCategories = validationBuffer.similar_topics.categories || [];
validationBuffer.similar_topics.categories =
Category.findByIds(bufferCategories);
}
diff --git a/assets/javascripts/discourse/components/wizard-subscription-badge.hbs b/assets/javascripts/discourse/components/wizard-subscription-badge.hbs
deleted file mode 100644
index 992504bbea..0000000000
--- a/assets/javascripts/discourse/components/wizard-subscription-badge.hbs
+++ /dev/null
@@ -1,19 +0,0 @@
-
- {{#if this.updating}}
- {{loading-spinner size="small"}}
- {{/if}}
-
-
- {{d-icon "pavilion-logo"}}
- {{this.label}}
-
\ No newline at end of file
diff --git a/assets/javascripts/discourse/components/wizard-subscription-badge.js b/assets/javascripts/discourse/components/wizard-subscription-badge.js
deleted file mode 100644
index 24eb5aab56..0000000000
--- a/assets/javascripts/discourse/components/wizard-subscription-badge.js
+++ /dev/null
@@ -1,46 +0,0 @@
-import Component from "@glimmer/component";
-import { tracked } from "@glimmer/tracking";
-import { action, computed } from "@ember/object";
-import { service } from "@ember/service";
-import I18n from "I18n";
-
-export default class WizardSubscriptionBadge extends Component {
- @service subscription;
- @tracked updating = false;
- @tracked updateIcon = "arrows-rotate";
- basePath = "/admin/plugins/subscription-client";
-
- @computed("subscription.subscriptionType")
- get i18nKey() {
- return `admin.wizard.subscription.type.${
- this.subscription.subscriptionType
- ? this.subscription.subscriptionType
- : "none"
- }`;
- }
-
- @computed("i18nKey")
- get title() {
- return `${this.i18nKey}.title`;
- }
-
- @computed("i18nKey")
- get label() {
- return I18n.t(`${this.i18nKey}.label`);
- }
-
- @action
- click() {
- window.open(this.subscription.subscriptionCtaLink, "_blank").focus();
- }
-
- @action
- update() {
- this.updating = true;
- this.updateIcon = null;
- this.subscription.updateSubscriptionStatus().finally(() => {
- this.updateIcon = "arrows-rotate";
- this.updating = false;
- });
- }
-}
diff --git a/assets/javascripts/discourse/components/wizard-subscription-status.hbs b/assets/javascripts/discourse/components/wizard-subscription-status.hbs
deleted file mode 100644
index 23ac0e1257..0000000000
--- a/assets/javascripts/discourse/components/wizard-subscription-status.hbs
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
- {{#if this.authorized}}
-
-
- {{i18n "admin.wizard.subscription.deauthorize.label"}}
-
- {{else}}
-
- {{/if}}
-
\ No newline at end of file
diff --git a/assets/javascripts/discourse/components/wizard-subscription-status.js b/assets/javascripts/discourse/components/wizard-subscription-status.js
deleted file mode 100644
index 4c6a1f0698..0000000000
--- a/assets/javascripts/discourse/components/wizard-subscription-status.js
+++ /dev/null
@@ -1,57 +0,0 @@
-import Component from "@glimmer/component";
-import { tracked } from "@glimmer/tracking";
-import { action } from "@ember/object";
-import { service } from "@ember/service";
-import { ajax } from "discourse/lib/ajax";
-import { popupAjaxError } from "discourse/lib/ajax-error";
-
-export default class WizardSubscriptionStatus extends Component {
- @service siteSettings;
- @service subscription;
- @tracked supplierId = null;
- @tracked authorized = false;
- @tracked unauthorizing = false;
- basePath = "/admin/plugins/subscription-client/suppliers";
-
- constructor() {
- super(...arguments);
- ajax(`${this.basePath}?resource=discourse-custom-wizard`)
- .then((result) => {
- if (result.suppliers && result.suppliers.length) {
- this.supplierId = result.suppliers[0].id;
- this.authorized = result.suppliers[0].authorized;
- }
- })
- .finally(() => {
- this.subscription.retrieveSubscriptionStatus();
- });
- }
-
- @action
- authorize() {
- window.location.href = `${this.basePath}/authorize?supplier_id=${this.supplierId}&final_landing_path=/admin/wizards/wizard`;
- }
-
- @action
- deauthorize() {
- this.unauthorizing = true;
-
- ajax(`${this.basePath}/authorize`, {
- type: "DELETE",
- data: {
- supplier_id: this.supplierId,
- },
- })
- .then((result) => {
- if (result.success) {
- this.supplierId = result.supplier_id;
- this.authorized = false;
- }
- })
- .finally(() => {
- this.unauthorizing = false;
- this.subscription.retrieveSubscriptionStatus();
- })
- .catch(popupAjaxError);
- }
-}
diff --git a/assets/javascripts/discourse/lib/wizard-schema.js b/assets/javascripts/discourse/lib/wizard-schema.js
index 424af3277e..71b6288e31 100644
--- a/assets/javascripts/discourse/lib/wizard-schema.js
+++ b/assets/javascripts/discourse/lib/wizard-schema.js
@@ -1,5 +1,4 @@
import { get, set } from "@ember/object";
-import { getOwnerWithFallback } from "discourse-common/lib/get-owner";
const wizard = {
basic: {
diff --git a/assets/javascripts/discourse/lib/wizard.js b/assets/javascripts/discourse/lib/wizard.js
index 270707100e..fbcb584a7e 100644
--- a/assets/javascripts/discourse/lib/wizard.js
+++ b/assets/javascripts/discourse/lib/wizard.js
@@ -3,7 +3,7 @@ import I18n from "I18n";
import wizardSchema from "./wizard-schema";
function selectKitContent(content) {
- return content.map((i) => ({ id: i, name: i }));
+ return content.map((i) => ({ id: i, name: `${i}` }));
}
function generateName(id) {
diff --git a/config/routes.rb b/config/routes.rb
index 50cc4f4e9f..a3f5991820 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -11,6 +11,7 @@
mount ::CustomWizard::Engine, at: "w"
post "wizard/authorization/callback" => "custom_wizard/authorization#callback"
get "realtime-validations" => "custom_wizard/realtime_validations#validate"
+ get "custom-wizard/tags/search" => "custom_wizard/tags#search"
scope module: "custom_wizard", constraints: AdminConstraint.new do
get "admin/wizards" => "admin#index"
diff --git a/lib/custom_wizard/custom_field.rb b/lib/custom_wizard/custom_field.rb
index 48a1ed0608..9e319d935c 100644
--- a/lib/custom_wizard/custom_field.rb
+++ b/lib/custom_wizard/custom_field.rb
@@ -6,20 +6,20 @@ class ::CustomWizard::CustomField
attr_reader :id
- ATTRS ||= %w[name klass type serializers]
- REQUIRED ||= %w[name klass type]
- NAMESPACE ||= "custom_wizard_custom_fields"
- NAME_MIN_LENGTH ||= 3
+ ATTRS = %w[name klass type serializers]
+ REQUIRED = %w[name klass type]
+ NAMESPACE = "custom_wizard_custom_fields"
+ NAME_MIN_LENGTH = 3
- CLASSES ||= {
+ CLASSES = {
topic: %w[topic_view topic_list_item],
group: ["basic_group"],
category: ["basic_category"],
post: ["post"],
}
- TYPES ||= %w[string boolean integer json]
- LIST_CACHE_KEY ||= "custom_field_list"
+ TYPES = %w[string boolean integer json]
+ LIST_CACHE_KEY = "custom_field_list"
def self.serializers
CLASSES.values.flatten.uniq
diff --git a/lib/custom_wizard/engine.rb b/lib/custom_wizard/engine.rb
index b8d148adf4..fb0b2d3ef1 100644
--- a/lib/custom_wizard/engine.rb
+++ b/lib/custom_wizard/engine.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module ::CustomWizard
- PLUGIN_NAME ||= "custom_wizard"
+ PLUGIN_NAME = "custom_wizard"
class Engine < ::Rails::Engine
engine_name PLUGIN_NAME
diff --git a/lib/custom_wizard/extensions/discourse_tagging.rb b/lib/custom_wizard/extensions/discourse_tagging.rb
deleted file mode 100644
index a1dc5316e9..0000000000
--- a/lib/custom_wizard/extensions/discourse_tagging.rb
+++ /dev/null
@@ -1,60 +0,0 @@
-# frozen_string_literal: true
-
-module CustomWizardDiscourseTagging
- def filter_allowed_tags(guardian, opts = {})
- normalize_selected_tags!(opts)
-
- if opts[:for_input].respond_to?(:dig) && (groups = opts.dig(:for_input, :groups)).present?
- tag_group_array = groups.split(",")
- filtered_tags =
- TagGroup
- .includes(:tags)
- .where(name: tag_group_array)
- .map { |tag_group| tag_group.tags.pluck(:name) }
- .flatten
-
- opts[:only_tag_names] ||= []
- opts[:only_tag_names].push(*filtered_tags)
- opts.delete(:for_input)
- end
-
- super
- end
-
- private
-
- def normalize_selected_tags!(opts)
- selected_tags = opts[:selected_tags]
- return if selected_tags.blank?
-
- selected_tag_ids = Array(opts[:selected_tag_ids])
- normalized_selected_tags = []
-
- selected_tags = selected_tags.values if selected_tags.respond_to?(:values)
-
- Array(selected_tags).each do |selected_tag|
- name, selected_tag_id = extract_selected_tag(selected_tag)
-
- normalized_selected_tags << name if name.present?
- selected_tag_ids << selected_tag_id.to_i if selected_tag_id.present?
- end
-
- opts[:selected_tags] = normalized_selected_tags
- opts[:selected_tag_ids] = selected_tag_ids
- end
-
- def extract_selected_tag(selected_tag)
- case selected_tag
- when String
- [selected_tag, nil]
- when Symbol
- [selected_tag.to_s, nil]
- when Numeric
- [nil, selected_tag]
- when Hash, ActionController::Parameters
- [selected_tag[:name] || selected_tag["name"], selected_tag[:id] || selected_tag["id"]]
- else
- [nil, nil]
- end
- end
-end
diff --git a/lib/custom_wizard/mapper.rb b/lib/custom_wizard/mapper.rb
index 17211efc3d..c83bd59d7a 100644
--- a/lib/custom_wizard/mapper.rb
+++ b/lib/custom_wizard/mapper.rb
@@ -220,7 +220,7 @@ def interpolate(string, opts = { user: true, wizard: true, value: true, template
result = ""
if key == "time"
- time_format = format.present? ? format : "%B %-d, %Y"
+ time_format = (format.presence || "%B %-d, %Y")
result = Time.now.strftime(time_format)
end
diff --git a/lib/custom_wizard/submission.rb b/lib/custom_wizard/submission.rb
index d1cb9acb38..98028741e4 100644
--- a/lib/custom_wizard/submission.rb
+++ b/lib/custom_wizard/submission.rb
@@ -3,8 +3,8 @@ class CustomWizard::Submission
include ActiveModel::SerializerSupport
PAGE_LIMIT = 50
- KEY ||= "submissions"
- META ||= %w[updated_at submitted_at route_to redirect_on_complete redirect_to]
+ KEY = "submissions"
+ META = %w[updated_at submitted_at route_to redirect_on_complete redirect_to]
attr_reader :id, :wizard
diff --git a/lib/custom_wizard/subscription.rb b/lib/custom_wizard/subscription.rb
index 6632b14298..d8a10ebae5 100644
--- a/lib/custom_wizard/subscription.rb
+++ b/lib/custom_wizard/subscription.rb
@@ -119,7 +119,7 @@ def initialize(update = false)
end
def includes?(feature, attribute, value = nil)
- return true
+ true
end
def type
@@ -130,7 +130,7 @@ def type
end
def subscribed?
- return true
+ true
end
def standard?
@@ -146,7 +146,7 @@ def community?
end
def self.subscribed?
- return true
+ true
end
def self.business?
diff --git a/lib/custom_wizard/tag_search.rb b/lib/custom_wizard/tag_search.rb
new file mode 100644
index 0000000000..2668f3c729
--- /dev/null
+++ b/lib/custom_wizard/tag_search.rb
@@ -0,0 +1,75 @@
+# frozen_string_literal: true
+
+class CustomWizard::TagSearch
+ def initialize(guardian, params)
+ @guardian = guardian
+ @params = params
+ end
+
+ def results
+ tags = DiscourseTagging.filter_allowed_tags(@guardian, **filter_options)
+ ::TagsController.tag_counts_json(Tag.with_localizations(tags), @guardian)
+ end
+
+ private
+
+ def filter_options
+ opts = { limit: capped_limit }
+ opts[:selected_tag_ids] = selected_tag_ids if selected_tag_ids.present?
+ opts[:only_tag_names] = allowed_tag_names if restricted_to_tags?
+
+ if term.present?
+ opts[:term] = term
+ opts[:order_search_results] = true
+ else
+ opts[:order_popularity] = true
+ end
+
+ opts
+ end
+
+ def restricted_to_tags?
+ tag_group_names.present? || content_tag_names.present?
+ end
+
+ def allowed_tag_names
+ (tag_group_tag_names + content_tag_names).uniq
+ end
+
+ def tag_group_names
+ @tag_group_names ||= @params[:tag_groups].to_s.split(",").map(&:strip).reject(&:blank?)
+ end
+
+ def tag_group_tag_names
+ return [] if tag_group_names.blank?
+
+ TagGroup
+ .includes(:tags)
+ .where(name: tag_group_names)
+ .flat_map { |tag_group| tag_group.tags.pluck(:name) }
+ end
+
+ def content_tag_names
+ @content_tag_names ||= Array(@params[:content]).map { |name| name.to_s.strip }.reject(&:blank?)
+ end
+
+ def selected_tag_ids
+ @selected_tag_ids ||=
+ if @params[:selected_tag_ids].present?
+ Array(@params[:selected_tag_ids]).map(&:to_i)
+ elsif @params[:selected_tags].present?
+ Tag.where_name(Array(@params[:selected_tags])).pluck(:id)
+ else
+ []
+ end
+ end
+
+ def term
+ @term ||= @params[:q].present? ? DiscourseTagging.clean_tag(@params[:q]) : nil
+ end
+
+ def capped_limit
+ max = SiteSetting.max_tag_search_results
+ [@params[:limit].presence&.to_i || max, max].min
+ end
+end
diff --git a/lib/custom_wizard/template.rb b/lib/custom_wizard/template.rb
index cc77d078fb..ae99bfa4e4 100644
--- a/lib/custom_wizard/template.rb
+++ b/lib/custom_wizard/template.rb
@@ -3,8 +3,8 @@
class CustomWizard::Template
include HasErrors
- AFTER_SIGNUP_CACHE_KEY ||= "after_signup_wizard_ids"
- AFTER_TIME_CACHE_KEY ||= "after_time_wizard_ids"
+ AFTER_SIGNUP_CACHE_KEY = "after_signup_wizard_ids"
+ AFTER_TIME_CACHE_KEY = "after_time_wizard_ids"
attr_reader :data, :opts, :steps, :actions
diff --git a/lib/custom_wizard/validators/template.rb b/lib/custom_wizard/validators/template.rb
index f3bc9dd792..e5eda953e7 100644
--- a/lib/custom_wizard/validators/template.rb
+++ b/lib/custom_wizard/validators/template.rb
@@ -116,11 +116,11 @@ def validate_after_time
end
wizard = CustomWizard::Wizard.create(@data[:id]) if !@opts[:create]
- current_time = wizard.present? ? wizard.after_time_scheduled : nil
+ current_time = (wizard.presence&.after_time_scheduled)
new_time = @data[:after_time_scheduled]
begin
- active_time = Time.parse(new_time.present? ? new_time : current_time).utc
+ active_time = Time.parse((new_time.presence || current_time)).utc
rescue ArgumentError
invalid_time = true
end
diff --git a/lib/custom_wizard/validators/update.rb b/lib/custom_wizard/validators/update.rb
index 3dc37649cf..582282bbec 100644
--- a/lib/custom_wizard/validators/update.rb
+++ b/lib/custom_wizard/validators/update.rb
@@ -144,7 +144,7 @@ def is_url_type(field)
["url"].include? field.type
end
- SCHEMES ||= %w[http https]
+ SCHEMES = %w[http https]
def check_if_url(url)
parsed = Addressable::URI.parse(url) or return false
diff --git a/lib/custom_wizard/wizard.rb b/lib/custom_wizard/wizard.rb
index 3c491b21b6..6f78824437 100644
--- a/lib/custom_wizard/wizard.rb
+++ b/lib/custom_wizard/wizard.rb
@@ -36,7 +36,7 @@ class CustomWizard::Wizard
attr_reader :all_step_ids
attr_writer :submissions
- GUEST_ID_PREFIX ||= "guest"
+ GUEST_ID_PREFIX = "guest"
GUEST_GROUP_ID = -1
def initialize(attrs = {}, user = nil, guest_id = nil)
@@ -321,7 +321,7 @@ def cleanup_on_complete!
def cleanup_on_skip!
remove_user_redirect
- current_submission.remove if current_submission.present?
+ (current_submission.presence&.remove)
reset
end
diff --git a/plugin.rb b/plugin.rb
index 39f0fde4d9..91b8a63840 100644
--- a/plugin.rb
+++ b/plugin.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
# name: discourse-custom-wizard
# about: Forms for Discourse. Better onboarding, structured posting, data enrichment, automated actions and much more.
-# version: 2.14.2
+# version: 2.15.0
# authors: Angus McLeod, Faizaan Gagan, Robert Barrow, Keegan George, Kaitlin Maddever, Marcos Gutierrez
# url: https://github.com/paviliondev/discourse-custom-wizard
# contact_emails: development@pavilion.tech
@@ -44,6 +44,7 @@
require_relative "app/controllers/custom_wizard/wizard.rb"
require_relative "app/controllers/custom_wizard/steps.rb"
require_relative "app/controllers/custom_wizard/realtime_validations.rb"
+ require_relative "app/controllers/custom_wizard/tags.rb"
require_relative "app/jobs/regular/refresh_api_access_token.rb"
require_relative "app/jobs/regular/set_after_time_wizard.rb"
require_relative "lib/custom_wizard/validators/template.rb"
@@ -57,6 +58,7 @@
require_relative "lib/custom_wizard/realtime_validation.rb"
require_relative "lib/custom_wizard/realtime_validations/result.rb"
require_relative "lib/custom_wizard/realtime_validations/similar_topics.rb"
+ require_relative "lib/custom_wizard/tag_search.rb"
require_relative "lib/custom_wizard/mapper.rb"
require_relative "lib/custom_wizard/log.rb"
require_relative "lib/custom_wizard/step_updater.rb"
@@ -93,7 +95,6 @@
require_relative "lib/custom_wizard/extensions/custom_field/preloader.rb"
require_relative "lib/custom_wizard/extensions/custom_field/serializer.rb"
require_relative "lib/custom_wizard/extensions/custom_field/extension.rb"
- require_relative "lib/custom_wizard/extensions/discourse_tagging.rb"
Liquid::Template.error_mode = :strict
@@ -122,13 +123,7 @@
!!custom_redirect
end
- add_to_class(:user, :redirect_to_wizard) do
- if custom_fields["redirect_to_wizard"].present?
- custom_fields["redirect_to_wizard"]
- else
- nil
- end
- end
+ add_to_class(:user, :redirect_to_wizard) { custom_fields["redirect_to_wizard"].presence }
add_to_class(:users_controller, :wizard_path) do
if custom_wizard_redirect = current_user.redirect_to_wizard
@@ -222,10 +217,6 @@
"#{serializer_klass}_serializer".classify.constantize.prepend CustomWizardCustomFieldSerializer
end
- reloadable_patch do |plugin|
- ::DiscourseTagging.singleton_class.prepend CustomWizardDiscourseTagging
- end
-
DiscourseEvent.trigger(:custom_wizard_ready)
on(:before_create_topic) do |topic_params, user|
diff --git a/spec/components/custom_wizard/log_spec.rb b/spec/components/custom_wizard/log_spec.rb
index f0258e32e6..b73ef45f79 100644
--- a/spec/components/custom_wizard/log_spec.rb
+++ b/spec/components/custom_wizard/log_spec.rb
@@ -21,7 +21,7 @@
"perform_third_action",
"third_test_user",
"Third log message",
- 1.minutes.ago,
+ 1.minute.ago,
)
end
diff --git a/spec/components/custom_wizard/submission_spec.rb b/spec/components/custom_wizard/submission_spec.rb
index c229a8575e..9aa6b99b6a 100644
--- a/spec/components/custom_wizard/submission_spec.rb
+++ b/spec/components/custom_wizard/submission_spec.rb
@@ -2,7 +2,7 @@
describe CustomWizard::Submission do
fab!(:user)
- fab!(:user2) { Fabricate(:user) }
+ fab!(:user2, :user)
let(:template_json) { get_wizard_fixture("wizard") }
let(:guest_id) { CustomWizard::Wizard.generate_guest_id }
diff --git a/spec/extensions/discourse_tagging_spec.rb b/spec/extensions/discourse_tagging_spec.rb
deleted file mode 100644
index da57bf79ec..0000000000
--- a/spec/extensions/discourse_tagging_spec.rb
+++ /dev/null
@@ -1,73 +0,0 @@
-# frozen_string_literal: true
-
-describe ::DiscourseTagging, type: :request do
- fab!(:user)
- 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 "#filter_allowed_tags" do
- let(:guardian) { Guardian.new(user) }
- let(:all_tag_names) { Tag.all.pluck(:name) }
- let(:filtered_tag_names) do
- ->(filter_params) do
- DiscourseTagging.filter_allowed_tags(guardian, filter_params).map(&:name)
- end
- end
- let(:expect_filtered_tag_names) do
- lambda do |filter_params, expected_tag_names|
- expect(filtered_tag_names.call(filter_params)).to contain_exactly(*expected_tag_names)
- end
- end
- let(:custom_wizard_for_input) do
- ->(groups) { { name: "custom-wizard-tag-chooser", groups: groups } }
- end
-
- context "for_input is a boolean" do
- it "works normally" do
- expect_filtered_tag_names.call({ q: "", for_input: true }, all_tag_names)
- end
- end
-
- context "for_input is an object including a tag group" do
- it "returns tags only in the tag group" do
- filter_params = { q: "", for_input: custom_wizard_for_input.call(tag_group_1.name) }
- expect_filtered_tag_names.call(filter_params, tag_group_1.tags.pluck(:name))
- end
- end
-
- context "for_input is an object including an empty tag group string" do
- it "returns all tags" do
- expect_filtered_tag_names.call(
- { q: "", for_input: custom_wizard_for_input.call("") },
- all_tag_names,
- )
- end
- end
-
- context "when selected_tags are parameter objects" do
- it "normalizes selected_tags and does not raise" do
- filter_params = {
- q: "",
- for_input: custom_wizard_for_input.call(tag_group_1.name),
- selected_tags:
- ActionController::Parameters.new(
- "0" => ActionController::Parameters.new(id: tag_1.id.to_s, name: tag_1.name),
- "1" => ActionController::Parameters.new(id: tag_2.id.to_s, name: tag_2.name),
- ),
- }
-
- tags = nil
- expect {
- tags = DiscourseTagging.filter_allowed_tags(guardian, filter_params)
- }.not_to raise_error
- expect(tags.map(&:name)).to be_empty
- end
- end
- end
-end
diff --git a/spec/extensions/topic_extension_spec.rb b/spec/extensions/topic_extension_spec.rb
index a9b8fa48fc..54eece511a 100644
--- a/spec/extensions/topic_extension_spec.rb
+++ b/spec/extensions/topic_extension_spec.rb
@@ -4,7 +4,7 @@
fab!(:category_with_wizard) do
Fabricate(:category, custom_fields: { create_topic_wizard: "true" })
end
- fab!(:category_without_wizard) { Fabricate(:category) }
+ fab!(:category_without_wizard, :category)
fab!(:user) { Fabricate(:user, refresh_auto_groups: true) }
let(:valid_attrs) { Fabricate.attributes_for(:topic) }
diff --git a/spec/jobs/set_after_time_wizard_spec.rb b/spec/jobs/set_after_time_wizard_spec.rb
index 0141076680..2f1a29aa1e 100644
--- a/spec/jobs/set_after_time_wizard_spec.rb
+++ b/spec/jobs/set_after_time_wizard_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
describe Jobs::SetAfterTimeWizard do
- fab!(:user1) { Fabricate(:user) }
+ fab!(:user1, :user)
fab!(:user2) { Fabricate(:user, trust_level: TrustLevel[3]) }
fab!(:user3) { Fabricate(:user, admin: true) }
@@ -48,7 +48,7 @@
end
context "when after_time_groups is set" do
- fab!(:group1) { Fabricate(:group) }
+ fab!(:group1, :group)
fab!(:group_user) { Fabricate(:group_user, group: group1, user: user2) }
before do
diff --git a/spec/requests/custom_wizard/admin/wizard_controller_spec.rb b/spec/requests/custom_wizard/admin/wizard_controller_spec.rb
index 06a3e1fafc..d7cbfa71f9 100644
--- a/spec/requests/custom_wizard/admin/wizard_controller_spec.rb
+++ b/spec/requests/custom_wizard/admin/wizard_controller_spec.rb
@@ -2,8 +2,8 @@
describe CustomWizard::AdminWizardController do
fab!(:admin_user) { Fabricate(:user, admin: true) }
- fab!(:user1) { Fabricate(:user) }
- fab!(:user2) { Fabricate(:user) }
+ fab!(:user1, :user)
+ fab!(:user2, :user)
let(:template) { get_wizard_fixture("wizard") }
let(:category) do
Fabricate(
diff --git a/spec/requests/custom_wizard/tags_controller_spec.rb b/spec/requests/custom_wizard/tags_controller_spec.rb
new file mode 100644
index 0000000000..362bb49e61
--- /dev/null
+++ b/spec/requests/custom_wizard/tags_controller_spec.rb
@@ -0,0 +1,79 @@
+# frozen_string_literal: true
+
+describe CustomWizard::TagsController do
+ fab!(:user)
+ 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_group_1) { Fabricate(:tag_group, tags: [tag_1, tag_2]) }
+ fab!(:tag_group_2) { Fabricate(:tag_group, tags: [tag_3, tag_4]) }
+
+ before { sign_in(user) }
+
+ def search(params)
+ get "/custom-wizard/tags/search.json", params: params
+ expect(response.status).to eq(200)
+ response.parsed_body["results"].map { |result| result["name"] }
+ end
+
+ it "restricts results to the tags in the given tag group" do
+ expect(search(tag_groups: tag_group_1.name)).to contain_exactly("Angus", "Faizaan")
+ end
+
+ it "supports more than one tag group" do
+ expect(search(tag_groups: "#{tag_group_1.name},#{tag_group_2.name}")).to contain_exactly(
+ "Angus",
+ "Faizaan",
+ "Robert",
+ "Eli",
+ )
+ end
+
+ it "filters within the tag group by the query term" do
+ expect(search(tag_groups: tag_group_1.name, q: "ang")).to contain_exactly("Angus")
+ end
+
+ it "excludes already-selected tags from results" do
+ expect(search(tag_groups: tag_group_1.name, selected_tags: [tag_1.name])).to contain_exactly(
+ "Faizaan",
+ )
+ end
+
+ it "restricts results to the field content allow-list" do
+ expect(search(content: [tag_1.name, tag_3.name])).to contain_exactly("Angus", "Robert")
+ end
+
+ it "allows tags from either the tag group or the content allow-list" do
+ expect(search(tag_groups: tag_group_1.name, content: [tag_3.name])).to contain_exactly(
+ "Angus",
+ "Faizaan",
+ "Robert",
+ )
+ end
+
+ it "returns all tags when neither a tag group nor content is given" do
+ expect(search({})).to contain_exactly("Angus", "Faizaan", "Robert", "Eli")
+ end
+
+ it "returns no tags when the tag group does not exist" do
+ expect(search(tag_groups: "does-not-exist")).to be_empty
+ end
+
+ context "with a hidden (staff-only) tag in the allow-list" do
+ fab!(:hidden_tag) { Fabricate(:tag, name: "Secret") }
+ fab!(:hidden_group) do
+ Fabricate(:tag_group, permissions: { "staff" => 1 }, tag_names: [hidden_tag.name])
+ end
+
+ it "does not expose it to non-staff users" do
+ expect(search(content: [tag_1.name, hidden_tag.name])).to contain_exactly("Angus")
+ end
+
+ it "exposes it to staff" do
+ sign_in(Fabricate(:admin))
+ expect(search(content: [tag_1.name, hidden_tag.name])).to contain_exactly("Angus", "Secret")
+ end
+ end
+end
diff --git a/spec/serializers/custom_wizard/submission_serializer_spec.rb b/spec/serializers/custom_wizard/submission_serializer_spec.rb
index bf136c4873..6be91bd851 100644
--- a/spec/serializers/custom_wizard/submission_serializer_spec.rb
+++ b/spec/serializers/custom_wizard/submission_serializer_spec.rb
@@ -3,8 +3,8 @@
require_relative "../../plugin_helper"
describe CustomWizard::SubmissionSerializer do
- fab!(:user1) { Fabricate(:user) }
- fab!(:user2) { Fabricate(:user) }
+ fab!(:user1, :user)
+ fab!(:user2, :user)
let(:template_json) do
JSON.parse(
diff --git a/test/javascripts/acceptance/admin-custom-fields-unsubscribed-test.js b/test/javascripts/acceptance/admin-custom-fields-unsubscribed-test.js
index a8b76cb03d..0a9244aee6 100644
--- a/test/javascripts/acceptance/admin-custom-fields-unsubscribed-test.js
+++ b/test/javascripts/acceptance/admin-custom-fields-unsubscribed-test.js
@@ -111,16 +111,8 @@ acceptance("Admin | Custom Fields Unsubscribed", function (needs) {
let disabledOptions1 = queryAll(
'.admin-wizard-container details:has(summary[name="Filter by: Select a class"]) ul li.disabled'
);
- assert.equal(
- enabledOptions1.length,
- 2,
- "There are two enabled options for class fields"
- );
- assert.equal(
- disabledOptions1.length,
- 2,
- "There are two disabled options for class fields"
- );
+ assert.equal(enabledOptions1.length, 4, "All class options are enabled");
+ assert.equal(disabledOptions1.length, 0, "No class options are disabled");
const dropdown2 = selectKit(
'.admin-wizard-container details:has(summary[name="Filter by: Select a type"])'
);
@@ -131,16 +123,8 @@ acceptance("Admin | Custom Fields Unsubscribed", function (needs) {
let disabledOptions2 = queryAll(
'.admin-wizard-container details:has(summary[name="Filter by: Select a type"]) ul li.disabled'
);
- assert.equal(
- enabledOptions2.length,
- 3,
- "There are three enabled options for type"
- );
- assert.equal(
- disabledOptions2.length,
- 1,
- "There is one disabled option for type"
- );
+ assert.equal(enabledOptions2.length, 4, "All type options are enabled");
+ assert.equal(disabledOptions2.length, 0, "No type options are disabled");
});
test("change custom fields for unsubscribed plan", async (assert) => {
await visit("/admin/wizards/custom-fields");
diff --git a/test/javascripts/acceptance/admin-wizards-business-subscription-test.js b/test/javascripts/acceptance/admin-wizards-business-subscription-test.js
index 5ab915ac4d..7df657f5d2 100644
--- a/test/javascripts/acceptance/admin-wizards-business-subscription-test.js
+++ b/test/javascripts/acceptance/admin-wizards-business-subscription-test.js
@@ -3,7 +3,6 @@ import $ from "jquery";
import { test } from "qunit";
import {
acceptance,
- exists,
query,
queryAll,
visible,
@@ -65,18 +64,6 @@ acceptance("Admin | Custom Wizard Business Subscription", function (needs) {
assert.equal(count, 6, "There should be 6 admin tabs");
});
- test("shows authorized and subscribed", async (assert) => {
- await visit("/admin/wizards");
- assert.notOk(
- exists(".supplier-authorize .btn-primary:not(.update)"),
- "the authorize button is shown."
- );
- assert.strictEqual(
- query("button.wizard-subscription-badge span").innerText.trim(),
- "Support"
- );
- });
-
test("creating a new wizard", async (assert) => {
await visit("/admin/wizards/wizard");
await click(".admin-wizard-controls button");
diff --git a/test/javascripts/acceptance/admin-wizards-standard-subscription-test.js b/test/javascripts/acceptance/admin-wizards-standard-subscription-test.js
index f7a8002516..13eb471dff 100644
--- a/test/javascripts/acceptance/admin-wizards-standard-subscription-test.js
+++ b/test/javascripts/acceptance/admin-wizards-standard-subscription-test.js
@@ -3,7 +3,6 @@ import $ from "jquery";
import { test } from "qunit";
import {
acceptance,
- exists,
query,
queryAll,
visible,
@@ -65,18 +64,6 @@ acceptance("Admin | Custom Wizard Standard Subscription", function (needs) {
assert.equal(count, 5, "There should be 5 admin tabs");
});
- test("shows authorized and subscribed", async (assert) => {
- await visit("/admin/wizards");
- assert.notOk(
- exists(".supplier-authorize .btn-primary:not(.update)"),
- "the authorize button not shown."
- );
- assert.strictEqual(
- query("button.wizard-subscription-badge span").innerText.trim(),
- "Support"
- );
- });
-
test("creating a new wizard", async (assert) => {
await visit("/admin/wizards/wizard");
await click(".admin-wizard-controls button");
@@ -174,11 +161,11 @@ acceptance("Admin | Custom Wizard Standard Subscription", function (needs) {
".wizard-custom-action .setting .setting-value ul li.disabled"
);
assert.ok(
- listDisabled.length === 4,
+ listDisabled.length === 0,
"Disabled items displayed correctly in action dropdown"
);
assert.ok(
- listEnabled.length === 7,
+ listEnabled.length === 11,
"Enabled items displayed correctly in action dropdown"
);
await actionTypeDropdown.selectRowByValue("create_topic");
diff --git a/test/javascripts/acceptance/admin-wizards-unsubscribed-test.js b/test/javascripts/acceptance/admin-wizards-unsubscribed-test.js
index 0d700a4fb1..6ce7331e2f 100644
--- a/test/javascripts/acceptance/admin-wizards-unsubscribed-test.js
+++ b/test/javascripts/acceptance/admin-wizards-unsubscribed-test.js
@@ -78,18 +78,6 @@ acceptance("Admin | Custom Wizard Unsubscribed", function (needs) {
assert.equal(count, 5, "There should be 5 admin tabs");
});
- test("shows unauthorized and unsubscribed", async (assert) => {
- await visit("/admin/wizards");
- assert.ok(
- exists(".supplier-authorize .btn-primary"),
- "the authorize button is shown."
- );
- assert.strictEqual(
- query("button.wizard-subscription-badge span").innerText.trim(),
- "Subscribe"
- );
- });
-
test("creating a new wizard", async (assert) => {
await visit("/admin/wizards/wizard");
await click(".admin-wizard-controls button");
@@ -116,13 +104,8 @@ acceptance("Admin | Custom Wizard Unsubscribed", function (needs) {
let timeText = query(
".d-date-time-input .d-time-input span.name"
).innerText;
- const regex = /\d\d\:\d\d/;
+ const regex = /\d{1,2}:\d\d/;
assert.ok(regex.test(timeText));
- assert.equal(
- $.trim($("a[title='Subscribe to use these features']").text()),
- "Not Subscribed",
- "Show messsage and link of user not subscribed"
- );
await click(".step .link-list button");
const stepOneText = "step_1 (step_1)";
@@ -265,35 +248,6 @@ acceptance("Admin | Custom Wizard Unsubscribed", function (needs) {
"Google",
"The link text in the preview wrapper should be 'Google'"
);
- await click(
- ".wizard-custom-step .wizard-text-editor .d-editor button.local-dates"
- );
-
- assert.ok(
- exists(".d-modal.discourse-local-dates-create-modal"),
- "Insert date-time modal visible"
- );
-
- assert.ok(
- !exists(
- ".discourse-local-dates-create-modal .d-modal__body .advanced-options"
- ),
- "Advanced mode not visible"
- );
- await click(".d-modal__footer button.advanced-mode-btn");
- assert.ok(
- exists(
- ".discourse-local-dates-create-modal .d-modal__body .advanced-options"
- ),
- "Advanced mode is visible"
- );
- await click(".d-modal__footer button.btn-primary");
- assert.ok(
- exists(
- ".wizard-custom-step .wizard-text-editor .d-editor-preview-wrapper span.discourse-local-date"
- ),
- "Date inserted"
- );
await click(".field .link-list button");
assert.ok(
@@ -365,11 +319,11 @@ acceptance("Admin | Custom Wizard Unsubscribed", function (needs) {
".wizard-custom-action .setting .setting-value ul li.disabled"
);
assert.ok(
- listDisabled.length === 7,
+ listDisabled.length === 0,
"disabled items displayed correctly in action dropdown"
);
assert.ok(
- listEnabled.length === 4,
+ listEnabled.length === 11,
"Enabled items displayed correctly in action dropdown"
);
await actionTypeDropdown.selectRowByValue("create_topic");
@@ -529,7 +483,7 @@ acceptance("Admin | Custom Wizard Unsubscribed", function (needs) {
await click(
`.wizard-links.step .link-list div:nth-of-type(${
i + 1
- }) button.btn-text`
+ }) button:first-child`
);
assert.equal(
query(".wizard-custom-step input[name='title']").value,
@@ -546,7 +500,7 @@ acceptance("Admin | Custom Wizard Unsubscribed", function (needs) {
await click(
`.wizard-links.field .link-list div:nth-of-type(${
j + 1
- }) button.btn-text`
+ }) button:first-child`
);
assert.equal(
query(".wizard-custom-field.visible .setting:nth-of-type(1) input")
@@ -563,7 +517,9 @@ acceptance("Admin | Custom Wizard Unsubscribed", function (needs) {
let selectTypeElement = document.querySelector(
`.admin-wizard-container .wizard-custom-field.visible .setting:nth-of-type(5) .select-kit`
);
- let summaryElement = selectTypeElement.querySelector("summary");
+ let summaryElement = selectTypeElement.querySelector(
+ ".select-kit-selected-name"
+ );
assert.equal(
summaryElement.getAttribute("data-value"),
getUniqueWizard.steps[i].fields[j].type,
diff --git a/test/javascripts/acceptance/field-test.js b/test/javascripts/acceptance/field-test.js
index b5c2e80f19..2b3d1530d5 100644
--- a/test/javascripts/acceptance/field-test.js
+++ b/test/javascripts/acceptance/field-test.js
@@ -14,7 +14,7 @@ import { allFieldsWizard } from "../helpers/wizard";
acceptance("Field | Fields", function (needs) {
needs.pretender((server, helper) => {
server.get("/w/wizard.json", () => helper.response(allFieldsWizard));
- server.get("/tags/filter/search", () =>
+ server.get("/custom-wizard/tags/search", () =>
helper.response({ results: tagsJson["tags"] })
);
server.get("/u/search/users", () => helper.response(usersJson));
@@ -130,7 +130,7 @@ acceptance("Field | Fields", function (needs) {
);
await fillIn(".d-modal__body.insert-link .inputs .link-url", "google.com");
await fillIn(".d-modal__body.insert-link .inputs .link-text", "Google");
- await click(".d-modal__footer button.btn-danger");
+ await click(".d-modal__footer button.btn-transparent");
assert.strictEqual(
query(".wizard-field.composer-field .wizard-field-composer textarea")
@@ -152,10 +152,8 @@ acceptance("Field | Fields", function (needs) {
test("Time", async function (assert) {
await visit("/w/wizard");
- assert.ok(visible(".wizard-field.time-field .d-time-input .select-kit"));
- await click(
- ".wizard-field.time-field .d-time-input .select-kit .select-kit-header"
- );
+ assert.ok(visible(".wizard-field.time-field .select-kit"));
+ await click(".wizard-field.time-field .select-kit .select-kit-header");
assert.ok(visible(".wizard-field.time-field .select-kit-collection"));
});
@@ -226,17 +224,64 @@ acceptance("Field | Fields", function (needs) {
test("User", async function (assert) {
await visit("/w/wizard");
- await fillIn(
- ".wizard-field.user-selector-field .d-multi-select-trigger input",
- "a"
- );
+ await click(".wizard-field.user-selector-field .d-multi-select-trigger");
+ await fillIn(".d-multi-select__search-input", "a");
await triggerKeyEvent(
- ".wizard-field.user-selector-field .d-multi-select-trigger input",
+ ".d-multi-select__search-input",
"keyup",
"a".charCodeAt(0)
);
- assert.ok(visible(".wizard-field.user-selector-field .d-multi-select"));
+ assert.ok(
+ visible(".wizard-field.user-selector-field .d-multi-select-trigger")
+ );
// TODO: add assertion for ac results. autocomplete does not appear in time.
});
});
+
+acceptance("Field | Tag search request", function (needs) {
+ let capturedParams;
+
+ needs.pretender((server, helper) => {
+ const baseTagField = allFieldsWizard.steps[0].fields.find(
+ (field) => field.type === "tag"
+ );
+ const tagWizard = {
+ ...allFieldsWizard,
+ steps: [
+ {
+ ...allFieldsWizard.steps[0],
+ fields: [
+ {
+ ...baseTagField,
+ tag_groups: ["colours", "sizes"],
+ content: ["red", "blue"],
+ },
+ ],
+ },
+ ],
+ };
+
+ server.get("/w/wizard.json", () => helper.response(tagWizard));
+ server.get("/custom-wizard/tags/search", (request) => {
+ capturedParams = request.queryParams;
+ return helper.response({ results: tagsJson["tags"] });
+ });
+ });
+
+ test("sends the field's tag groups and content allow-list to the endpoint", async function (assert) {
+ await visit("/w/wizard");
+ await click(".wizard-field.tag-field .select-kit-header");
+
+ assert.strictEqual(
+ capturedParams.tag_groups,
+ "colours,sizes",
+ "joins the configured tag groups"
+ );
+ assert.deepEqual(
+ capturedParams.content,
+ ["red", "blue"],
+ "sends the content allow-list tag names"
+ );
+ });
+});