diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000..c799fa5a --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,7 @@ +# Security Policy + +The security of Discourse plugins are premised on the security of [Discourse](https://github.com/discourse/discourse). Please first consider whether a security issue is best reported and handled by the Discourse team. You can view the Discourse security policy [here](https://github.com/discourse/discourse/security/policy). + +## Reporting a Vulnerability + +If you find a security vulnerability that is specific to this plugin, please report it to development@pavilion.tech. Security issues always take precedence over all other work. All commits specific to security are prefixed with SECURITY. diff --git a/app/controllers/custom_wizard/admin/wizard.rb b/app/controllers/custom_wizard/admin/wizard.rb index c778bc7e..08e7b6d0 100644 --- a/app/controllers/custom_wizard/admin/wizard.rb +++ b/app/controllers/custom_wizard/admin/wizard.rb @@ -114,6 +114,7 @@ class CustomWizard::AdminWizardController < CustomWizard::AdminController :property, :preview_template, :placeholder, + :can_create_tag, prefill: mapped_params, content: mapped_params, condition: mapped_params, diff --git a/app/serializers/custom_wizard/wizard_field_serializer.rb b/app/serializers/custom_wizard/wizard_field_serializer.rb index d5f57060..56e86cc8 100644 --- a/app/serializers/custom_wizard/wizard_field_serializer.rb +++ b/app/serializers/custom_wizard/wizard_field_serializer.rb @@ -17,6 +17,7 @@ class CustomWizard::FieldSerializer < ::ApplicationSerializer :property, :content, :tag_groups, + :can_create_tag, :validations, :max_length, :char_counter, @@ -98,6 +99,10 @@ class CustomWizard::FieldSerializer < ::ApplicationSerializer object.tag_groups end + def can_create_tag + object.can_create_tag + end + def validations validations = {} object.validations&.each do |type, props| diff --git a/assets/javascripts/discourse/components/custom-wizard-composer-editor.js.es6 b/assets/javascripts/discourse/components/custom-wizard-composer-editor.js.es6 index 92c56ea1..5e2ef424 100644 --- a/assets/javascripts/discourse/components/custom-wizard-composer-editor.js.es6 +++ b/assets/javascripts/discourse/components/custom-wizard-composer-editor.js.es6 @@ -149,7 +149,7 @@ export default ComposerEditor.extend({ extraButtons(toolbar) { const component = this; - if (this.allowUpload && this.uploadIcon && !this.site.mobileView) { + if (this.allowUpload && this.uploadIcon) { toolbar.addButton({ id: "upload", group: "insertions", diff --git a/assets/javascripts/discourse/components/wizard-text-editor.js.es6 b/assets/javascripts/discourse/components/wizard-text-editor.js.es6 index 88d7200c..b6d07cef 100644 --- a/assets/javascripts/discourse/components/wizard-text-editor.js.es6 +++ b/assets/javascripts/discourse/components/wizard-text-editor.js.es6 @@ -5,11 +5,7 @@ import { scheduleOnce } from "@ember/runloop"; import Component from "@ember/component"; import I18n from "I18n"; -const excludedUserProperties = [ - "avatar", - "profile_background", - "card_background", -]; +const excludedUserProperties = ["profile_background", "card_background"]; export default Component.extend({ classNames: "wizard-text-editor", @@ -52,12 +48,12 @@ export default Component.extend({ @discourseComputed("wizardFields") wizardFieldList(wizardFields) { - return wizardFields.map((f) => ` w{${f.id}}`); + return (wizardFields || []).map((f) => ` w{${f.id}}`); }, @discourseComputed("wizardActions") wizardActionList(wizardActions) { - return wizardActions.map((a) => ` w{${a.id}}`); + return (wizardActions || []).map((a) => ` w{${a.id}}`); }, actions: { diff --git a/assets/javascripts/discourse/controllers/admin-wizards-wizard-show.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards-wizard-show.js.es6 index e6b0ad04..c9a80e0e 100644 --- a/assets/javascripts/discourse/controllers/admin-wizards-wizard-show.js.es6 +++ b/assets/javascripts/discourse/controllers/admin-wizards-wizard-show.js.es6 @@ -36,7 +36,8 @@ export default Controller.extend({ @discourseComputed("wizard.id") wizardUrl(wizardId) { - return window.location.origin + "/w/" + dasherize(wizardId); + let baseUrl = window.location.href.split("/admin"); + return baseUrl[0] + "/w/" + dasherize(wizardId); }, @discourseComputed("wizard.after_time_scheduled") diff --git a/assets/javascripts/discourse/templates/components/custom-wizard-field-tag.hbs b/assets/javascripts/discourse/templates/components/custom-wizard-field-tag.hbs index fb45db4e..90679ae7 100644 --- a/assets/javascripts/discourse/templates/components/custom-wizard-field-tag.hbs +++ b/assets/javascripts/discourse/templates/components/custom-wizard-field-tag.hbs @@ -5,4 +5,5 @@ everyTag=true options=(hash maximum=field.limit + allowAny=field.can_create_tag )}} diff --git a/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs b/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs index 6e524fe3..6273f9a9 100644 --- a/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs +++ b/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs @@ -222,6 +222,18 @@ }} + +
+
+ +
+ +
+ {{input + type="checkbox" + checked=field.can_create_tag}} +
+
{{/if}} {{#wizard-subscription-container}} diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 267f099f..b3adf8d4 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -16,7 +16,7 @@ en: x_characters: one: "%{count} Character" other: "%{count} Characters" - quit: "Maybe Later" + quit: "Exit" done_custom: "Done" back: "Back" next: "Next" @@ -277,6 +277,7 @@ en: prefill: "Prefill" content: "Content" tag_groups: "Tag Groups" + can_create_tag: "Can Create Tag" date_time_format: label: "Format" instructions: "Moment.js format" diff --git a/lefthook.yml b/lefthook.yml index 995d1f2a..91d2ad53 100644 --- a/lefthook.yml +++ b/lefthook.yml @@ -20,9 +20,6 @@ pre-commit: glob: "*.js" include: "app/assets/javascripts|plugins/.+?/assets/javascripts" run: yarn eslint -f compact --quiet {staged_files} - i18n-lint: - glob: "**/{client,server}.en.yml" - run: bundle exec ruby script/i18n_lint.rb {staged_files} lints: parallel: true @@ -36,6 +33,3 @@ lints: run: yarn prettier --list-different **/*.scss eslint: run: yarn eslint -f compact --quiet --ext .js . - i18n-lint: - glob: "**/{client,server}.en.yml" - run: bundle exec ruby script/i18n_lint.rb {all_files} diff --git a/lib/custom_wizard/builder.rb b/lib/custom_wizard/builder.rb index 251ec1e5..3b12ad27 100644 --- a/lib/custom_wizard/builder.rb +++ b/lib/custom_wizard/builder.rb @@ -135,6 +135,10 @@ class CustomWizard::Builder params[:limit] = field_template['limit'] end + if field_template['type'] === 'tag' + params[:can_create_tag] = standardise_boolean(field_template['can_create_tag']) + end + if field_template['type'] === 'category' params[:property] = field_template['property'] end diff --git a/lib/custom_wizard/field.rb b/lib/custom_wizard/field.rb index 600f685e..6215fc8c 100644 --- a/lib/custom_wizard/field.rb +++ b/lib/custom_wizard/field.rb @@ -22,6 +22,7 @@ class CustomWizard::Field :property, :content, :tag_groups, + :can_create_tag, :preview_template, :placeholder @@ -47,6 +48,7 @@ class CustomWizard::Field @property = attrs[:property] @content = attrs[:content] @tag_groups = attrs[:tag_groups] + @can_create_tag = attrs[:can_create_tag] @preview_template = attrs[:preview_template] @placeholder = attrs[:placeholder] end @@ -113,7 +115,8 @@ class CustomWizard::Field limit: nil, prefill: nil, content: nil, - tag_groups: nil + tag_groups: nil, + can_create_tag: false }, category: { limit: 1, diff --git a/lib/custom_wizard/mapper.rb b/lib/custom_wizard/mapper.rb index aa444de1..b677a710 100644 --- a/lib/custom_wizard/mapper.rb +++ b/lib/custom_wizard/mapper.rb @@ -211,6 +211,8 @@ class CustomWizard::Mapper user.send(value) elsif USER_OPTION_FIELDS.include?(value) user.user_option.send(value) + elsif value.include?('avatar') + get_avatar_url(value) else nil end @@ -269,4 +271,15 @@ class CustomWizard::Mapper def bool(value) ActiveRecord::Type::Boolean.new.cast(value) end + + def get_avatar_url(value) + parts = value.split('.') + valid_sizes = Discourse.avatar_sizes.to_a + + if value === 'avatar' || parts.size === 1 || valid_sizes.exclude?(parts.last.to_i) + user.small_avatar_url + else + user.avatar_template_url.gsub("{size}", parts.last) + end + end end diff --git a/plugin.rb b/plugin.rb index 51774b64..73701d84 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.0.7 +# version: 2.1.3 # authors: Angus McLeod, Faizaan Gagan, Robert Barrow, Keegan George, Kaitlin Maddever # url: https://github.com/paviliondev/discourse-custom-wizard # contact_emails: development@pavilion.tech diff --git a/spec/components/custom_wizard/mapper_spec.rb b/spec/components/custom_wizard/mapper_spec.rb index 111558a4..56778a07 100644 --- a/spec/components/custom_wizard/mapper_spec.rb +++ b/spec/components/custom_wizard/mapper_spec.rb @@ -1,4 +1,4 @@ -# frozen_string_literal: true +# rubocop:disable Style/FrozenStringLiteralComment describe CustomWizard::Mapper do fab!(:user1) { @@ -254,6 +254,36 @@ describe CustomWizard::Mapper do user: user1 ).perform).to eq("Time: #{Time.now.strftime("%B %-d, %Y")}") end + + it "avatar" do + expect(CustomWizard::Mapper.new( + inputs: inputs['interpolate_avatar'], + data: data, + user: user1 + ).perform).to eq("Avatar: ![avatar](#{user1.small_avatar_url})") + end + + it "avatar with invalid size" do + avatar_inputs = inputs['interpolate_avatar'].dup + avatar_inputs[0]["output"] = "Avatar: ![avatar](u{avatar.345})" + + expect(CustomWizard::Mapper.new( + inputs: avatar_inputs, + data: data, + user: user1 + ).perform).to eq("Avatar: ![avatar](#{user1.small_avatar_url})") + end + + it "avatar with valid size" do + avatar_inputs = inputs['interpolate_avatar'].dup + avatar_inputs[0]["output"] = "Avatar: ![avatar](u{avatar.120})" + + expect(CustomWizard::Mapper.new( + inputs: avatar_inputs, + data: data, + user: user1 + ).perform).to eq("Avatar: ![avatar](#{user1.avatar_template_url.gsub("{size}", "120")})") + end end it "handles greater than pairs" do diff --git a/spec/fixtures/mapper/inputs.json b/spec/fixtures/mapper/inputs.json index 443f186b..3fd406a4 100644 --- a/spec/fixtures/mapper/inputs.json +++ b/spec/fixtures/mapper/inputs.json @@ -89,6 +89,14 @@ "output": "Time: v{time}" } ], + "interpolate_avatar": [ + { + "type": "assignment", + "output_type": "text", + "output_connector": "set", + "output": "Avatar: ![avatar](u{avatar})" + } + ], "validation": [ { "type": "validation",