From c490da31457e5200306c128f47531c713a35ed56 Mon Sep 17 00:00:00 2001 From: Keegan George Date: Fri, 18 Jun 2021 10:25:01 -0700 Subject: [PATCH 001/160] UX: Add wizard message component to submissions page --- .../discourse/controllers/admin-wizards-submissions.js.es6 | 6 ++++++ .../discourse/templates/admin-wizards-submissions.hbs | 5 +++++ config/locales/client.en.yml | 3 +++ 3 files changed, 14 insertions(+) create mode 100644 assets/javascripts/discourse/controllers/admin-wizards-submissions.js.es6 diff --git a/assets/javascripts/discourse/controllers/admin-wizards-submissions.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards-submissions.js.es6 new file mode 100644 index 00000000..b5b9365a --- /dev/null +++ b/assets/javascripts/discourse/controllers/admin-wizards-submissions.js.es6 @@ -0,0 +1,6 @@ +import Controller from "@ember/controller"; + +export default Controller.extend({ + messageKey: "select", + documentationUrl: "https://thepavilion.io/t/2818", +}); diff --git a/assets/javascripts/discourse/templates/admin-wizards-submissions.hbs b/assets/javascripts/discourse/templates/admin-wizards-submissions.hbs index d843485a..5a660790 100644 --- a/assets/javascripts/discourse/templates/admin-wizards-submissions.hbs +++ b/assets/javascripts/discourse/templates/admin-wizards-submissions.hbs @@ -8,6 +8,11 @@ )}} +{{wizard-message + key=messageKey + url=documentationUrl + component="submissions"}} +
{{outlet}}
diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 43b86698..fa3a6c5d 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -91,6 +91,9 @@ en: destroying: Destroying wizards... import_complete: Import complete destroy_complete: Destruction complete + submissions: + select: "Select a wizard to see its submissions" + documentation: "Check out the submissions documentation" editor: show: "Show" From 10c7c8bcc09721ae917dba6ef9f97524449da8de Mon Sep 17 00:00:00 2001 From: Keegan George Date: Fri, 18 Jun 2021 11:25:51 -0700 Subject: [PATCH 002/160] UX: Change info message when selecting a wizard in the submissions pane --- .../admin-wizards-submissions.js.es6 | 30 ++++++++++++++++++- .../templates/admin-wizards-submissions.hbs | 3 +- config/locales/client.en.yml | 1 + 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/assets/javascripts/discourse/controllers/admin-wizards-submissions.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards-submissions.js.es6 index b5b9365a..7388a8d6 100644 --- a/assets/javascripts/discourse/controllers/admin-wizards-submissions.js.es6 +++ b/assets/javascripts/discourse/controllers/admin-wizards-submissions.js.es6 @@ -1,6 +1,34 @@ import Controller from "@ember/controller"; +import { default as discourseComputed } from "discourse-common/utils/decorators"; export default Controller.extend({ - messageKey: "select", documentationUrl: "https://thepavilion.io/t/2818", + + @discourseComputed("wizardId") + wizardName(wizardId) { + let currentWizard = this.wizardList.find( + (wizard) => wizard.id === wizardId + ); + if (currentWizard) { + return currentWizard.name; + } + }, + + @discourseComputed("wizardName") + messageOpts(wizardName) { + return { + wizardName, + }; + }, + + @discourseComputed("wizardId") + messageKey(wizardId) { + let key = "select"; + + if (wizardId) { + key = "viewing"; + } + + return key; + }, }); diff --git a/assets/javascripts/discourse/templates/admin-wizards-submissions.hbs b/assets/javascripts/discourse/templates/admin-wizards-submissions.hbs index 5a660790..07dd1682 100644 --- a/assets/javascripts/discourse/templates/admin-wizards-submissions.hbs +++ b/assets/javascripts/discourse/templates/admin-wizards-submissions.hbs @@ -1,4 +1,4 @@ -
+
{{combo-box value=wizardId content=wizardList @@ -10,6 +10,7 @@ {{wizard-message key=messageKey + opts=messageOpts url=documentationUrl component="submissions"}} diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index fa3a6c5d..31f10924 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -93,6 +93,7 @@ en: destroy_complete: Destruction complete submissions: select: "Select a wizard to see its submissions" + viewing: "You're viewing the logs of the %{wizardName}. Click 'Download' on the right to download them." documentation: "Check out the submissions documentation" editor: From dd8513a56390f5b254441783fccf23a72c748575 Mon Sep 17 00:00:00 2001 From: Keegan George Date: Fri, 18 Jun 2021 11:47:24 -0700 Subject: [PATCH 003/160] UX: Add wizard-message component to logs pane --- .../discourse/controllers/admin-wizards-logs.js.es6 | 2 ++ .../javascripts/discourse/templates/admin-wizards-logs.hbs | 6 ++++++ config/locales/client.en.yml | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/assets/javascripts/discourse/controllers/admin-wizards-logs.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards-logs.js.es6 index 9559b01b..f45013d7 100644 --- a/assets/javascripts/discourse/controllers/admin-wizards-logs.js.es6 +++ b/assets/javascripts/discourse/controllers/admin-wizards-logs.js.es6 @@ -9,6 +9,8 @@ export default Controller.extend({ page: 0, canLoadMore: true, logs: [], + documentationUrl: "https://thepavilion.io/t/2818", + messageKey: "viewing", loadLogs() { if (!this.canLoadMore) { diff --git a/assets/javascripts/discourse/templates/admin-wizards-logs.hbs b/assets/javascripts/discourse/templates/admin-wizards-logs.hbs index 18fd3fdb..33402cce 100644 --- a/assets/javascripts/discourse/templates/admin-wizards-logs.hbs +++ b/assets/javascripts/discourse/templates/admin-wizards-logs.hbs @@ -8,6 +8,12 @@ class="refresh"}}
+{{wizard-message + key=messageKey + opts=messageOpts + url=documentationUrl + component="logs"}} + {{#load-more selector=".log-list tr" action=(action "loadMore") class="wizard-logs"}} {{#if noResults}}

{{i18n "search.no_results"}}

diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 31f10924..569f252f 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -95,6 +95,10 @@ en: select: "Select a wizard to see its submissions" viewing: "You're viewing the logs of the %{wizardName}. Click 'Download' on the right to download them." documentation: "Check out the submissions documentation" + logs: + viewing: "View recent logs for wizards on the forum" + documentation: "Check out the logs documentation" + editor: show: "Show" From 34fee3729cb4e5c62a94e5875436eabee7099777 Mon Sep 17 00:00:00 2001 From: angusmcleod Date: Wed, 14 Jul 2021 14:04:19 +0800 Subject: [PATCH 004/160] Add pagination to submissions --- .../admin-wizards-submissions-show.js.es6 | 28 ++++++++++++ .../discourse/models/custom-wizard.js.es6 | 44 ++++++++++++++++++- .../admin-wizards-submissions-show.js.es6 | 38 +++------------- .../admin-wizards-submissions-show.hbs | 44 +++++++++++-------- .../custom_wizard/admin/submissions.rb | 12 +++-- lib/custom_wizard/submission.rb | 30 +++++++++---- lib/custom_wizard/wizard.rb | 2 +- .../custom_wizard/submission_serializer.rb | 5 +-- .../custom_wizard/submission_spec.rb | 15 +++++-- 9 files changed, 146 insertions(+), 72 deletions(-) diff --git a/assets/javascripts/discourse/controllers/admin-wizards-submissions-show.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards-submissions-show.js.es6 index f5f9926d..bc38648d 100644 --- a/assets/javascripts/discourse/controllers/admin-wizards-submissions-show.js.es6 +++ b/assets/javascripts/discourse/controllers/admin-wizards-submissions-show.js.es6 @@ -1,6 +1,34 @@ import Controller from "@ember/controller"; import { fmt } from "discourse/lib/computed"; +import { empty } from '@ember/object/computed'; +import CustomWizard from "../models/custom-wizard"; export default Controller.extend({ downloadUrl: fmt("wizard.id", "/admin/wizards/submissions/%@/download"), + noResults: empty('submissions'), + page: 0, + total: 0, + + loadMoreSubmissions() { + const page = this.get('page'); + const wizardId = this.get('wizard.id'); + + this.set('loadingMore', true); + CustomWizard.submissions(wizardId, page).then(result => { + if (result.submissions) { + this.get('submissions').pushObjects(result.submissions); + } + }).finally(() => { + this.set('loadingMore', false); + }); + }, + + actions: { + loadMore() { + if (!this.loadingMore && (this.submissions.length < this.total)) { + this.set('page', this.get('page') + 1); + this.loadMoreSubmissions(); + } + } + } }); diff --git a/assets/javascripts/discourse/models/custom-wizard.js.es6 b/assets/javascripts/discourse/models/custom-wizard.js.es6 index e6a8408d..e4b0a530 100644 --- a/assets/javascripts/discourse/models/custom-wizard.js.es6 +++ b/assets/javascripts/discourse/models/custom-wizard.js.es6 @@ -211,9 +211,51 @@ CustomWizard.reopenClass({ .catch(popupAjaxError); }, - submissions(wizardId) { + submissions(wizardId, page = null) { + let data = {}; + + if (page) { + data.page = page; + } + return ajax(`/admin/wizards/submissions/${wizardId}`, { type: "GET", + data + }).then(result => { + if (result.wizard) { + let fields = ["username"]; + let submissions = []; + let wizard = result.wizard; + let total = result.total; + + result.submissions.forEach((s) => { + let submission = { + username: s.username, + }; + + Object.keys(s.fields).forEach((f) => { + if (fields.indexOf(f) < 0) { + fields.push(f); + } + + if (fields.includes(f)) { + submission[f] = s.fields[f]; + } + }); + + submission['submitted_at'] = s.submitted_at; + submissions.push(submission); + }); + + fields.push("submitted_at"); + + return { + wizard, + fields, + submissions, + total + }; + } }).catch(popupAjaxError); }, diff --git a/assets/javascripts/discourse/routes/admin-wizards-submissions-show.js.es6 b/assets/javascripts/discourse/routes/admin-wizards-submissions-show.js.es6 index 73168ff3..509816da 100644 --- a/assets/javascripts/discourse/routes/admin-wizards-submissions-show.js.es6 +++ b/assets/javascripts/discourse/routes/admin-wizards-submissions-show.js.es6 @@ -1,7 +1,6 @@ import CustomWizard from "../models/custom-wizard"; import DiscourseRoute from "discourse/routes/discourse"; - -const excludedMetaFields = ["route_to", "redirect_on_complete", "redirect_to"]; +import { A } from "@ember/array"; export default DiscourseRoute.extend({ model(params) { @@ -9,34 +8,11 @@ export default DiscourseRoute.extend({ }, setupController(controller, model) { - if (model && model.submissions) { - let fields = ["username"]; - model.submissions.forEach((s) => { - Object.keys(s.fields).forEach((k) => { - if (!excludedMetaFields.includes(k) && fields.indexOf(k) < 0) { - fields.push(k); - } - }); - }); - - let submissions = []; - model.submissions.forEach((s) => { - let submission = { - username: s.username, - }; - Object.keys(s.fields).forEach((f) => { - if (fields.includes(f)) { - submission[f] = s.fields[f]; - } - }); - submissions.push(submission); - }); - - controller.setProperties({ - wizard: model.wizard, - submissions, - fields, - }); - } + controller.setProperties({ + wizard: model.wizard, + fields: model.fields, + submissions: A(model.submissions), + total: model.total + }); }, }); diff --git a/assets/javascripts/discourse/templates/admin-wizards-submissions-show.hbs b/assets/javascripts/discourse/templates/admin-wizards-submissions-show.hbs index 6d1f255b..9e8e10c8 100644 --- a/assets/javascripts/discourse/templates/admin-wizards-submissions-show.hbs +++ b/assets/javascripts/discourse/templates/admin-wizards-submissions-show.hbs @@ -11,23 +11,31 @@
- - - - {{#each fields as |f|}} - - {{/each}} - - - - {{#each submissions as |s|}} - - {{#each-in s as |k v|}} - - {{/each-in}} - - {{/each}} - -
{{f}}
{{v}}
+ {{#load-more selector=".wizard-submissions tr" action=(action "loadMore")}} + {{#if noResults}} +

{{i18n "search.no_results"}}

+ {{else}} + + + + {{#each fields as |f|}} + + {{/each}} + + + + {{#each submissions as |s|}} + + {{#each-in s as |k v|}} + + {{/each-in}} + + {{/each}} + +
{{f}}
{{v}}
+ {{/if}} + + {{conditional-loading-spinner condition=loadingMore}} + {{/load-more}}
{{/if}} diff --git a/controllers/custom_wizard/admin/submissions.rb b/controllers/custom_wizard/admin/submissions.rb index 4cb2a0e4..c3bf809f 100644 --- a/controllers/custom_wizard/admin/submissions.rb +++ b/controllers/custom_wizard/admin/submissions.rb @@ -13,12 +13,16 @@ class CustomWizard::AdminSubmissionsController < CustomWizard::AdminController def show render_json_dump( wizard: CustomWizard::BasicWizardSerializer.new(@wizard, root: false), - submissions: ActiveModel::ArraySerializer.new(ordered_submissions, each_serializer: CustomWizard::SubmissionSerializer) + submissions: ActiveModel::ArraySerializer.new( + submission_list.submissions, + each_serializer: CustomWizard::SubmissionSerializer + ), + total: submission_list.total ) end def download - send_data ordered_submissions.to_json, + send_data submission_list.submissions.to_json, filename: "#{Discourse.current_hostname}-wizard-submissions-#{@wizard.name}.json", content_type: "application/json", disposition: "attachment" @@ -26,7 +30,7 @@ class CustomWizard::AdminSubmissionsController < CustomWizard::AdminController protected - def ordered_submissions - CustomWizard::Submission.list(@wizard, order_by: 'id') + def submission_list + CustomWizard::Submission.list(@wizard, page: params[:page].to_i) end end diff --git a/lib/custom_wizard/submission.rb b/lib/custom_wizard/submission.rb index e50cb259..95b4f7fa 100644 --- a/lib/custom_wizard/submission.rb +++ b/lib/custom_wizard/submission.rb @@ -1,7 +1,8 @@ # frozen_string_literal: true class CustomWizard::Submission include ActiveModel::SerializerSupport - + + PAGE_LIMIT = 50 KEY ||= "submissions" META ||= %w(submitted_at route_to redirect_on_complete redirect_to) @@ -44,7 +45,7 @@ class CustomWizard::Submission validate submission_list = self.class.list(wizard, user_id: user.id) - submissions = submission_list.select { |submission| submission.id != self.id } + submissions = submission_list.submissions.select { |submission| submission.id != self.id } submissions.push(self) submission_data = submissions.map { |submission| data_to_save(submission) } @@ -92,27 +93,38 @@ class CustomWizard::Submission end def self.get(wizard, user_id) - data = PluginStore.get("#{wizard.id}_#{KEY}", user_id).first + data = PluginStore.get("#{wizard.id}_#{KEY}", user_id).last new(wizard, data, user_id) end - def self.list(wizard, user_id: nil, order_by: nil) + def self.list(wizard, user_id: nil, page: nil) params = { plugin_name: "#{wizard.id}_#{KEY}" } params[:key] = user_id if user_id.present? query = PluginStoreRow.where(params) - query = query.order("#{order_by} DESC") if order_by.present? - - result = [] + result = OpenStruct.new(submissions: [], total: nil) query.each do |record| - if (submission_data = ::JSON.parse(record.value)).any? + if (submission_data = ::JSON.parse(record.value)).any? submission_data.each do |data| - result.push(new(wizard, data, record.key)) + result.submissions.push(new(wizard, data, record.key)) end end end + result.total = result.submissions.size + + if !page.nil? + start = page * PAGE_LIMIT + length = PAGE_LIMIT + + if result.submissions.length > start + result.submissions = result.submissions[start, length] + else + result.submissions = [] + end + end + result end end diff --git a/lib/custom_wizard/wizard.rb b/lib/custom_wizard/wizard.rb index 8f5a897f..e8427334 100644 --- a/lib/custom_wizard/wizard.rb +++ b/lib/custom_wizard/wizard.rb @@ -272,7 +272,7 @@ class CustomWizard::Wizard def submissions return nil unless user.present? - @submissions ||= CustomWizard::Submission.list(self, user_id: user.id) + @submissions ||= CustomWizard::Submission.list(self, user_id: user.id).submissions end def current_submission diff --git a/serializers/custom_wizard/submission_serializer.rb b/serializers/custom_wizard/submission_serializer.rb index 52f0cb32..992deacb 100644 --- a/serializers/custom_wizard/submission_serializer.rb +++ b/serializers/custom_wizard/submission_serializer.rb @@ -3,10 +3,7 @@ class CustomWizard::SubmissionSerializer < ApplicationSerializer attributes :id, :username, :fields, - :submitted_at, - :route_to, - :redirect_on_complete, - :redirect_to + :submitted_at def username object.user.present? ? diff --git a/spec/components/custom_wizard/submission_spec.rb b/spec/components/custom_wizard/submission_spec.rb index a8c33861..ce9756d1 100644 --- a/spec/components/custom_wizard/submission_spec.rb +++ b/spec/components/custom_wizard/submission_spec.rb @@ -21,8 +21,11 @@ describe CustomWizard::Submission do @wizard = CustomWizard::Wizard.create(template_json["id"], user) @wizard2 = CustomWizard::Wizard.create(template_json["id"], user2) @wizard3 = CustomWizard::Wizard.create(template_json_2["id"], user) + @count = CustomWizard::Submission::PAGE_LIMIT + 20 - described_class.new(@wizard, step_1_field_1: "I am a user submission").save + @count.times do |index| + described_class.new(@wizard, step_1_field_1: "I am user submission #{index+1}").save + end described_class.new(@wizard2, step_1_field_1: "I am another user's submission").save described_class.new(@wizard3, step_1_field_1: "I am a user submission on another wizard").save end @@ -30,14 +33,18 @@ describe CustomWizard::Submission do it "saves a user's submission" do expect( described_class.get(@wizard, user.id).fields["step_1_field_1"] - ).to eq("I am a user submission") + ).to eq("I am user submission #{@count}") end it "list submissions by wizard" do - expect(described_class.list(@wizard).size).to eq(2) + expect(described_class.list(@wizard).total).to eq(@count + 1) end it "list submissions by wizard and user" do - expect(described_class.list(@wizard, user_id: user.id).size).to eq(1) + expect(described_class.list(@wizard, user_id: user.id).total).to eq(@count) + end + + it "paginates submission lists" do + expect(described_class.list(@wizard, page: 1).submissions.size).to eq((@count + 1) - CustomWizard::Submission::PAGE_LIMIT) end end From 56a146341335d4ef768012b55821f3048dbda3c5 Mon Sep 17 00:00:00 2001 From: angusmcleod Date: Wed, 14 Jul 2021 14:05:13 +0800 Subject: [PATCH 005/160] Apply prettier --- .../admin-wizards-submissions-show.js.es6 | 34 +++++----- .../discourse/models/custom-wizard.js.es6 | 64 ++++++++++--------- .../admin-wizards-submissions-show.js.es6 | 2 +- 3 files changed, 52 insertions(+), 48 deletions(-) diff --git a/assets/javascripts/discourse/controllers/admin-wizards-submissions-show.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards-submissions-show.js.es6 index bc38648d..6352b3b2 100644 --- a/assets/javascripts/discourse/controllers/admin-wizards-submissions-show.js.es6 +++ b/assets/javascripts/discourse/controllers/admin-wizards-submissions-show.js.es6 @@ -1,34 +1,36 @@ import Controller from "@ember/controller"; import { fmt } from "discourse/lib/computed"; -import { empty } from '@ember/object/computed'; +import { empty } from "@ember/object/computed"; import CustomWizard from "../models/custom-wizard"; export default Controller.extend({ downloadUrl: fmt("wizard.id", "/admin/wizards/submissions/%@/download"), - noResults: empty('submissions'), + noResults: empty("submissions"), page: 0, total: 0, loadMoreSubmissions() { - const page = this.get('page'); - const wizardId = this.get('wizard.id'); + const page = this.get("page"); + const wizardId = this.get("wizard.id"); - this.set('loadingMore', true); - CustomWizard.submissions(wizardId, page).then(result => { - if (result.submissions) { - this.get('submissions').pushObjects(result.submissions); - } - }).finally(() => { - this.set('loadingMore', false); - }); + this.set("loadingMore", true); + CustomWizard.submissions(wizardId, page) + .then((result) => { + if (result.submissions) { + this.get("submissions").pushObjects(result.submissions); + } + }) + .finally(() => { + this.set("loadingMore", false); + }); }, actions: { loadMore() { - if (!this.loadingMore && (this.submissions.length < this.total)) { - this.set('page', this.get('page') + 1); + if (!this.loadingMore && this.submissions.length < this.total) { + this.set("page", this.get("page") + 1); this.loadMoreSubmissions(); } - } - } + }, + }, }); diff --git a/assets/javascripts/discourse/models/custom-wizard.js.es6 b/assets/javascripts/discourse/models/custom-wizard.js.es6 index e4b0a530..5e94e31e 100644 --- a/assets/javascripts/discourse/models/custom-wizard.js.es6 +++ b/assets/javascripts/discourse/models/custom-wizard.js.es6 @@ -220,43 +220,45 @@ CustomWizard.reopenClass({ return ajax(`/admin/wizards/submissions/${wizardId}`, { type: "GET", - data - }).then(result => { - if (result.wizard) { - let fields = ["username"]; - let submissions = []; - let wizard = result.wizard; - let total = result.total; + data, + }) + .then((result) => { + if (result.wizard) { + let fields = ["username"]; + let submissions = []; + let wizard = result.wizard; + let total = result.total; - result.submissions.forEach((s) => { - let submission = { - username: s.username, - }; + result.submissions.forEach((s) => { + let submission = { + username: s.username, + }; - Object.keys(s.fields).forEach((f) => { - if (fields.indexOf(f) < 0) { - fields.push(f); - } + Object.keys(s.fields).forEach((f) => { + if (fields.indexOf(f) < 0) { + fields.push(f); + } - if (fields.includes(f)) { - submission[f] = s.fields[f]; - } + if (fields.includes(f)) { + submission[f] = s.fields[f]; + } + }); + + submission["submitted_at"] = s.submitted_at; + submissions.push(submission); }); - - submission['submitted_at'] = s.submitted_at; - submissions.push(submission); - }); - fields.push("submitted_at"); + fields.push("submitted_at"); - return { - wizard, - fields, - submissions, - total - }; - } - }).catch(popupAjaxError); + return { + wizard, + fields, + submissions, + total, + }; + } + }) + .catch(popupAjaxError); }, create(wizardJson = {}) { diff --git a/assets/javascripts/discourse/routes/admin-wizards-submissions-show.js.es6 b/assets/javascripts/discourse/routes/admin-wizards-submissions-show.js.es6 index 509816da..2ff9fbf9 100644 --- a/assets/javascripts/discourse/routes/admin-wizards-submissions-show.js.es6 +++ b/assets/javascripts/discourse/routes/admin-wizards-submissions-show.js.es6 @@ -12,7 +12,7 @@ export default DiscourseRoute.extend({ wizard: model.wizard, fields: model.fields, submissions: A(model.submissions), - total: model.total + total: model.total, }); }, }); From 1a78b44d35fa35b0192d5c72093d95817e172003 Mon Sep 17 00:00:00 2001 From: angusmcleod Date: Wed, 14 Jul 2021 14:06:54 +0800 Subject: [PATCH 006/160] Apply rubocop --- lib/custom_wizard/submission.rb | 4 ++-- spec/components/custom_wizard/submission_spec.rb | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/custom_wizard/submission.rb b/lib/custom_wizard/submission.rb index 95b4f7fa..618a9a67 100644 --- a/lib/custom_wizard/submission.rb +++ b/lib/custom_wizard/submission.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class CustomWizard::Submission include ActiveModel::SerializerSupport - + PAGE_LIMIT = 50 KEY ||= "submissions" META ||= %w(submitted_at route_to redirect_on_complete redirect_to) @@ -105,7 +105,7 @@ class CustomWizard::Submission result = OpenStruct.new(submissions: [], total: nil) query.each do |record| - if (submission_data = ::JSON.parse(record.value)).any? + if (submission_data = ::JSON.parse(record.value)).any? submission_data.each do |data| result.submissions.push(new(wizard, data, record.key)) end diff --git a/spec/components/custom_wizard/submission_spec.rb b/spec/components/custom_wizard/submission_spec.rb index ce9756d1..fd7e8984 100644 --- a/spec/components/custom_wizard/submission_spec.rb +++ b/spec/components/custom_wizard/submission_spec.rb @@ -24,7 +24,7 @@ describe CustomWizard::Submission do @count = CustomWizard::Submission::PAGE_LIMIT + 20 @count.times do |index| - described_class.new(@wizard, step_1_field_1: "I am user submission #{index+1}").save + described_class.new(@wizard, step_1_field_1: "I am user submission #{index + 1}").save end described_class.new(@wizard2, step_1_field_1: "I am another user's submission").save described_class.new(@wizard3, step_1_field_1: "I am a user submission on another wizard").save From 55b92f4256745a6be2e60c7940f31303c918186f Mon Sep 17 00:00:00 2001 From: angusmcleod Date: Wed, 21 Jul 2021 11:31:03 +0800 Subject: [PATCH 007/160] IMPROVE: Add additional data to submission serialiser --- lib/custom_wizard/wizard.rb | 4 ++- .../custom_wizard/submission_serializer.rb | 30 +++++++++++++++---- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/lib/custom_wizard/wizard.rb b/lib/custom_wizard/wizard.rb index e8427334..e52feec4 100644 --- a/lib/custom_wizard/wizard.rb +++ b/lib/custom_wizard/wizard.rb @@ -32,7 +32,8 @@ class CustomWizard::Wizard :actions, :action_ids, :user, - :submissions + :submissions, + :template attr_reader :all_step_ids @@ -79,6 +80,7 @@ class CustomWizard::Wizard @actions = attrs['actions'] || [] @action_ids = @actions.map { |a| a['id'] } + @template = attrs end def cast_bool(val) diff --git a/serializers/custom_wizard/submission_serializer.rb b/serializers/custom_wizard/submission_serializer.rb index 992deacb..f9cc7230 100644 --- a/serializers/custom_wizard/submission_serializer.rb +++ b/serializers/custom_wizard/submission_serializer.rb @@ -1,13 +1,33 @@ # frozen_string_literal: true class CustomWizard::SubmissionSerializer < ApplicationSerializer attributes :id, - :username, + :user, :fields, :submitted_at - def username - object.user.present? ? - object.user.username : - I18n.t('admin.wizard.submission.no_user', user_id: object.user_id) + has_one :user, serializer: ::BasicUserSerializer, embed: :objects + + def include_user? + object.user.present? + end + + def fields + @fields ||= begin + result = {} + + object.wizard.template['steps'].each do |step| + step['fields'].each do |field| + if value = object.fields[field['id']] + result[field['id']] = { + value: value, + type: field['type'], + label: field['label'] + } + end + end + end + + result + end end end From 51038ade8a1caefe663b4c9a8cf63ee9f7684beb Mon Sep 17 00:00:00 2001 From: Keegan George Date: Mon, 26 Jul 2021 10:44:08 -0700 Subject: [PATCH 008/160] FIX: Update help text in wizard message bar --- config/locales/client.en.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index e7c10b52..ce253455 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -93,7 +93,7 @@ en: destroy_complete: Destruction complete submissions: select: "Select a wizard to see its submissions" - viewing: "You're viewing the logs of the %{wizardName}. Click 'Download' on the right to download them." + viewing: "You're viewing the submissions of the %{wizardName}. Click 'Download' on the right to download them." documentation: "Check out the submissions documentation" logs: viewing: "View recent logs for wizards on the forum" From ae271ce647fcfb321000755aa91b98a30a0ffe89 Mon Sep 17 00:00:00 2001 From: Keegan George Date: Mon, 9 Aug 2021 14:44:44 -0700 Subject: [PATCH 009/160] UX: Add support button to admin-nav (#118) * Add support button to admin-nav * FIX: Security vulnerabilities with _blank anchor link * Update pro support url * UX: Create pro button custom styling * UX: Merge support button focus styling with hover * DEV: Move pro support url to setting * UX: Change support button name to Pro Support * DEV: Format stylesheet code * DEV: Use variables and change selector specificity for pro button * DEV: Hardcode pro-support url in button * DEV: Remove support url localization * DEV: Undo formatting fixes and add pro support button strings * DEV: Undo formatting fixes auto applied * DEV: Add space between selectors * DEV: Convert scss variables to CSS Custom properties * DEV: Fix linting * FIX: Use SCSS variables for color manipulation functions * DEV: Fix space before i18n * DEV: Add new line at end of file * DEV: Add new line at end of file * DEV: Remove name attribute in localizations * DEV: Remove padding from new line --- .../discourse/templates/admin-wizards.hbs | 4 ++++ assets/stylesheets/common/wizard-admin.scss | 20 +++++++++++++++++++ .../stylesheets/common/wizard-variables.scss | 7 +++++++ config/locales/client.en.yml | 3 +++ plugin.rb | 1 + 5 files changed, 35 insertions(+) create mode 100644 assets/stylesheets/common/wizard-variables.scss diff --git a/assets/javascripts/discourse/templates/admin-wizards.hbs b/assets/javascripts/discourse/templates/admin-wizards.hbs index bd575aae..5baa9f52 100644 --- a/assets/javascripts/discourse/templates/admin-wizards.hbs +++ b/assets/javascripts/discourse/templates/admin-wizards.hbs @@ -7,6 +7,10 @@ {{/if}} {{nav-item route="adminWizardsLogs" label="admin.wizard.log.nav_label"}} {{nav-item route="adminWizardsManager" label="admin.wizard.manager.nav_label"}} + + {{/admin-nav}}
diff --git a/assets/stylesheets/common/wizard-admin.scss b/assets/stylesheets/common/wizard-admin.scss index 66cc6b43..3c4f9ccf 100644 --- a/assets/stylesheets/common/wizard-admin.scss +++ b/assets/stylesheets/common/wizard-admin.scss @@ -2,6 +2,7 @@ @import "wizard-manager"; @import "wizard-api"; @import "common/components/buttons"; +@import "wizard-variables"; .admin-wizard-controls { display: flex; @@ -715,3 +716,22 @@ width: 80px; vertical-align: middle; } + +.btn.btn-pavilion-pro { + background: var(--pavilion-primary); + color: var(--pavilion-secondary); + + .d-icon { + color: var(--pavilion-secondary); + } + + &:hover, + &:focus { + background: darken($pavilionPrimary, 5%); + + &[href], + svg.d-icon { + color: darken($pavilionSecondary, 10%); + } + } +} diff --git a/assets/stylesheets/common/wizard-variables.scss b/assets/stylesheets/common/wizard-variables.scss new file mode 100644 index 00000000..68f02b6b --- /dev/null +++ b/assets/stylesheets/common/wizard-variables.scss @@ -0,0 +1,7 @@ +$pavilionPrimary: #3c1c8c; +$pavilionSecondary: #ffffff; + +:root { + --pavilion-primary: #3c1c8c; + --pavilion-secondary: #ffffff; +} diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index ef826cab..16cbe883 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -58,6 +58,9 @@ en: select_type: "Select a type" condition: "Condition" index: "Index" + pro_support_button: + title: "Request Pro Support" + label: "Pro Support" message: wizard: diff --git a/plugin.rb b/plugin.rb index 449d0237..615ad81e 100644 --- a/plugin.rb +++ b/plugin.rb @@ -33,6 +33,7 @@ if respond_to?(:register_svg_icon) register_svg_icon "chevron-right" register_svg_icon "chevron-left" register_svg_icon "save" + register_svg_icon "far-life-ring" end class ::Sprockets::DirectiveProcessor From a27c222dc66bc16e2efb996ef2d8b39ced3b8b3e Mon Sep 17 00:00:00 2001 From: angusmcleod Date: Tue, 10 Aug 2021 14:45:23 +0800 Subject: [PATCH 010/160] Update authentication and subscription handling --- .../components/wizard-message.js.es6 | 1 + .../components/wizard-pro-subscription.js.es6 | 47 ++++++ .../controllers/admin-wizards-pro.js.es6 | 56 +++++++ .../custom-wizard-admin-route-map.js.es6 | 10 +- .../discourse/models/custom-wizard-pro.js.es6 | 34 ++++ .../discourse/models/custom-wizard.js.es6 | 2 +- .../discourse/routes/admin-wizards-pro.js.es6 | 20 +++ .../discourse/templates/admin-wizards-pro.hbs | 31 ++++ .../discourse/templates/admin-wizards.hbs | 1 + .../components/wizard-pro-subscription.hbs | 30 ++++ assets/stylesheets/common/wizard-admin.scss | 60 ++++++- config/locales/client.en.yml | 31 +++- config/routes.rb | 6 + controllers/custom_wizard/admin/pro.rb | 46 ++++++ .../{ => regular}/refresh_api_access_token.rb | 0 jobs/{ => regular}/set_after_time_wizard.rb | 0 jobs/scheduled/update_pro_status.rb | 11 ++ lib/custom_wizard/pro.rb | 21 +++ lib/custom_wizard/pro/authentication.rb | 155 ++++++++++++++++++ lib/custom_wizard/pro/subscription.rb | 74 +++++++++ plugin.rb | 12 +- .../pro/authentication_serializer.rb | 11 ++ .../pro/subscription_serializer.rb | 10 ++ serializers/custom_wizard/pro_serializer.rb | 26 +++ 24 files changed, 687 insertions(+), 8 deletions(-) create mode 100644 assets/javascripts/discourse/components/wizard-pro-subscription.js.es6 create mode 100644 assets/javascripts/discourse/controllers/admin-wizards-pro.js.es6 create mode 100644 assets/javascripts/discourse/models/custom-wizard-pro.js.es6 create mode 100644 assets/javascripts/discourse/routes/admin-wizards-pro.js.es6 create mode 100644 assets/javascripts/discourse/templates/admin-wizards-pro.hbs create mode 100644 assets/javascripts/discourse/templates/components/wizard-pro-subscription.hbs create mode 100644 controllers/custom_wizard/admin/pro.rb rename jobs/{ => regular}/refresh_api_access_token.rb (100%) rename jobs/{ => regular}/set_after_time_wizard.rb (100%) create mode 100644 jobs/scheduled/update_pro_status.rb create mode 100644 lib/custom_wizard/pro.rb create mode 100644 lib/custom_wizard/pro/authentication.rb create mode 100644 lib/custom_wizard/pro/subscription.rb create mode 100644 serializers/custom_wizard/pro/authentication_serializer.rb create mode 100644 serializers/custom_wizard/pro/subscription_serializer.rb create mode 100644 serializers/custom_wizard/pro_serializer.rb diff --git a/assets/javascripts/discourse/components/wizard-message.js.es6 b/assets/javascripts/discourse/components/wizard-message.js.es6 index b273e78b..686a7254 100644 --- a/assets/javascripts/discourse/components/wizard-message.js.es6 +++ b/assets/javascripts/discourse/components/wizard-message.js.es6 @@ -6,6 +6,7 @@ import I18n from "I18n"; const icons = { error: "times-circle", success: "check-circle", + warn: "exclamation-circle", info: "info-circle", }; diff --git a/assets/javascripts/discourse/components/wizard-pro-subscription.js.es6 b/assets/javascripts/discourse/components/wizard-pro-subscription.js.es6 new file mode 100644 index 00000000..8ea56699 --- /dev/null +++ b/assets/javascripts/discourse/components/wizard-pro-subscription.js.es6 @@ -0,0 +1,47 @@ +import Component from "@ember/component"; +import CustomWizardPro from "../models/custom-wizard-pro"; +import { notEmpty } from "@ember/object/computed"; +import discourseComputed from "discourse-common/utils/decorators"; + +export default Component.extend({ + classNameBindings: [':custom-wizard-pro-subscription', 'subscription.active:active:inactive'], + subscribed: notEmpty('subscription'), + + @discourseComputed('subscription.type') + title(type) { + return type ? + I18n.t(`admin.wizard.pro.subscription.title.${type}`) : + I18n.t("admin.wizard.pro.not_subscribed"); + }, + + @discourseComputed('subscription.active') + stateClass(active) { + return active ? 'active' : 'inactive'; + }, + + @discourseComputed('stateClass') + stateLabel(stateClass) { + return I18n.t(`admin.wizard.pro.subscription.status.${stateClass}`); + }, + + actions: { + update() { + this.set('updating', true); + CustomWizardPro.update_subscription().then(result => { + if (result.success) { + this.setProperties({ + updateIcon: 'check', + subscription: result.subscription + }); + } else { + this.set('updateIcon', 'times'); + } + }).finally(() => { + this.set('updating', false); + setTimeout(() => { + this.set('updateIcon', null); + }, 7000); + }) + } + } +}); \ No newline at end of file diff --git a/assets/javascripts/discourse/controllers/admin-wizards-pro.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards-pro.js.es6 new file mode 100644 index 00000000..7c873c66 --- /dev/null +++ b/assets/javascripts/discourse/controllers/admin-wizards-pro.js.es6 @@ -0,0 +1,56 @@ +import Controller from "@ember/controller"; +import discourseComputed from "discourse-common/utils/decorators"; +import CustomWizardPro from "../models/custom-wizard-pro"; +import { alias } from "@ember/object/computed"; + +export default Controller.extend({ + messageUrl: "https://thepavilion.io/t/3652", + messageType: 'info', + messageKey: null, + showSubscription: alias('model.authentication.active'), + + setup() { + const authentication = this.get('model.authentication'); + const subscription = this.get('model.subscription'); + const subscribed = subscription && subscription.active; + const authenticated = authentication && authentication.active; + + if (!subscribed) { + this.set('messageKey', authenticated ? 'not_subscribed' : 'authorize'); + } else { + this.set('messageKey', !authenticated ? + 'subscription_expiring' : + subscribed ? 'subscription_active' : 'subscription_inactive' + ); + } + }, + + @discourseComputed('model.server') + messageOpts(server) { + return { server }; + }, + + actions: { + unauthorize() { + this.set('unauthorizing', true); + + CustomWizardPro.unauthorize().then(result => { + if (result.success) { + this.setProperties({ + messageKey: 'unauthorized', + messageType: 'warn', + "model.authentication": null, + "model.subscription": null + }); + } else { + this.setProperties({ + messageKey: 'unauthorize_failed', + messageType: 'error' + }); + } + }).finally(() => { + this.set('unauthorizing', false); + }) + } + } +}); diff --git a/assets/javascripts/discourse/custom-wizard-admin-route-map.js.es6 b/assets/javascripts/discourse/custom-wizard-admin-route-map.js.es6 index 90ab5359..ec2f1b98 100644 --- a/assets/javascripts/discourse/custom-wizard-admin-route-map.js.es6 +++ b/assets/javascripts/discourse/custom-wizard-admin-route-map.js.es6 @@ -43,12 +43,20 @@ export default { } ); - this.route("adminWizardsLogs", { path: "/logs", resetNamespace: true }); + this.route("adminWizardsLogs", { + path: "/logs", + resetNamespace: true + }); this.route("adminWizardsManager", { path: "/manager", resetNamespace: true, }); + + this.route("adminWizardsPro", { + path: "/pro", + resetNamespace: true, + }); } ); }, diff --git a/assets/javascripts/discourse/models/custom-wizard-pro.js.es6 b/assets/javascripts/discourse/models/custom-wizard-pro.js.es6 new file mode 100644 index 00000000..66d80572 --- /dev/null +++ b/assets/javascripts/discourse/models/custom-wizard-pro.js.es6 @@ -0,0 +1,34 @@ +import { ajax } from "discourse/lib/ajax"; +import { popupAjaxError } from "discourse/lib/ajax-error"; +import EmberObject from "@ember/object"; +import DiscourseURL from "discourse/lib/url"; + +const CustomWizardPro = EmberObject.extend(); + +const basePath = "/admin/wizards/pro"; + +CustomWizardPro.reopenClass({ + status() { + return ajax(basePath, { + type: "GET", + }).catch(popupAjaxError); + }, + + authorize() { + window.location.href = `${basePath}/authorize`; + }, + + unauthorize() { + return ajax(`${basePath}/authorize`, { + type: "DELETE", + }).catch(popupAjaxError); + }, + + update_subscription() { + return ajax(`${basePath}/subscription`, { + type: "POST", + }).catch(popupAjaxError); + } +}); + +export default CustomWizardPro; \ No newline at end of file diff --git a/assets/javascripts/discourse/models/custom-wizard.js.es6 b/assets/javascripts/discourse/models/custom-wizard.js.es6 index e6a8408d..80c4d86a 100644 --- a/assets/javascripts/discourse/models/custom-wizard.js.es6 +++ b/assets/javascripts/discourse/models/custom-wizard.js.es6 @@ -221,7 +221,7 @@ CustomWizard.reopenClass({ const wizard = this._super.apply(this); wizard.setProperties(buildProperties(wizardJson)); return wizard; - }, + } }); export default CustomWizard; diff --git a/assets/javascripts/discourse/routes/admin-wizards-pro.js.es6 b/assets/javascripts/discourse/routes/admin-wizards-pro.js.es6 new file mode 100644 index 00000000..b6fdcb10 --- /dev/null +++ b/assets/javascripts/discourse/routes/admin-wizards-pro.js.es6 @@ -0,0 +1,20 @@ +import CustomWizardPro from "../models/custom-wizard-pro"; +import DiscourseRoute from "discourse/routes/discourse"; + +export default DiscourseRoute.extend({ + model() { + return CustomWizardPro.status(); + }, + + setupController(controller, model) { + console.log(model) + controller.set('model', model); + controller.setup(); + }, + + actions: { + authorize() { + CustomWizardPro.authorize(); + } + } +}); diff --git a/assets/javascripts/discourse/templates/admin-wizards-pro.hbs b/assets/javascripts/discourse/templates/admin-wizards-pro.hbs new file mode 100644 index 00000000..67a48a8f --- /dev/null +++ b/assets/javascripts/discourse/templates/admin-wizards-pro.hbs @@ -0,0 +1,31 @@ +
+

{{i18n "admin.wizard.pro.title"}}

+ +
+ {{#if model.authentication.active}} + {{conditional-loading-spinner size="small" condition=unauthorizing}} + + {{i18n "admin.wizard.pro.unauthorize"}} + + + {{else}} + {{d-button + icon="id-card" + label="admin.wizard.pro.authorize" + action=(route-action "authorize")}} + {{/if}} +
+
+ +{{wizard-message + key=messageKey + url=messageUrl + type=messageType + opts=messageOpts + component="pro"}} + +
+ {{#if showSubscription}} + {{wizard-pro-subscription subscription=model.subscription}} + {{/if}} +
diff --git a/assets/javascripts/discourse/templates/admin-wizards.hbs b/assets/javascripts/discourse/templates/admin-wizards.hbs index bd575aae..a2e104f7 100644 --- a/assets/javascripts/discourse/templates/admin-wizards.hbs +++ b/assets/javascripts/discourse/templates/admin-wizards.hbs @@ -7,6 +7,7 @@ {{/if}} {{nav-item route="adminWizardsLogs" label="admin.wizard.log.nav_label"}} {{nav-item route="adminWizardsManager" label="admin.wizard.manager.nav_label"}} + {{nav-item route="adminWizardsPro" label="admin.wizard.pro.nav_label"}} {{/admin-nav}}
diff --git a/assets/javascripts/discourse/templates/components/wizard-pro-subscription.hbs b/assets/javascripts/discourse/templates/components/wizard-pro-subscription.hbs new file mode 100644 index 00000000..3d360220 --- /dev/null +++ b/assets/javascripts/discourse/templates/components/wizard-pro-subscription.hbs @@ -0,0 +1,30 @@ +
+

{{title}}

+ +
+ + {{#if updating}} + {{loading-spinner size="small"}} + {{else if updateIcon}} + {{d-icon updateIcon}} + {{/if}} + + {{d-button + icon="sync" + action=(action "update") + disabled=updating + label="admin.wizard.pro.subscription.update"}} +
+
+ +{{#if subscribed}} +
+
{{stateLabel}}
+
+ {{{i18n + 'admin.wizard.pro.subscription.last_updated' + updated_at=(format-date subscription.updated_at leaveAgo="true") + }}} +
+
+{{/if}} \ No newline at end of file diff --git a/assets/stylesheets/common/wizard-admin.scss b/assets/stylesheets/common/wizard-admin.scss index 66cc6b43..b887dace 100644 --- a/assets/stylesheets/common/wizard-admin.scss +++ b/assets/stylesheets/common/wizard-admin.scss @@ -7,7 +7,7 @@ display: flex; align-items: center; justify-content: space-between; - margin-bottom: 20px; + margin-bottom: 10px; & + .wizard-message + div { margin-top: 20px; @@ -715,3 +715,61 @@ width: 80px; vertical-align: middle; } + +.admin-wizards-pro { + .admin-wizard-controls { + h3, label { + margin: 0; + } + + label { + padding: .4em .5em; + margin-left: .75em; + background-color: $success; + color: $secondary; + } + + .buttons { + display: flex; + align-items: center; + + .loading-container { + margin-right: 1em; + } + } + } + + .custom-wizard-pro-subscription { + .title-container { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: .5em; + + h3 { + margin: 0; + } + + .buttons > span { + margin-right: .5em; + } + } + + .detail-container { + display: flex; + align-items: center; + padding: 1em; + background-color: $primary-very-low; + + .subscription-state { + padding: .25em .5em; + margin-right: .75em; + + &.active { + background-color: $success; + color: $secondary; + } + } + } + } +} diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index ef826cab..e6a0e849 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -86,12 +86,20 @@ en: no_file: Please choose a file to import file_size_error: The file size must be 512kb or less file_format_error: The file must be a .json file - server_error: "Error: {{message}}" importing: Importing wizards... destroying: Destroying wizards... import_complete: Import complete destroy_complete: Destruction complete - + pro: + documentation: Check out the PRO documentation + authorize: "Authorize this forum to use your PRO subscription plan on %{server}." + not_subscribed: "You've authorized, but are not currently subscribed to a PRO plan on %{server}." + subscription_expiring: "Your subscription is active, but will expire in the next 48 hours." + subscription_active: "Your subscription is active." + subscription_inactive: "Your subscription is inactive on this forum. Read more in the documentation." + unauthorized: "You're unauthorized. If you have a subscription, it will become inactive in the next 48 hours." + unauthorize_failed: Failed to unauthorize. + editor: show: "Show" hide: "Hide" @@ -424,7 +432,24 @@ en: imported: imported upload: Select wizards.json destroy: Destroy - destroyed: destroyed + destroyed: destroyed + + pro: + nav_label: PRO + title: Custom Wizard PRO + authorize: Authorize + authorized: Authorized + unauthorize: cancel + not_subscribed: You're not currently subscribed + subscription: + title: + community: Community Subscription + business: Business Subscription + status: + active: Active + inactive: Inactive + update: Update + last_updated: Last updated {{updated_at}} wizard_js: group: diff --git a/config/routes.rb b/config/routes.rb index 28fcbb82..94cc5858 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -43,5 +43,11 @@ Discourse::Application.routes.append do get 'admin/wizards/manager/export' => 'admin_manager#export' post 'admin/wizards/manager/import' => 'admin_manager#import' delete 'admin/wizards/manager/destroy' => 'admin_manager#destroy' + + get 'admin/wizards/pro' => 'admin_pro#index' + get 'admin/wizards/pro/authorize' => 'admin_pro#authorize' + get 'admin/wizards/pro/authorize/callback' => 'admin_pro#authorize_callback' + delete 'admin/wizards/pro/authorize' => 'admin_pro#destroy' + post 'admin/wizards/pro/subscription' => 'admin_pro#update_subscription' end end diff --git a/controllers/custom_wizard/admin/pro.rb b/controllers/custom_wizard/admin/pro.rb new file mode 100644 index 00000000..b0686af2 --- /dev/null +++ b/controllers/custom_wizard/admin/pro.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +class CustomWizard::AdminProController < CustomWizard::AdminController + skip_before_action :check_xhr, :preload_json, :verify_authenticity_token, only: [:authorize, :authorize_callback] + + def index + render_serialized(CustomWizard::Pro.new, CustomWizard::ProSerializer, root: false) + end + + def authorize + request_id = SecureRandom.hex(32) + cookies[:user_api_request_id] = request_id + redirect_to CustomWizard::ProAuthentication.generate_request(current_user.id, request_id).to_s + end + + def authorize_callback + payload = params[:payload] + request_id = cookies[:user_api_request_id] + + CustomWizard::ProAuthentication.handle_response(request_id, payload) + CustomWizard::ProSubscription.update + + redirect_to '/admin/wizards/pro' + end + + def destroy + if CustomWizard::ProAuthentication.destroy + render json: success_json + else + render json: failed_json + end + end + + def update_subscription + if CustomWizard::ProSubscription.update + render json: success_json.merge( + subscription: CustomWizard::ProSubscriptionSerializer.new( + CustomWizard::ProSubscription.new, + root: false + ) + ) + else + render json: failed_json + end + end +end \ No newline at end of file diff --git a/jobs/refresh_api_access_token.rb b/jobs/regular/refresh_api_access_token.rb similarity index 100% rename from jobs/refresh_api_access_token.rb rename to jobs/regular/refresh_api_access_token.rb diff --git a/jobs/set_after_time_wizard.rb b/jobs/regular/set_after_time_wizard.rb similarity index 100% rename from jobs/set_after_time_wizard.rb rename to jobs/regular/set_after_time_wizard.rb diff --git a/jobs/scheduled/update_pro_status.rb b/jobs/scheduled/update_pro_status.rb new file mode 100644 index 00000000..962f3ba9 --- /dev/null +++ b/jobs/scheduled/update_pro_status.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +module Jobs + class UpdateProSubscription < ::Jobs::Scheduled + every 1.days + + def execute(args) + CustomWizard::ProSubscription.update + end + end +end \ No newline at end of file diff --git a/lib/custom_wizard/pro.rb b/lib/custom_wizard/pro.rb new file mode 100644 index 00000000..c280c09d --- /dev/null +++ b/lib/custom_wizard/pro.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +class CustomWizard::Pro + NAMESPACE ||= "#{CustomWizard::PLUGIN_NAME}_pro" + + attr_reader :authentication, + :subscription + + def initialize + @authentication = CustomWizard::ProAuthentication.new + @subscription = CustomWizard::ProSubscription.new + end + + def authorized? + @authentication.active? + end + + def subscribed? + @subscription.active? + end +end \ No newline at end of file diff --git a/lib/custom_wizard/pro/authentication.rb b/lib/custom_wizard/pro/authentication.rb new file mode 100644 index 00000000..c92710bc --- /dev/null +++ b/lib/custom_wizard/pro/authentication.rb @@ -0,0 +1,155 @@ +class CustomWizard::ProAuthentication + include ActiveModel::Serialization + + API_KEY ||= "api_key" + API_CLIENT_ID ||= 'api_client_id' + KEYS ||= "keys" + + attr_reader :client_id, + :auth_by, + :auth_at, + :api_key + + def initialize + api = get_api_key + + @api_key = api.key + @auth_at = api.auth_at + @auth_by = api.auth_by + @client_id = get_client_id || set_client_id + end + + def active? + @api_key.present? + end + + def update(data) + api_key = data[:key] + user_id = data[:user_id] + user = User.find(user_id) + + if user&.admin + set_api_key(api_key, user.id) + else + false + end + end + + def destroy + remove + end + + def self.destroy + self.new.destroy + end + + def generate_keys(user_id, request_id) + rsa = OpenSSL::PKey::RSA.generate(2048) + nonce = SecureRandom.hex(32) + set_keys(request_id, user_id, rsa, nonce) + + OpenStruct.new(nonce: nonce, public_key: rsa.public_key) + end + + def decrypt_payload(request_id, payload) + keys = get_keys(request_id) + return false unless keys.present? && keys.pem + delete_keys(request_id) + + rsa = OpenSSL::PKey::RSA.new(keys.pem) + decrypted_payload = rsa.private_decrypt(Base64.decode64(payload)) + return false unless decrypted_payload.present? + + begin + data = JSON.parse(decrypted_payload).symbolize_keys + rescue JSON::ParserError + return false + end + + return false unless data[:nonce] == keys.nonce + data[:user_id] = keys.user_id + + data + end + + def self.generate_request(user_id, request_id) + authentication = self.new + keys = authentication.generate_keys(user_id, request_id) + + params = { + public_key: keys.public_key, + nonce: keys.nonce, + client_id: authentication.client_id, + auth_redirect: "#{Discourse.base_url}/admin/wizards/pro/authorize/callback", + application_name: SiteSetting.title, + scopes: CustomWizard::ProSubscription::SCOPE + } + + uri = URI.parse("https://#{CustomWizard::ProSubscription::SUBSCRIPTION_SERVER}/user-api-key/new") + uri.query = URI.encode_www_form(params) + uri.to_s + end + + def self.handle_response(request_id, payload) + authentication = self.new + + data = authentication.decrypt_payload(request_id, payload) + return unless data.is_a?(Hash) && data[:key] && data[:user_id] + + authentication.update(data) + end + + private + + def get_api_key + raw = PluginStore.get(CustomWizard::Pro::NAMESPACE, API_KEY) + OpenStruct.new( + key: raw && raw['key'], + auth_by: raw && raw['auth_by'], + auth_at: raw && raw['auth_at'] + ) + end + + def set_api_key(key, user_id) + PluginStore.set(CustomWizard::Pro::NAMESPACE, API_KEY, + key: key, + auth_by: user_id, + auth_at: Time.now + ) + end + + def remove + PluginStore.remove(CustomWizard::Pro::NAMESPACE, API_KEY) + end + + def get_client_id + PluginStore.get(CustomWizard::Pro::NAMESPACE, API_CLIENT_ID) + end + + def set_client_id + client_id = SecureRandom.hex(32) + PluginStore.set(CustomWizard::Pro::NAMESPACE, API_CLIENT_ID, client_id) + client_id + end + + def set_keys(request_id, user_id, rsa, nonce) + PluginStore.set(CustomWizard::Pro::NAMESPACE, "#{KEYS}_#{request_id}", + user_id: user_id, + pem: rsa.export, + nonce: nonce + ) + end + + def get_keys(request_id) + raw = PluginStore.get(CustomWizard::Pro::NAMESPACE, "#{KEYS}_#{request_id}") + OpenStruct.new( + user_id: raw && raw['user_id'], + pem: raw && raw['pem'], + nonce: raw && raw['nonce'] + ) + end + + def delete_keys(request_id) + PluginStore.remove(CustomWizard::Pro::NAMESPACE, "#{KEYS}_#{request_id}") + end +end \ No newline at end of file diff --git a/lib/custom_wizard/pro/subscription.rb b/lib/custom_wizard/pro/subscription.rb new file mode 100644 index 00000000..2e0c8542 --- /dev/null +++ b/lib/custom_wizard/pro/subscription.rb @@ -0,0 +1,74 @@ +class CustomWizard::ProSubscription + include ActiveModel::Serialization + + SUBSCRIPTION_SERVER ||= "test.thepavilion.io" + SUBSCRIPTION_TYPE ||= "stripe" + SCOPE ||= "discourse-subscription-server:user_subscription" + CLIENT_NAME ||= "custom-wizard" + SUBSCRIPTION_KEY ||= "custom_wizard_pro_subscription" + UPDATE_DAY_BUFFER ||= 2 + TYPES ||= %w(community business) + + attr_reader :type, + :updated_at + + def initialize + raw = get + + if raw + @type = raw['type'] + @updated_at = raw['updated_at'] + end + end + + def active? + TYPES.include?(type) && updated_at.to_datetime > (Date.today - UPDATE_DAY_BUFFER.days).to_datetime + end + + def update(data) + return false unless data && data.is_a?(Hash) + subscriptions = data[:subscriptions] + + if subscriptions.present? + subscription = subscriptions.first + type = subscription[:price_nickname] + + set(type) + end + end + + def self.update + @subscribed = nil + auth = CustomWizard::ProAuthentication.new + subscription = self.new + + if auth.active? + response = Excon.get( + "https://#{SUBSCRIPTION_SERVER}/subscription-server/user-subscriptions/#{SUBSCRIPTION_TYPE}/#{CLIENT_NAME}", + headers: { "User-Api-Key" => auth.api_key } + ) + + if response.status == 200 + begin + data = JSON.parse(response.body).deep_symbolize_keys + rescue JSON::ParserError + return false + end + + return subscription.update(data) + end + end + + false + end + + private + + def set(type) + PluginStore.set(CustomWizard::Pro::NAMESPACE, SUBSCRIPTION_KEY, type: type, updated_at: Time.now) + end + + def get + PluginStore.get(CustomWizard::Pro::NAMESPACE, SUBSCRIPTION_KEY) + end +end \ No newline at end of file diff --git a/plugin.rb b/plugin.rb index 449d0237..97ed8e5a 100644 --- a/plugin.rb +++ b/plugin.rb @@ -62,11 +62,13 @@ after_initialize do ../controllers/custom_wizard/admin/logs.rb ../controllers/custom_wizard/admin/manager.rb ../controllers/custom_wizard/admin/custom_fields.rb + ../controllers/custom_wizard/admin/pro.rb ../controllers/custom_wizard/wizard.rb ../controllers/custom_wizard/steps.rb ../controllers/custom_wizard/realtime_validations.rb - ../jobs/refresh_api_access_token.rb - ../jobs/set_after_time_wizard.rb + ../jobs/regular/refresh_api_access_token.rb + ../jobs/regular/set_after_time_wizard.rb + ../jobs/scheduled/update_pro_status.rb ../lib/custom_wizard/validators/template.rb ../lib/custom_wizard/validators/update.rb ../lib/custom_wizard/action_result.rb @@ -85,6 +87,9 @@ after_initialize do ../lib/custom_wizard/submission.rb ../lib/custom_wizard/template.rb ../lib/custom_wizard/wizard.rb + ../lib/custom_wizard/pro.rb + ../lib/custom_wizard/pro/subscription.rb + ../lib/custom_wizard/pro/authentication.rb ../lib/custom_wizard/api/api.rb ../lib/custom_wizard/api/authorization.rb ../lib/custom_wizard/api/endpoint.rb @@ -105,6 +110,9 @@ after_initialize do ../serializers/custom_wizard/log_serializer.rb ../serializers/custom_wizard/submission_serializer.rb ../serializers/custom_wizard/realtime_validation/similar_topics_serializer.rb + ../serializers/custom_wizard/pro_serializer.rb + ../serializers/custom_wizard/pro/authentication_serializer.rb + ../serializers/custom_wizard/pro/subscription_serializer.rb ../extensions/extra_locales_controller.rb ../extensions/invites_controller.rb ../extensions/users_controller.rb diff --git a/serializers/custom_wizard/pro/authentication_serializer.rb b/serializers/custom_wizard/pro/authentication_serializer.rb new file mode 100644 index 00000000..b54f428f --- /dev/null +++ b/serializers/custom_wizard/pro/authentication_serializer.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true +class CustomWizard::ProAuthenticationSerializer < ApplicationSerializer + attributes :active, + :client_id, + :auth_by, + :auth_at + + def active + object.active? + end +end \ No newline at end of file diff --git a/serializers/custom_wizard/pro/subscription_serializer.rb b/serializers/custom_wizard/pro/subscription_serializer.rb new file mode 100644 index 00000000..6be5ec6f --- /dev/null +++ b/serializers/custom_wizard/pro/subscription_serializer.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true +class CustomWizard::ProSubscriptionSerializer < ApplicationSerializer + attributes :type, + :active, + :updated_at + + def active + object.active? + end +end \ No newline at end of file diff --git a/serializers/custom_wizard/pro_serializer.rb b/serializers/custom_wizard/pro_serializer.rb new file mode 100644 index 00000000..5b351f29 --- /dev/null +++ b/serializers/custom_wizard/pro_serializer.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true +class CustomWizard::ProSerializer < ApplicationSerializer + attributes :server, + :authentication, + :subscription + + def server + CustomWizard::ProSubscription::SUBSCRIPTION_SERVER + end + + def authentication + if object.authentication + CustomWizard::ProAuthenticationSerializer.new(object.authentication, root: false) + else + nil + end + end + + def subscription + if object.subscription + CustomWizard::ProSubscriptionSerializer.new(object.subscription, root: false) + else + nil + end + end +end \ No newline at end of file From a7904a28af923b1c4b2f369b64289c97793404d0 Mon Sep 17 00:00:00 2001 From: angusmcleod Date: Tue, 10 Aug 2021 15:32:21 +0800 Subject: [PATCH 011/160] Update pro admin title attributes --- .../discourse/routes/admin-wizards-pro.js.es6 | 1 - .../discourse/templates/admin-wizards-pro.hbs | 5 ++++- .../templates/components/wizard-pro-subscription.hbs | 10 ++++------ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/assets/javascripts/discourse/routes/admin-wizards-pro.js.es6 b/assets/javascripts/discourse/routes/admin-wizards-pro.js.es6 index b6fdcb10..2fa091c7 100644 --- a/assets/javascripts/discourse/routes/admin-wizards-pro.js.es6 +++ b/assets/javascripts/discourse/routes/admin-wizards-pro.js.es6 @@ -7,7 +7,6 @@ export default DiscourseRoute.extend({ }, setupController(controller, model) { - console.log(model) controller.set('model', model); controller.setup(); }, diff --git a/assets/javascripts/discourse/templates/admin-wizards-pro.hbs b/assets/javascripts/discourse/templates/admin-wizards-pro.hbs index 67a48a8f..a0658ccb 100644 --- a/assets/javascripts/discourse/templates/admin-wizards-pro.hbs +++ b/assets/javascripts/discourse/templates/admin-wizards-pro.hbs @@ -7,11 +7,14 @@ {{i18n "admin.wizard.pro.unauthorize"}} - + {{else}} {{d-button icon="id-card" label="admin.wizard.pro.authorize" + title="admin.wizard.pro.authorize" action=(route-action "authorize")}} {{/if}}
diff --git a/assets/javascripts/discourse/templates/components/wizard-pro-subscription.hbs b/assets/javascripts/discourse/templates/components/wizard-pro-subscription.hbs index 3d360220..72631bfd 100644 --- a/assets/javascripts/discourse/templates/components/wizard-pro-subscription.hbs +++ b/assets/javascripts/discourse/templates/components/wizard-pro-subscription.hbs @@ -13,18 +13,16 @@ icon="sync" action=(action "update") disabled=updating + title="admin.wizard.pro.subscription.update" label="admin.wizard.pro.subscription.update"}}
{{#if subscribed}}
-
{{stateLabel}}
-
- {{{i18n - 'admin.wizard.pro.subscription.last_updated' - updated_at=(format-date subscription.updated_at leaveAgo="true") - }}} +
{{stateLabel}}
+
+ {{{i18n 'admin.wizard.pro.subscription.last_updated' updated_at=(format-date subscription.updated_at leaveAgo="true")}}}
{{/if}} \ No newline at end of file From f49f5164033ec20124bb375c6e19fc280183b995 Mon Sep 17 00:00:00 2001 From: angusmcleod Date: Tue, 10 Aug 2021 17:00:42 +0800 Subject: [PATCH 012/160] Add pro restriction --- .../routes/admin-wizards-wizard-show.js.es6 | 1 + .../templates/admin-wizards-wizard-show.hbs | 3 +- .../components/wizard-custom-field.hbs | 21 +++++----- .../components/wizard-custom-step.hbs | 40 ++++++++++--------- assets/stylesheets/common/wizard-admin.scss | 16 ++++++++ config/locales/client.en.yml | 3 +- config/locales/server.en.yml | 1 + controllers/custom_wizard/admin/wizard.rb | 3 +- lib/custom_wizard/builder.rb | 3 ++ lib/custom_wizard/pro.rb | 4 ++ lib/custom_wizard/validators/template.rb | 19 +++++++++ 11 files changed, 84 insertions(+), 30 deletions(-) diff --git a/assets/javascripts/discourse/routes/admin-wizards-wizard-show.js.es6 b/assets/javascripts/discourse/routes/admin-wizards-wizard-show.js.es6 index cb2d54c3..f298fa1c 100644 --- a/assets/javascripts/discourse/routes/admin-wizards-wizard-show.js.es6 +++ b/assets/javascripts/discourse/routes/admin-wizards-wizard-show.js.es6 @@ -39,6 +39,7 @@ export default DiscourseRoute.extend({ currentStep: wizard.steps[0], currentAction: wizard.actions[0], creating: model.create, + proSubscribed: parentModel.pro_subscribed }; controller.setProperties(props); diff --git a/assets/javascripts/discourse/templates/admin-wizards-wizard-show.hbs b/assets/javascripts/discourse/templates/admin-wizards-wizard-show.hbs index 7e5b0ee0..9d91cb0b 100644 --- a/assets/javascripts/discourse/templates/admin-wizards-wizard-show.hbs +++ b/assets/javascripts/discourse/templates/admin-wizards-wizard-show.hbs @@ -166,7 +166,8 @@ wizard=wizard currentField=currentField wizardFields=wizardFields - fieldTypes=fieldTypes}} + fieldTypes=fieldTypes + proSubscribed=proSubscribed}} {{/if}} {{wizard-links diff --git a/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs b/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs index 3b63cef7..cf15abd3 100644 --- a/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs +++ b/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs @@ -213,17 +213,20 @@ {{#if field.showAdvanced}}
-
-
- -
+ {{#if proSubscribed}} +
+
+ + {{i18n "admin.wizard.pro.label"}} +
-
- {{wizard-mapper - inputs=field.condition - options=fieldConditionOptions}} +
+ {{wizard-mapper + inputs=field.condition + options=fieldConditionOptions}} +
-
+ {{/if}}
diff --git a/assets/javascripts/discourse/templates/components/wizard-custom-step.hbs b/assets/javascripts/discourse/templates/components/wizard-custom-step.hbs index 85adfe8a..3d8fad9d 100644 --- a/assets/javascripts/discourse/templates/components/wizard-custom-step.hbs +++ b/assets/javascripts/discourse/templates/components/wizard-custom-step.hbs @@ -38,26 +38,29 @@ {{#if step.showAdvanced}}
-
-
- + {{#if proSubscribed}} +
+
+ + {{i18n "admin.wizard.pro.label"}} +
+ +
+ {{wizard-mapper + inputs=step.condition + options=stepConditionOptions}} +
-
- {{wizard-mapper - inputs=step.condition - options=stepConditionOptions}} +
+
+
+

{{i18n "admin.wizard.step.force_final.label"}}

+ {{input type="checkbox" checked=step.force_final}} + {{i18n "admin.wizard.step.force_final.description"}} +
-
- -
-
-
-

{{i18n "admin.wizard.step.force_final.label"}}

- {{input type="checkbox" checked=step.force_final}} - {{i18n "admin.wizard.step.force_final.description"}} -
-
+ {{/if}}
@@ -129,5 +132,6 @@ currentFieldId=currentField.id fieldTypes=fieldTypes removeField="removeField" - wizardFields=wizardFields}} + wizardFields=wizardFields + proSubscribed=proSubscribed}} {{/each}} diff --git a/assets/stylesheets/common/wizard-admin.scss b/assets/stylesheets/common/wizard-admin.scss index b887dace..cca03c74 100644 --- a/assets/stylesheets/common/wizard-admin.scss +++ b/assets/stylesheets/common/wizard-admin.scss @@ -372,6 +372,22 @@ .setting-gutter { margin-top: 5px; } + + &.pro { + .setting-label { + display: flex; + flex-direction: column; + + label { + margin: 0; + } + } + + .pro-label { + color: $tertiary; + font-size: .75em; + } + } } .advanced-settings { diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index e6a0e849..4a2bf28f 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -58,7 +58,7 @@ en: select_type: "Select a type" condition: "Condition" index: "Index" - + message: wizard: select: "Select a wizard, or create a new one" @@ -436,6 +436,7 @@ en: pro: nav_label: PRO + label: PRO title: Custom Wizard PRO authorize: Authorize authorized: Authorized diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index 7e507450..c425baa5 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -49,6 +49,7 @@ en: required: "%{property} is required" conflict: "Wizard with id '%{wizard_id}' already exists" after_time: "After time setting is invalid" + pro: "%{property} is PRO only" site_settings: custom_wizard_enabled: "Enable custom wizards." diff --git a/controllers/custom_wizard/admin/wizard.rb b/controllers/custom_wizard/admin/wizard.rb index 8da64fac..e824398c 100644 --- a/controllers/custom_wizard/admin/wizard.rb +++ b/controllers/custom_wizard/admin/wizard.rb @@ -10,7 +10,8 @@ class CustomWizard::AdminWizardController < CustomWizard::AdminController ), field_types: CustomWizard::Field.types, realtime_validations: CustomWizard::RealtimeValidation.types, - custom_fields: custom_field_list + custom_fields: custom_field_list, + pro_subscribed: CustomWizard::Pro.subscribed? ) end diff --git a/lib/custom_wizard/builder.rb b/lib/custom_wizard/builder.rb index 51ca4183..a949653c 100644 --- a/lib/custom_wizard/builder.rb +++ b/lib/custom_wizard/builder.rb @@ -6,6 +6,7 @@ class CustomWizard::Builder @template = CustomWizard::Template.create(wizard_id) return nil if @template.nil? @wizard = CustomWizard::Wizard.new(template.data, user) + @pro = CustomWizard::Pro.new end def self.sorted_handlers @@ -222,6 +223,8 @@ class CustomWizard::Builder end def check_condition(template) + return false unless @pro.subscribed? + if template['condition'].present? result = CustomWizard::Mapper.new( inputs: template['condition'], diff --git a/lib/custom_wizard/pro.rb b/lib/custom_wizard/pro.rb index c280c09d..e6bf2b0b 100644 --- a/lib/custom_wizard/pro.rb +++ b/lib/custom_wizard/pro.rb @@ -18,4 +18,8 @@ class CustomWizard::Pro def subscribed? @subscription.active? end + + def self.subscribed? + self.new.subscribed? + end end \ No newline at end of file diff --git a/lib/custom_wizard/validators/template.rb b/lib/custom_wizard/validators/template.rb index c90944e9..deaa5119 100644 --- a/lib/custom_wizard/validators/template.rb +++ b/lib/custom_wizard/validators/template.rb @@ -6,6 +6,7 @@ class CustomWizard::TemplateValidator def initialize(data, opts = {}) @data = data @opts = opts + @pro = CustomWizard::Pro.new end def perform @@ -14,12 +15,15 @@ class CustomWizard::TemplateValidator check_id(data, :wizard) check_required(data, :wizard) validate_after_time + validate_pro(data, :wizard) data[:steps].each do |step| check_required(step, :step) + validate_pro(step, :step) if data[:fields].present? data[:fields].each do |field| + validate_pro(field, :field) check_required(field, :field) end end @@ -47,6 +51,13 @@ class CustomWizard::TemplateValidator } end + def self.pro + { + step: ['condition'], + field: ['conition'] + } + end + private def check_required(object, type) @@ -57,6 +68,14 @@ class CustomWizard::TemplateValidator end end + def validate_pro(object, type) + CustomWizard::TemplateValidator.required[type].each do |property| + if object[property].present? && !@pro.subscribed? + errors.add :base, I18n.t("wizard.validation.pro", property: property) + end + end + end + def check_id(object, type) if type === :wizard && @opts[:create] && CustomWizard::Template.exists?(object[:id]) errors.add :base, I18n.t("wizard.validation.conflict", wizard_id: object[:id]) From e81b773512de22e067cdfb0f40ab2a2d2574185e Mon Sep 17 00:00:00 2001 From: angusmcleod Date: Tue, 10 Aug 2021 17:18:49 +0800 Subject: [PATCH 013/160] Move pro conditions out of advanced section --- .../components/wizard-custom-field.hbs | 30 ++++++------ .../components/wizard-custom-step.hbs | 48 +++++++++---------- 2 files changed, 39 insertions(+), 39 deletions(-) diff --git a/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs b/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs index cf15abd3..9237cf62 100644 --- a/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs +++ b/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs @@ -207,27 +207,27 @@
{{/if}} +{{#if proSubscribed}} +
+
+ + {{i18n "admin.wizard.pro.label"}} +
+ +
+ {{wizard-mapper + inputs=field.condition + options=fieldConditionOptions}} +
+
+{{/if}} + {{#if showAdvanced}} {{wizard-advanced-toggle showAdvanced=field.showAdvanced}} {{#if field.showAdvanced}}
- {{#if proSubscribed}} -
-
- - {{i18n "admin.wizard.pro.label"}} -
- -
- {{wizard-mapper - inputs=field.condition - options=fieldConditionOptions}} -
-
- {{/if}} -
diff --git a/assets/javascripts/discourse/templates/components/wizard-custom-step.hbs b/assets/javascripts/discourse/templates/components/wizard-custom-step.hbs index 3d8fad9d..3081be66 100644 --- a/assets/javascripts/discourse/templates/components/wizard-custom-step.hbs +++ b/assets/javascripts/discourse/templates/components/wizard-custom-step.hbs @@ -33,35 +33,35 @@
+{{#if proSubscribed}} +
+
+ + {{i18n "admin.wizard.pro.label"}} +
+ +
+ {{wizard-mapper + inputs=step.condition + options=stepConditionOptions}} +
+
+ +
+
+
+

{{i18n "admin.wizard.step.force_final.label"}}

+ {{input type="checkbox" checked=step.force_final}} + {{i18n "admin.wizard.step.force_final.description"}} +
+
+{{/if}} + {{wizard-advanced-toggle showAdvanced=step.showAdvanced}} {{#if step.showAdvanced}}
- {{#if proSubscribed}} -
-
- - {{i18n "admin.wizard.pro.label"}} -
- -
- {{wizard-mapper - inputs=step.condition - options=stepConditionOptions}} -
-
- -
-
-
-

{{i18n "admin.wizard.step.force_final.label"}}

- {{input type="checkbox" checked=step.force_final}} - {{i18n "admin.wizard.step.force_final.description"}} -
-
- {{/if}} -
From eadc40bdae1f580c3b718e2e23685c47e769aef6 Mon Sep 17 00:00:00 2001 From: angusmcleod Date: Wed, 18 Aug 2021 14:11:00 +0800 Subject: [PATCH 014/160] Add CustomWizard class protections and pro feature restrictions --- config/locales/server.en.yml | 1 + jobs/scheduled/update_pro_status.rb | 10 ++++------ lib/custom_wizard/action.rb | 10 ++++++++++ lib/custom_wizard/builder.rb | 21 +++++++++++++-------- lib/custom_wizard/pro/subscription.rb | 5 +++-- plugin.rb | 12 ++++++++++++ 6 files changed, 43 insertions(+), 16 deletions(-) diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index c425baa5..86968fda 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -17,6 +17,7 @@ en: name_too_short: "'%{name}' is too short for a custom field name (min length is #{min_length})" name_already_taken: "'%{name}' is already taken as a custom field name" save_default: "Failed to save custom field '%{name}'" + pro_required: "PRO Actions require a PRO Subscription" field: too_short: "%{label} must be at least %{min} characters" diff --git a/jobs/scheduled/update_pro_status.rb b/jobs/scheduled/update_pro_status.rb index 962f3ba9..5f76f52b 100644 --- a/jobs/scheduled/update_pro_status.rb +++ b/jobs/scheduled/update_pro_status.rb @@ -1,11 +1,9 @@ # frozen_string_literal: true -module Jobs - class UpdateProSubscription < ::Jobs::Scheduled - every 1.days +class CustomWizard::UpdateProSubscription < ::Jobs::Scheduled + every 10.minutes - def execute(args) - CustomWizard::ProSubscription.update - end + def execute(args) + CustomWizard::ProSubscription.update end end \ No newline at end of file diff --git a/lib/custom_wizard/action.rb b/lib/custom_wizard/action.rb index 6a36af41..1cadbb5b 100644 --- a/lib/custom_wizard/action.rb +++ b/lib/custom_wizard/action.rb @@ -14,9 +14,15 @@ class CustomWizard::Action @submission = opts[:submission] @log = [] @result = CustomWizard::ActionResult.new + @pro = CustomWizard::Pro.new end def perform + if pro_actions.include?(action['type']) && !@pro.subscribed? + log_error(I18n.t("wizard.custom_field.error.pro_required")) + return + end + ActiveRecord::Base.transaction do self.send(action['type'].to_sym) end @@ -752,4 +758,8 @@ class CustomWizard::Action CustomWizard::Log.create(log) end + + def pro_actions + %w[send_message watch_categories send_to_api create_group create_category] + end end diff --git a/lib/custom_wizard/builder.rb b/lib/custom_wizard/builder.rb index 656a3820..78a960b9 100644 --- a/lib/custom_wizard/builder.rb +++ b/lib/custom_wizard/builder.rb @@ -22,13 +22,6 @@ class CustomWizard::Builder @sorted_handlers.sort_by! { |h| -h[:priority] } end - def mapper - CustomWizard::Mapper.new( - user: @wizard.user, - data: @wizard.current_submission&.fields_and_meta - ) - end - def build(build_opts = {}, params = {}) return nil if !SiteSetting.custom_wizard_enabled || !@wizard return @wizard if !@wizard.can_access? && !build_opts[:force] @@ -80,6 +73,15 @@ class CustomWizard::Builder @wizard end + private + + def mapper + CustomWizard::Mapper.new( + user: @wizard.user, + data: @wizard.current_submission&.fields_and_meta + ) + end + def append_field(step, step_template, field_template, build_opts) params = { id: field_template['id'], @@ -224,7 +226,10 @@ class CustomWizard::Builder end def check_condition(template) - return false unless @pro.subscribed? + unless @pro.subscribed? + CustomWizard::Log.create(I18n.t("wizard.custom_field.error.pro_required")) + return false + end if template['condition'].present? result = CustomWizard::Mapper.new( diff --git a/lib/custom_wizard/pro/subscription.rb b/lib/custom_wizard/pro/subscription.rb index 2e0c8542..ef0ac2b9 100644 --- a/lib/custom_wizard/pro/subscription.rb +++ b/lib/custom_wizard/pro/subscription.rb @@ -38,14 +38,15 @@ class CustomWizard::ProSubscription end def self.update - @subscribed = nil auth = CustomWizard::ProAuthentication.new subscription = self.new if auth.active? response = Excon.get( "https://#{SUBSCRIPTION_SERVER}/subscription-server/user-subscriptions/#{SUBSCRIPTION_TYPE}/#{CLIENT_NAME}", - headers: { "User-Api-Key" => auth.api_key } + headers: { + "User-Api-Key" => auth.api_key + } ) if response.status == 200 diff --git a/plugin.rb b/plugin.rb index c0379374..7c056b5d 100644 --- a/plugin.rb +++ b/plugin.rb @@ -126,6 +126,18 @@ after_initialize do Liquid::Template.register_filter(::CustomWizard::LiquidFilter::FirstNonEmpty) + class CustomWizard::UnpermittedOverride < StandardError; end + + CustomWizard.constants.each do |class_name| + klass = CustomWizard.const_get(class_name) + next if !klass.is_a?(Class) || klass.superclass.name.to_s.split("::").first == 'CustomWizard' + + klass.define_singleton_method(:prepend) { |klass| raise CustomWizard::UnpermittedOverride.new } + klass.define_singleton_method(:include) { |klass| raise CustomWizard::UnpermittedOverride.new } + klass.define_singleton_method(:define_method) { |name, &block| raise CustomWizard::UnpermittedOverride.new } + klass.define_singleton_method(:define_singleton_method) { |name, &block| raise CustomWizard::UnpermittedOverride.new } + end + add_class_method(:wizard, :user_requires_completion?) do |user| wizard_result = self.new(user).requires_completion? return wizard_result if wizard_result From a810155f9177e91c2679cc922b352bad88be30c9 Mon Sep 17 00:00:00 2001 From: angusmcleod Date: Wed, 18 Aug 2021 14:59:43 +0800 Subject: [PATCH 015/160] Clean up pro constants --- controllers/custom_wizard/admin/pro.rb | 8 +-- jobs/scheduled/update_pro_status.rb | 2 +- lib/custom_wizard/pro.rb | 85 ++++++++++++++++++++++++- lib/custom_wizard/pro/authentication.rb | 63 ++++++------------ lib/custom_wizard/pro/subscription.rb | 56 ++++++---------- 5 files changed, 127 insertions(+), 87 deletions(-) diff --git a/controllers/custom_wizard/admin/pro.rb b/controllers/custom_wizard/admin/pro.rb index b0686af2..9f32045e 100644 --- a/controllers/custom_wizard/admin/pro.rb +++ b/controllers/custom_wizard/admin/pro.rb @@ -10,15 +10,15 @@ class CustomWizard::AdminProController < CustomWizard::AdminController def authorize request_id = SecureRandom.hex(32) cookies[:user_api_request_id] = request_id - redirect_to CustomWizard::ProAuthentication.generate_request(current_user.id, request_id).to_s + redirect_to CustomWizard::Pro.auth_request(current_user.id, request_id).to_s end def authorize_callback payload = params[:payload] request_id = cookies[:user_api_request_id] - CustomWizard::ProAuthentication.handle_response(request_id, payload) - CustomWizard::ProSubscription.update + CustomWizard::Pro.auth_response(request_id, payload) + CustomWizard::Pro.update_subscription redirect_to '/admin/wizards/pro' end @@ -32,7 +32,7 @@ class CustomWizard::AdminProController < CustomWizard::AdminController end def update_subscription - if CustomWizard::ProSubscription.update + if CustomWizard::Pro.update render json: success_json.merge( subscription: CustomWizard::ProSubscriptionSerializer.new( CustomWizard::ProSubscription.new, diff --git a/jobs/scheduled/update_pro_status.rb b/jobs/scheduled/update_pro_status.rb index 5f76f52b..6f947304 100644 --- a/jobs/scheduled/update_pro_status.rb +++ b/jobs/scheduled/update_pro_status.rb @@ -4,6 +4,6 @@ class CustomWizard::UpdateProSubscription < ::Jobs::Scheduled every 10.minutes def execute(args) - CustomWizard::ProSubscription.update + CustomWizard::Pro.update_subscription end end \ No newline at end of file diff --git a/lib/custom_wizard/pro.rb b/lib/custom_wizard/pro.rb index e6bf2b0b..f54dd92d 100644 --- a/lib/custom_wizard/pro.rb +++ b/lib/custom_wizard/pro.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true class CustomWizard::Pro - NAMESPACE ||= "#{CustomWizard::PLUGIN_NAME}_pro" - attr_reader :authentication, :subscription @@ -11,6 +9,18 @@ class CustomWizard::Pro @subscription = CustomWizard::ProSubscription.new end + def server + "test.thepavilion.io" + end + + def subscription_type + "stripe" + end + + def client_name + "custom-wizard" + end + def authorized? @authentication.active? end @@ -19,7 +29,78 @@ class CustomWizard::Pro @subscription.active? end + def update_subscription + if @authentication.active? + response = Excon.get( + "https://#{server}/subscription-server/user-subscriptions/#{subscription_type}/#{client_name}", + headers: { + "User-Api-Key" => @authentication.api_key + } + ) + + if response.status == 200 + begin + data = JSON.parse(response.body).deep_symbolize_keys + rescue JSON::ParserError + return false + end + + return @subscription.update(data) + end + end + + @subscription.destroy + false + end + + def destroy + @authentication.destroy + end + + def auth_request(user_id, request_id) + keys = @authentication.generate_keys(user_id, request_id) + + params = { + public_key: keys.public_key, + nonce: keys.nonce, + client_id: @authentication.client_id, + auth_redirect: "#{Discourse.base_url}/admin/wizards/pro/authorize/callback", + application_name: SiteSetting.title, + scopes: "discourse-subscription-server:user_subscription" + } + + uri = URI.parse("https://#{server}/user-api-key/new") + uri.query = URI.encode_www_form(params) + uri.to_s + end + + def auth_response(request_id, payload) + data = @authentication.decrypt_payload(request_id, payload) + return unless data.is_a?(Hash) && data[:key] && data[:user_id] + @authentication.update(data) + end + + def self.update + self.new.update + end + + def self.destroy + self.new.destroy + end + + def self.generate_request + self.new.generate_request + end + + def self.handle_response + self.new.handle_response + end + def self.subscribed? self.new.subscribed? end + + def self.namespace + "custom_wizard_pro" + end end \ No newline at end of file diff --git a/lib/custom_wizard/pro/authentication.rb b/lib/custom_wizard/pro/authentication.rb index c92710bc..0d89ab06 100644 --- a/lib/custom_wizard/pro/authentication.rb +++ b/lib/custom_wizard/pro/authentication.rb @@ -1,10 +1,6 @@ class CustomWizard::ProAuthentication include ActiveModel::Serialization - API_KEY ||= "api_key" - API_CLIENT_ID ||= 'api_client_id' - KEYS ||= "keys" - attr_reader :client_id, :auth_by, :auth_at, @@ -39,10 +35,6 @@ class CustomWizard::ProAuthentication remove end - def self.destroy - self.new.destroy - end - def generate_keys(user_id, request_id) rsa = OpenSSL::PKey::RSA.generate(2048) nonce = SecureRandom.hex(32) @@ -72,37 +64,22 @@ class CustomWizard::ProAuthentication data end - def self.generate_request(user_id, request_id) - authentication = self.new - keys = authentication.generate_keys(user_id, request_id) - - params = { - public_key: keys.public_key, - nonce: keys.nonce, - client_id: authentication.client_id, - auth_redirect: "#{Discourse.base_url}/admin/wizards/pro/authorize/callback", - application_name: SiteSetting.title, - scopes: CustomWizard::ProSubscription::SCOPE - } - - uri = URI.parse("https://#{CustomWizard::ProSubscription::SUBSCRIPTION_SERVER}/user-api-key/new") - uri.query = URI.encode_www_form(params) - uri.to_s - end - - def self.handle_response(request_id, payload) - authentication = self.new - - data = authentication.decrypt_payload(request_id, payload) - return unless data.is_a?(Hash) && data[:key] && data[:user_id] - - authentication.update(data) - end - private + def api_key_db_key + "api_key" + end + + def api_client_id_db_key + "api_client_id" + end + + def keys_db_key + "keys" + end + def get_api_key - raw = PluginStore.get(CustomWizard::Pro::NAMESPACE, API_KEY) + raw = PluginStore.get(CustomWizard::Pro.namespace, api_key_db_key) OpenStruct.new( key: raw && raw['key'], auth_by: raw && raw['auth_by'], @@ -111,7 +88,7 @@ class CustomWizard::ProAuthentication end def set_api_key(key, user_id) - PluginStore.set(CustomWizard::Pro::NAMESPACE, API_KEY, + PluginStore.set(CustomWizard::Pro.namespace, api_key_db_key, key: key, auth_by: user_id, auth_at: Time.now @@ -119,21 +96,21 @@ class CustomWizard::ProAuthentication end def remove - PluginStore.remove(CustomWizard::Pro::NAMESPACE, API_KEY) + PluginStore.remove(CustomWizard::Pro.namespace, api_key_db_key) end def get_client_id - PluginStore.get(CustomWizard::Pro::NAMESPACE, API_CLIENT_ID) + PluginStore.get(CustomWizard::Pro.namespace, api_client_id_db_key) end def set_client_id client_id = SecureRandom.hex(32) - PluginStore.set(CustomWizard::Pro::NAMESPACE, API_CLIENT_ID, client_id) + PluginStore.set(CustomWizard::Pro.namespace, api_client_id_db_key, client_id) client_id end def set_keys(request_id, user_id, rsa, nonce) - PluginStore.set(CustomWizard::Pro::NAMESPACE, "#{KEYS}_#{request_id}", + PluginStore.set(CustomWizard::Pro.namespace, "#{keys_db_key}_#{request_id}", user_id: user_id, pem: rsa.export, nonce: nonce @@ -141,7 +118,7 @@ class CustomWizard::ProAuthentication end def get_keys(request_id) - raw = PluginStore.get(CustomWizard::Pro::NAMESPACE, "#{KEYS}_#{request_id}") + raw = PluginStore.get(CustomWizard::Pro.namespace, "#{keys_db_key}_#{request_id}") OpenStruct.new( user_id: raw && raw['user_id'], pem: raw && raw['pem'], @@ -150,6 +127,6 @@ class CustomWizard::ProAuthentication end def delete_keys(request_id) - PluginStore.remove(CustomWizard::Pro::NAMESPACE, "#{KEYS}_#{request_id}") + PluginStore.remove(CustomWizard::Pro.namespace, "#{keys_db_key}_#{request_id}") end end \ No newline at end of file diff --git a/lib/custom_wizard/pro/subscription.rb b/lib/custom_wizard/pro/subscription.rb index ef0ac2b9..f82b58df 100644 --- a/lib/custom_wizard/pro/subscription.rb +++ b/lib/custom_wizard/pro/subscription.rb @@ -1,14 +1,6 @@ class CustomWizard::ProSubscription include ActiveModel::Serialization - SUBSCRIPTION_SERVER ||= "test.thepavilion.io" - SUBSCRIPTION_TYPE ||= "stripe" - SCOPE ||= "discourse-subscription-server:user_subscription" - CLIENT_NAME ||= "custom-wizard" - SUBSCRIPTION_KEY ||= "custom_wizard_pro_subscription" - UPDATE_DAY_BUFFER ||= 2 - TYPES ||= %w(community business) - attr_reader :type, :updated_at @@ -21,8 +13,12 @@ class CustomWizard::ProSubscription end end + def types + %w(community business) + end + def active? - TYPES.include?(type) && updated_at.to_datetime > (Date.today - UPDATE_DAY_BUFFER.days).to_datetime + types.include?(type) && updated_at.to_datetime > (Date.today - 15.minutes).to_datetime end def update(data) @@ -37,39 +33,25 @@ class CustomWizard::ProSubscription end end - def self.update - auth = CustomWizard::ProAuthentication.new - subscription = self.new - - if auth.active? - response = Excon.get( - "https://#{SUBSCRIPTION_SERVER}/subscription-server/user-subscriptions/#{SUBSCRIPTION_TYPE}/#{CLIENT_NAME}", - headers: { - "User-Api-Key" => auth.api_key - } - ) - - if response.status == 200 - begin - data = JSON.parse(response.body).deep_symbolize_keys - rescue JSON::ParserError - return false - end - - return subscription.update(data) - end - end - - false + def destroy + remove end - + private - + + def key + "custom_wizard_pro_subscription" + end + def set(type) - PluginStore.set(CustomWizard::Pro::NAMESPACE, SUBSCRIPTION_KEY, type: type, updated_at: Time.now) + PluginStore.set(CustomWizard::Pro.namespace, key, type: type, updated_at: Time.now) end def get - PluginStore.get(CustomWizard::Pro::NAMESPACE, SUBSCRIPTION_KEY) + PluginStore.get(CustomWizard::Pro.namespace, key) + end + + def remove + PluginStore.remove(CustomWizard::Pro.namespace, key) end end \ No newline at end of file From 03fb7b7adab3780fc0860ed8da27cc144861095c Mon Sep 17 00:00:00 2001 From: angusmcleod Date: Wed, 25 Aug 2021 09:59:24 +0800 Subject: [PATCH 016/160] WIP: update field data handling to support column toggling --- .../admin-wizards-submissions-show.js.es6 | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/assets/javascripts/discourse/controllers/admin-wizards-submissions-show.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards-submissions-show.js.es6 index 6352b3b2..28a1c825 100644 --- a/assets/javascripts/discourse/controllers/admin-wizards-submissions-show.js.es6 +++ b/assets/javascripts/discourse/controllers/admin-wizards-submissions-show.js.es6 @@ -24,6 +24,18 @@ export default Controller.extend({ this.set("loadingMore", false); }); }, + + + @discourseComputed('submissions', 'fields.@each.enabled') + displaySubmissions(submissions, fields) { + return submissions.map(submission => { + let field = fields.find(f => Object.keys(submission).includes(f.id)); + if (!field.enabled) { + submission.delete(field.id); + }; + return submission; + }); + }, actions: { loadMore() { From d74d3d25beecae7011b9c102726f8d074cb8f66c Mon Sep 17 00:00:00 2001 From: Keegan George Date: Mon, 30 Aug 2021 16:23:33 -0700 Subject: [PATCH 017/160] UX: Display submission fields unique to each type --- .../components/submission-field.js.es6 | 114 ++++++++++++++++++ .../admin-wizards-submissions-show.hbs | 20 ++- .../templates/components/submission-field.hbs | 114 ++++++++++++++++++ assets/stylesheets/common/wizard-admin.scss | 51 ++++++++ config/locales/client.en.yml | 6 + plugin.rb | 7 ++ 6 files changed, 307 insertions(+), 5 deletions(-) create mode 100644 assets/javascripts/discourse/components/submission-field.js.es6 create mode 100644 assets/javascripts/discourse/templates/components/submission-field.hbs diff --git a/assets/javascripts/discourse/components/submission-field.js.es6 b/assets/javascripts/discourse/components/submission-field.js.es6 new file mode 100644 index 00000000..f76376a5 --- /dev/null +++ b/assets/javascripts/discourse/components/submission-field.js.es6 @@ -0,0 +1,114 @@ +import { action } from "@ember/object"; +import Component from "@ember/component"; +import { equal } from "@ember/object/computed"; +import discourseComputed from "discourse-common/utils/decorators"; +import I18n from "I18n"; + + +export default Component.extend({ + isText: equal("value.type", "text"), + isComposer: equal("value.type", "composer"), + isDate: equal("value.type", "date"), + isTime: equal("value.type", "time"), + isDateTime: equal("value.type", "date_time"), + isNumber: equal("value.type", "number"), + isCheckbox: equal("value.type", "checkbox"), + isUrl: equal("value.type", "url"), + isUpload: equal("value.type", "upload"), + isDropdown: equal("value.type", "dropdown"), + isTag: equal("value.type", "tag"), + isCategory: equal("value.type", "category"), + isGroup: equal("value.type", "group"), + isUser: equal("fieldName", "username"), + isUserSelector: equal("value.type", "user_selector"), + isSubmittedAt: equal("fieldName", "submitted_at"), + isTextArea: equal("value.type", "textarea"), + isComposerPreview: equal("value.type", "composer_preview"), + textState: "text-collapsed", + toggleText: I18n.t('admin.wizard.submissions.expand_text'), + + @discourseComputed("value") + checkboxValue(value) { + const isCheckbox = this.get("isCheckbox"); + if (isCheckbox) { + if (value.value.includes("true")) { + return true; + } else if (value.value.includes("false")) { + return false; + } + } + }, + + @action + expandText() { + const state = this.get("textState"); + + if (state === "text-collapsed") { + this.set("textState", "text-expanded"); + this.set("toggleText", I18n.t('admin.wizard.submissions.collapse_text')); + } else if (state === "text-expanded") { + this.set("textState", "text-collapsed"); + this.set("toggleText", I18n.t('admin.wizard.submissions.expand_text')); + } + }, + + @discourseComputed('value') + file(value) { + const isUpload = this.get("isUpload"); + if (isUpload) { + return value.value; + } + }, + + @discourseComputed('value') + submittedUsers(value) { + const isUserSelector = this.get("isUserSelector"); + const users = []; + + if (isUserSelector) { + const userData = value.value; + const usernames = []; + + if (userData.indexOf(',')) { + usernames.push(...userData.split(',')); + + usernames.forEach(u => { + const user = { + username: u, + url: `/u/${u}` + } + users.push(user); + }) + } + } + return users; + }, + + @discourseComputed('value') + userProfileUrl(value) { + const isUser = this.get("isUser"); + const isUserSelector = this.get("isUserSelector"); + + if (isUser) { + return `/u/${value.username}`; + } + }, + + @discourseComputed('value') + categoryUrl(value) { + const isCategory = this.get('isCategory'); + + if (isCategory) { + return `/c/${value.value}`; + } + }, + + @discourseComputed('value') + groupUrl(value) { + const isGroup = this.get('isGroup'); + + if (isGroup) { + return `/g/${value.value}`; + } + }, +}); diff --git a/assets/javascripts/discourse/templates/admin-wizards-submissions-show.hbs b/assets/javascripts/discourse/templates/admin-wizards-submissions-show.hbs index 9e8e10c8..2f0689b1 100644 --- a/assets/javascripts/discourse/templates/admin-wizards-submissions-show.hbs +++ b/assets/javascripts/discourse/templates/admin-wizards-submissions-show.hbs @@ -2,6 +2,14 @@
+
+ {{d-button + icon="sliders-h" + label="admin.wizard.submissions.edit_columns" + action=(action "showEditColumnsModal") + class="btn-default open-edit-columns-btn download-link"}} +
+ {{d-icon "download"}} @@ -18,16 +26,18 @@ - {{#each fields as |f|}} - + {{#each fields as |field|}} + {{#if field.enabled}} + + {{/if}} {{/each}} - {{#each submissions as |s|}} + {{#each displaySubmissions as |submission|}} - {{#each-in s as |k v|}} - + {{#each-in submission as |field value|}} + {{/each-in}} {{/each}} diff --git a/assets/javascripts/discourse/templates/components/submission-field.hbs b/assets/javascripts/discourse/templates/components/submission-field.hbs new file mode 100644 index 00000000..52cde1dd --- /dev/null +++ b/assets/javascripts/discourse/templates/components/submission-field.hbs @@ -0,0 +1,114 @@ +{{#if isText}} + {{value.value}} +{{/if}} + +{{#if isTextArea}} +
+

{{value.value}}

+ {{toggleText}} +
+{{/if}} + +{{#if isComposer}} +
+

{{value.value}}

+ {{toggleText}} +
+{{/if}} + +{{#if isComposerPreview}} + {{d-icon "comment-alt" }} {{i18n "admin.wizard.submissions.composer_preview"}}: {{value.value}} +{{/if}} + +{{#if isTextOnly}} + {{value.value}} +{{/if}} + +{{#if isDate}} + + {{d-icon "calendar"}}{{value.value}} + +{{/if}} + +{{#if isTime}} + + {{d-icon "clock"}}{{value.value}} + +{{/if}} + +{{#if isDateTime}} + + {{d-icon "calendar"}}{{format-date value.value format="medium"}} + +{{/if}} + +{{#if isNumber}} + {{value.value}} +{{/if}} + +{{#if isCheckbox}} + {{#if checkboxValue}} + + {{d-icon "check"}}{{value.value}} + + {{else}} + + {{d-icon "times"}}{{value.value}} + + {{/if}} +{{/if}} + +{{#if isUrl}} + + {{ d-icon "link" }} + + {{value.value}} + + +{{/if}} + +{{#if isUpload}} + + {{file.original_filename}} + +{{/if}} + +{{#if isDropdown}} + + {{ d-icon "check-square" }} + {{ value.value }} + +{{/if}} + +{{#if isTag}} + {{#each value.value as |tag|}} + {{discourse-tag tag}} + {{/each}} +{{/if}} + +{{#if isCategory}} + {{i18n "admin.wizard.submissions.category_id"}}: {{value.value}} +{{/if}} + +{{#if isGroup}} + {{i18n "admin.wizard.submissions.group_id"}}: {{ value.value }} +{{/if}} + + +{{#if isUserSelector}} + {{#each submittedUsers as |user|}} + {{ d-icon "user" }} + {{user.username}} + {{/each}} +{{/if}} + +{{#if isUser}} + {{#link-to "user" value}}{{avatar value imageSize="tiny"}}{{/link-to}} + {{value.username}} +{{/if}} + +{{#if isSubmittedAt}} + + {{d-icon "clock"}}{{format-date value format="tiny"}} + +{{/if}} diff --git a/assets/stylesheets/common/wizard-admin.scss b/assets/stylesheets/common/wizard-admin.scss index 66cc6b43..66c8d2f7 100644 --- a/assets/stylesheets/common/wizard-admin.scss +++ b/assets/stylesheets/common/wizard-admin.scss @@ -71,6 +71,51 @@ table td { min-width: 150px; } + + table thead th { + text-transform: capitalize; + } + + .submission-icon-item { + display: flex; + align-items: center; + svg { + margin-right: 5px; + } + } + + .submission-checkbox-true { + text-transform: capitalize; + color: var(--success); + } + + .submission-checkbox-false { + text-transform: capitalize; + color: var(--danger); + } + + .submission-long-text { + &-content { + white-space: nowrap; + word-wrap: break-word; + overflow: hidden; + text-overflow: ellipsis; + width: 250px; + margin-bottom: 0; + + &.text-expanded { + white-space: normal; + } + } + + a { + font-size: var(--font-down-1); + } + } + + .submission-composer-text { + font-family: monospace; + } } .admin-wizards-logs { @@ -203,6 +248,11 @@ &.underline { text-decoration: underline; } + + .controls { + margin-left: auto; + margin-right: 0.5rem; + } } .admin-wizard-buttons { @@ -715,3 +765,4 @@ width: 80px; vertical-align: middle; } + diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index ce253455..8501e2a2 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -367,6 +367,12 @@ en: nav_label: "Submissions" title: "{{name}} Submissions" download: "Download" + edit_columns: "Edit Columns" + expand_text: "Read More" + collapse_text: "Show Less" + group_id: "Group ID" + category_id: "Category ID" + composer_preview: "Composer Preview" api: label: "API" diff --git a/plugin.rb b/plugin.rb index 449d0237..993fe0d5 100644 --- a/plugin.rb +++ b/plugin.rb @@ -33,6 +33,13 @@ if respond_to?(:register_svg_icon) register_svg_icon "chevron-right" register_svg_icon "chevron-left" register_svg_icon "save" + register_svg_icon "sliders-h" + register_svg_icon "calendar" + register_svg_icon "check" + register_svg_icon "times" + register_svg_icon "clock" + register_svg_icon "link" + register_svg_icon "comment-alt" end class ::Sprockets::DirectiveProcessor From 0bc151fc7b4c17b9d19ed2e138dd724fe3c44eab Mon Sep 17 00:00:00 2001 From: Keegan George Date: Mon, 30 Aug 2021 16:33:48 -0700 Subject: [PATCH 018/160] WIP: Edit Columns modal functionality Currently modal and edit columns works but removes only field and not corresponding submission. --- .../admin-wizards-submissions-columns.js.es6 | 16 ++++++++++ .../admin-wizards-submissions-show.js.es6 | 19 +++++++++-- .../discourse/models/custom-wizard.js.es6 | 24 +++++++------- .../admin-wizards-submissions-show.js.es6 | 12 +++++-- .../admin-wizards-submissions-columns.hbs | 32 +++++++++++++++++++ 5 files changed, 87 insertions(+), 16 deletions(-) create mode 100644 assets/javascripts/discourse/controllers/admin-wizards-submissions-columns.js.es6 create mode 100644 assets/javascripts/discourse/templates/modal/admin-wizards-submissions-columns.hbs diff --git a/assets/javascripts/discourse/controllers/admin-wizards-submissions-columns.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards-submissions-columns.js.es6 new file mode 100644 index 00000000..5627dacf --- /dev/null +++ b/assets/javascripts/discourse/controllers/admin-wizards-submissions-columns.js.es6 @@ -0,0 +1,16 @@ +import Controller from "@ember/controller"; +import ModalFunctionality from "discourse/mixins/modal-functionality"; + +export default Controller.extend(ModalFunctionality, { + + actions: { + save() { + this.send("closeModal"); + }, + resetToDefault() { + this.get('model.fields').forEach(field => { + field.set("enabled", true); + }); + } + } +}); \ No newline at end of file diff --git a/assets/javascripts/discourse/controllers/admin-wizards-submissions-show.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards-submissions-show.js.es6 index 28a1c825..40a08f4f 100644 --- a/assets/javascripts/discourse/controllers/admin-wizards-submissions-show.js.es6 +++ b/assets/javascripts/discourse/controllers/admin-wizards-submissions-show.js.es6 @@ -2,6 +2,9 @@ import Controller from "@ember/controller"; import { fmt } from "discourse/lib/computed"; import { empty } from "@ember/object/computed"; import CustomWizard from "../models/custom-wizard"; +import showModal from "discourse/lib/show-modal"; +import discourseComputed from "discourse-common/utils/decorators"; + export default Controller.extend({ downloadUrl: fmt("wizard.id", "/admin/wizards/submissions/%@/download"), @@ -31,8 +34,11 @@ export default Controller.extend({ return submissions.map(submission => { let field = fields.find(f => Object.keys(submission).includes(f.id)); if (!field.enabled) { - submission.delete(field.id); - }; + // insert field / submission deletion code here: + console.log(field, "is not enabled for ", submission); + } else if (field.enabled) { + console.log(field, "is enabled for ", submission); + } return submission; }); }, @@ -44,5 +50,14 @@ export default Controller.extend({ this.loadMoreSubmissions(); } }, + + showEditColumnsModal() { + const controller = showModal("admin-wizards-submissions-columns", { + model: { + fields: this.get('fields'), + submissions: this.get('submissions') + } + }); + }, }, }); diff --git a/assets/javascripts/discourse/models/custom-wizard.js.es6 b/assets/javascripts/discourse/models/custom-wizard.js.es6 index 5e94e31e..d9a2bafa 100644 --- a/assets/javascripts/discourse/models/custom-wizard.js.es6 +++ b/assets/javascripts/discourse/models/custom-wizard.js.es6 @@ -224,31 +224,33 @@ CustomWizard.reopenClass({ }) .then((result) => { if (result.wizard) { - let fields = ["username"]; + console.log(result); + let fields = [{ id: "username", label: "User"}]; let submissions = []; let wizard = result.wizard; let total = result.total; result.submissions.forEach((s) => { let submission = { - username: s.username, + username: s.user, }; - Object.keys(s.fields).forEach((f) => { - if (fields.indexOf(f) < 0) { - fields.push(f); - } - - if (fields.includes(f)) { - submission[f] = s.fields[f]; + Object.keys(s.fields).forEach((fieldId) => { + if (!fields.some(field => field.id === fieldId)) { + fields.push({ id: fieldId, label: s.fields[fieldId].label }); } + submission[fieldId] = s.fields[fieldId]; }); - submission["submitted_at"] = s.submitted_at; submissions.push(submission); }); - fields.push("submitted_at"); + let submittedAt = { + id: "submitted_at", + label: "Submitted At" + } + + fields.push(submittedAt); return { wizard, diff --git a/assets/javascripts/discourse/routes/admin-wizards-submissions-show.js.es6 b/assets/javascripts/discourse/routes/admin-wizards-submissions-show.js.es6 index 2ff9fbf9..829d4d13 100644 --- a/assets/javascripts/discourse/routes/admin-wizards-submissions-show.js.es6 +++ b/assets/javascripts/discourse/routes/admin-wizards-submissions-show.js.es6 @@ -1,6 +1,7 @@ -import CustomWizard from "../models/custom-wizard"; -import DiscourseRoute from "discourse/routes/discourse"; import { A } from "@ember/array"; +import EmberObject from "@ember/object"; +import DiscourseRoute from "discourse/routes/discourse"; +import CustomWizard from "../models/custom-wizard"; export default DiscourseRoute.extend({ model(params) { @@ -8,9 +9,14 @@ export default DiscourseRoute.extend({ }, setupController(controller, model) { + const fields = model.fields.map((f) => { + const fieldsObject = EmberObject.create(f); + fieldsObject.enabled = true; + return fieldsObject; + }); controller.setProperties({ wizard: model.wizard, - fields: model.fields, + fields: A(fields), submissions: A(model.submissions), total: model.total, }); diff --git a/assets/javascripts/discourse/templates/modal/admin-wizards-submissions-columns.hbs b/assets/javascripts/discourse/templates/modal/admin-wizards-submissions-columns.hbs new file mode 100644 index 00000000..a167a954 --- /dev/null +++ b/assets/javascripts/discourse/templates/modal/admin-wizards-submissions-columns.hbs @@ -0,0 +1,32 @@ +{{#d-modal-body title="directory.edit_columns.title"}} + {{#if loading}} + {{loading-spinner size="large"}} + {{else}} +
+ {{#each model.fields as |field|}} +
+
+ +
+
+ {{/each}} +
+ {{/if}} +{{/d-modal-body}} + + From aa928c319e8fd7da010a48c894b7535c1b41a5bb Mon Sep 17 00:00:00 2001 From: KC Maddever Date: Tue, 10 Aug 2021 20:11:41 +0800 Subject: [PATCH 019/160] DEV: add data migration to split out log fields --- ...06135416_split_custom_wizard_log_fields.rb | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 db/migrate/20210806135416_split_custom_wizard_log_fields.rb diff --git a/db/migrate/20210806135416_split_custom_wizard_log_fields.rb b/db/migrate/20210806135416_split_custom_wizard_log_fields.rb new file mode 100644 index 00000000..b261c497 --- /dev/null +++ b/db/migrate/20210806135416_split_custom_wizard_log_fields.rb @@ -0,0 +1,70 @@ +class SplitCustomWizardLogFields < ActiveRecord::Migration[6.1] + def change + reversible do |dir| + dir.up do + # separate wizard/action/user into their own keys + + wizard_logs = PluginStoreRow.where(" + plugin_name = 'custom_wizard_log' + ") + + if wizard_logs.exists? + wizard_logs.each do |row| + begin + log_json = JSON.parse(row.value) + rescue TypeError, JSON::ParserError + next + end + + # first three keys are wizard/action/user + + if log_json.key?('message') + attr_strs = log_json['message'].split('; ', 4) + + log_json['message'] = attr_strs.pop + + attr_strs.each do |attr_str| + key, value = attr_str.split(': ') + log_json[key] = value + end + + row.value = log_json.to_json + row.save + + end + end + end + end + dir.down do + wizard_logs = PluginStoreRow.where(" + plugin_name = 'custom_wizard_log' + ") + + if wizard_logs.exists? + wizard_logs.each do |row| + begin + log_json = JSON.parse(row.value) + rescue TypeError, JSON::ParserError + next + end + + # concatenate wizard/action/user to start of message + prefixes = log_json.extract!('wizard', 'action', 'user') + + message_prefix = prefixes.map{|k,v| "#{k}: #{v}"}.join('; ') + + if log_json.key?('message') + log_json['message'] = "#{message_prefix}; #{log_json['message']}" + else + log_json['message'] = message_prefix + end + + row.value = log_json.to_json + row.save + + end + end + end + end + end +end From b79039c2e2d98d89d0dee99413b52328666de0ca Mon Sep 17 00:00:00 2001 From: KC Maddever Date: Tue, 10 Aug 2021 21:18:02 +0800 Subject: [PATCH 020/160] DEV: save logs with split fields --- lib/custom_wizard/action.rb | 15 ++++++--------- lib/custom_wizard/log.rb | 12 +++++++++--- serializers/custom_wizard/log_serializer.rb | 2 +- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/lib/custom_wizard/action.rb b/lib/custom_wizard/action.rb index 1b5770d7..79094024 100644 --- a/lib/custom_wizard/action.rb +++ b/lib/custom_wizard/action.rb @@ -742,14 +742,11 @@ class CustomWizard::Action end def save_log - log = "wizard: #{@wizard.id}; action: #{action['type']}; user: #{user.username}" - - if @log.any? - @log.each do |item| - log += "; #{item.to_s}" - end - end - - CustomWizard::Log.create(log) + CustomWizard::Log.create( + @wizard.id, + action['type'], + user.username, + @log.join('; ') + ) end end diff --git a/lib/custom_wizard/log.rb b/lib/custom_wizard/log.rb index fc747440..c50a5712 100644 --- a/lib/custom_wizard/log.rb +++ b/lib/custom_wizard/log.rb @@ -2,22 +2,28 @@ class CustomWizard::Log include ActiveModel::Serialization - attr_accessor :message, :date + attr_accessor :date, :wizard, :action, :user, :message PAGE_LIMIT = 100 def initialize(attrs) - @message = attrs['message'] @date = attrs['date'] + @wizard = attrs['wizard'] + @action = attrs['action'] + @user = attrs['user'] + @message = attrs['message'] end - def self.create(message) + def self.create(wizard, action, user, message) log_id = SecureRandom.hex(12) PluginStore.set('custom_wizard_log', log_id.to_s, { date: Time.now, + wizard: wizard, + action: action, + user: user, message: message } ) diff --git a/serializers/custom_wizard/log_serializer.rb b/serializers/custom_wizard/log_serializer.rb index e521c573..c4683ba8 100644 --- a/serializers/custom_wizard/log_serializer.rb +++ b/serializers/custom_wizard/log_serializer.rb @@ -1,4 +1,4 @@ # frozen_string_literal: true class CustomWizard::LogSerializer < ApplicationSerializer - attributes :message, :date + attributes :date, :wizard, :action, :user, :message end From 35ff172967eaa4964e9e919688dc090dd7a29cce Mon Sep 17 00:00:00 2001 From: KC Maddever Date: Tue, 24 Aug 2021 20:41:51 +0800 Subject: [PATCH 021/160] DEV: specify which fields to split in log data migration --- ...06135416_split_custom_wizard_log_fields.rb | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/db/migrate/20210806135416_split_custom_wizard_log_fields.rb b/db/migrate/20210806135416_split_custom_wizard_log_fields.rb index b261c497..2e7b7d21 100644 --- a/db/migrate/20210806135416_split_custom_wizard_log_fields.rb +++ b/db/migrate/20210806135416_split_custom_wizard_log_fields.rb @@ -16,16 +16,23 @@ class SplitCustomWizardLogFields < ActiveRecord::Migration[6.1] next end - # first three keys are wizard/action/user - if log_json.key?('message') - attr_strs = log_json['message'].split('; ', 4) + if log_json.key?('message') and log_json['message'].is_a? String - log_json['message'] = attr_strs.pop + attr_strs = [] + + # assumes no whitespace in the values + attr_strs << log_json['message'].slice!(/(wizard: \S*; )/, 1) + attr_strs << log_json['message'].slice!(/(action: \S*; )/, 1) + attr_strs << log_json['message'].slice!(/(user: \S*; )/, 1) attr_strs.each do |attr_str| - key, value = attr_str.split(': ') - log_json[key] = value + if attr_str.is_a? String + attr_str.gsub!(/[;]/ ,"") + key, value = attr_str.split(': ') + value.strip! if value + log_json[key] = value ? value : '' + end end row.value = log_json.to_json From c394656ed877469a90ae86106cbcbbd82fd72751 Mon Sep 17 00:00:00 2001 From: KC Maddever Date: Mon, 30 Aug 2021 21:45:57 +0800 Subject: [PATCH 022/160] DEV: update tests for split log fields --- spec/components/custom_wizard/log_spec.rb | 6 +++--- spec/requests/custom_wizard/admin/logs_controller_spec.rb | 6 +++--- spec/serializers/custom_wizard/log_serializer_spec.rb | 7 +++++-- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/spec/components/custom_wizard/log_spec.rb b/spec/components/custom_wizard/log_spec.rb index 30fd0173..62f2e6df 100644 --- a/spec/components/custom_wizard/log_spec.rb +++ b/spec/components/custom_wizard/log_spec.rb @@ -3,9 +3,9 @@ require_relative '../../plugin_helper' describe CustomWizard::Log do before do - CustomWizard::Log.create("First log message") - CustomWizard::Log.create("Second log message") - CustomWizard::Log.create("Third log message") + CustomWizard::Log.create('first-test-wizard', 'perform_first_action', 'first_test_user', 'First log message') + CustomWizard::Log.create('second-test-wizard', 'perform_second_action', 'second_test_user', 'Second log message') + CustomWizard::Log.create('third-test-wizard', 'perform_third_action', 'third_test_user', 'Third log message') end it "creates logs" do diff --git a/spec/requests/custom_wizard/admin/logs_controller_spec.rb b/spec/requests/custom_wizard/admin/logs_controller_spec.rb index 28b7d785..5aaf9578 100644 --- a/spec/requests/custom_wizard/admin/logs_controller_spec.rb +++ b/spec/requests/custom_wizard/admin/logs_controller_spec.rb @@ -5,9 +5,9 @@ describe CustomWizard::AdminLogsController do fab!(:admin_user) { Fabricate(:user, admin: true) } before do - CustomWizard::Log.create("First log message") - CustomWizard::Log.create("Second log message") - CustomWizard::Log.create("Third log message") + CustomWizard::Log.create('first-test-wizard', 'perform_first_action', 'first_test_user', 'First log message') + CustomWizard::Log.create('second-test-wizard', 'perform_second_action', 'second_test_user', 'Second log message') + CustomWizard::Log.create('third-test-wizard', 'perform_third_action', 'third_test_user', 'Third log message') sign_in(admin_user) end diff --git a/spec/serializers/custom_wizard/log_serializer_spec.rb b/spec/serializers/custom_wizard/log_serializer_spec.rb index bde16199..b452b9c5 100644 --- a/spec/serializers/custom_wizard/log_serializer_spec.rb +++ b/spec/serializers/custom_wizard/log_serializer_spec.rb @@ -6,14 +6,17 @@ describe CustomWizard::LogSerializer do fab!(:user) { Fabricate(:user) } it 'should return log attributes' do - CustomWizard::Log.create("First log message") - CustomWizard::Log.create("Second log message") + CustomWizard::Log.create('first-test-wizard', 'perform_first_action', 'first_test_user', 'First log message') + CustomWizard::Log.create('second-test-wizard', 'perform_second_action', 'second_test_user', 'Second log message') json_array = ActiveModel::ArraySerializer.new( CustomWizard::Log.list(0), each_serializer: CustomWizard::LogSerializer ).as_json expect(json_array.length).to eq(2) + expect(json_array[0][:wizard]).to eq("second-test-wizard") + expect(json_array[0][:action]).to eq("perform_second_action") + expect(json_array[0][:user]).to eq("second_test_user") expect(json_array[0][:message]).to eq("Second log message") end end From 399cc9c2205fed2ebdd7b82a5ffe5d9fbe97f4cd Mon Sep 17 00:00:00 2001 From: KC Maddever Date: Tue, 31 Aug 2021 14:43:21 +0800 Subject: [PATCH 023/160] DEV: rubocop --- ...10806135416_split_custom_wizard_log_fields.rb | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/db/migrate/20210806135416_split_custom_wizard_log_fields.rb b/db/migrate/20210806135416_split_custom_wizard_log_fields.rb index 2e7b7d21..984a7a23 100644 --- a/db/migrate/20210806135416_split_custom_wizard_log_fields.rb +++ b/db/migrate/20210806135416_split_custom_wizard_log_fields.rb @@ -1,11 +1,12 @@ +# frozen_string_literal: true class SplitCustomWizardLogFields < ActiveRecord::Migration[6.1] def change reversible do |dir| dir.up do - # separate wizard/action/user into their own keys + # separate wizard/action/user into their own keys - wizard_logs = PluginStoreRow.where(" - plugin_name = 'custom_wizard_log' + wizard_logs = PluginStoreRow.where(" + plugin_name = 'custom_wizard_log' ") if wizard_logs.exists? @@ -16,8 +17,7 @@ class SplitCustomWizardLogFields < ActiveRecord::Migration[6.1] next end - - if log_json.key?('message') and log_json['message'].is_a? String + if log_json.key?('message') && log_json['message'].is_a?(String) attr_strs = [] @@ -28,7 +28,7 @@ class SplitCustomWizardLogFields < ActiveRecord::Migration[6.1] attr_strs.each do |attr_str| if attr_str.is_a? String - attr_str.gsub!(/[;]/ ,"") + attr_str.gsub!(/[;]/ , "") key, value = attr_str.split(': ') value.strip! if value log_json[key] = value ? value : '' @@ -44,7 +44,7 @@ class SplitCustomWizardLogFields < ActiveRecord::Migration[6.1] end dir.down do wizard_logs = PluginStoreRow.where(" - plugin_name = 'custom_wizard_log' + plugin_name = 'custom_wizard_log' ") if wizard_logs.exists? @@ -58,7 +58,7 @@ class SplitCustomWizardLogFields < ActiveRecord::Migration[6.1] # concatenate wizard/action/user to start of message prefixes = log_json.extract!('wizard', 'action', 'user') - message_prefix = prefixes.map{|k,v| "#{k}: #{v}"}.join('; ') + message_prefix = prefixes.map { |k, v| "#{k}: #{v}" }.join('; ') if log_json.key?('message') log_json['message'] = "#{message_prefix}; #{log_json['message']}" From 280d2ffe54e4a9e3debe95deafa7bc419665707b Mon Sep 17 00:00:00 2001 From: Keegan George Date: Tue, 31 Aug 2021 13:29:30 -0700 Subject: [PATCH 024/160] IMPROVE: Make edit columns adjust submissions as well --- .../admin-wizards-submissions-show.js.es6 | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/assets/javascripts/discourse/controllers/admin-wizards-submissions-show.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards-submissions-show.js.es6 index 40a08f4f..2667b6b2 100644 --- a/assets/javascripts/discourse/controllers/admin-wizards-submissions-show.js.es6 +++ b/assets/javascripts/discourse/controllers/admin-wizards-submissions-show.js.es6 @@ -31,16 +31,20 @@ export default Controller.extend({ @discourseComputed('submissions', 'fields.@each.enabled') displaySubmissions(submissions, fields) { - return submissions.map(submission => { - let field = fields.find(f => Object.keys(submission).includes(f.id)); - if (!field.enabled) { - // insert field / submission deletion code here: - console.log(field, "is not enabled for ", submission); - } else if (field.enabled) { - console.log(field, "is enabled for ", submission); - } - return submission; + let result = []; + + submissions.forEach(submission => { + let sub = {}; + + Object.keys(submission).forEach(fieldId => { + if (fields.some(f => f.id === fieldId && f.enabled)) { + sub[fieldId] = submission[fieldId]; + } + }); + result.push(sub); }); + + return result; }, actions: { From 6b1e7568c154e545a4bdaabbef0259ddb90ae5ba Mon Sep 17 00:00:00 2001 From: angusmcleod Date: Wed, 1 Sep 2021 10:19:00 +0800 Subject: [PATCH 025/160] Improve PRO feature approach --- .../wizard-realtime-validations.js.es6 | 3 +- .../admin-wizards-wizard-show.js.es6 | 7 +- .../components/wizard-custom-field.hbs | 94 ++++++------- .../components/wizard-pro-subscription.hbs | 9 +- .../wizard-realtime-validations.hbs | 98 ++++++------- assets/stylesheets/common/wizard-admin.scss | 40 +++++- config/locales/client.en.yml | 2 +- config/locales/server.en.yml | 4 +- config/routes.rb | 2 +- controllers/custom_wizard/admin/pro.rb | 28 ++-- controllers/custom_wizard/wizard.rb | 5 + ...o_status.rb => update_pro_subscription.rb} | 2 +- lib/custom_wizard/action.rb | 6 - lib/custom_wizard/builder.rb | 6 - lib/custom_wizard/custom_field.rb | 6 + lib/custom_wizard/mapper.rb | 3 +- lib/custom_wizard/pro.rb | 131 +++++++++++++----- lib/custom_wizard/pro/authentication.rb | 78 +++-------- lib/custom_wizard/pro/subscription.rb | 46 +----- lib/custom_wizard/validators/template.rb | 33 ++++- plugin.rb | 16 +-- serializers/custom_wizard/pro_serializer.rb | 26 +--- 22 files changed, 331 insertions(+), 314 deletions(-) rename jobs/scheduled/{update_pro_status.rb => update_pro_subscription.rb} (89%) diff --git a/assets/javascripts/discourse/components/wizard-realtime-validations.js.es6 b/assets/javascripts/discourse/components/wizard-realtime-validations.js.es6 index 8332b86e..04123e3d 100644 --- a/assets/javascripts/discourse/components/wizard-realtime-validations.js.es6 +++ b/assets/javascripts/discourse/components/wizard-realtime-validations.js.es6 @@ -6,7 +6,8 @@ import discourseComputed from "discourse-common/utils/decorators"; import I18n from "I18n"; export default Component.extend({ - classNames: ["realtime-validations"], + classNames: ["realtime-validations", "setting", "full", "pro"], + @discourseComputed timeUnits() { return ["days", "weeks", "months", "years"].map((unit) => { 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 332efedd..edb06ad9 100644 --- a/assets/javascripts/discourse/controllers/admin-wizards-wizard-show.js.es6 +++ b/assets/javascripts/discourse/controllers/admin-wizards-wizard-show.js.es6 @@ -77,7 +77,12 @@ export default Controller.extend({ wizard .save(opts) .then((result) => { - this.send("afterSave", result.wizard_id); + console.log(result) + if (result.wizard_id) { + this.send("afterSave", result.wizard_id); + } else if (result.errors) { + this.set('error', result.errors.join(', ')); + } }) .catch((result) => { let errorType = "failed"; diff --git a/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs b/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs index 9237cf62..afdbd767 100644 --- a/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs +++ b/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs @@ -220,60 +220,54 @@ options=fieldConditionOptions}} -{{/if}} + +
+
+ + {{i18n "admin.wizard.pro.label"}} +
-{{#if showAdvanced}} - {{wizard-advanced-toggle showAdvanced=field.showAdvanced}} +
+ {{wizard-mapper + inputs=field.index + options=fieldIndexOptions}} +
+
- {{#if field.showAdvanced}} -
- -
-
- -
- -
- {{wizard-mapper - inputs=field.index - options=fieldIndexOptions}} -
+ {{#if isCategory}} +
+
+ + {{i18n "admin.wizard.pro.label"}}
- - {{#if isCategory}} -
-
- -
- -
- {{combo-box - value=field.property - content=categoryPropertyTypes - onChange=(action (mut field.property)) - options=(hash - none="admin.wizard.selector.placeholder.property" - )}} -
-
- {{/if}} - -
-
- -
-
- {{input - name="key" - value=field.key - class="medium" - placeholderKey="admin.wizard.translation_placeholder"}} -
+ +
+ {{combo-box + value=field.property + content=categoryPropertyTypes + onChange=(action (mut field.property)) + options=(hash + none="admin.wizard.selector.placeholder.property" + )}}
- - {{#if validations}} - {{wizard-realtime-validations field=field validations=validations}} - {{/if}}
{{/if}} + +
+
+ + {{i18n "admin.wizard.pro.label"}} +
+
+ {{input + name="key" + value=field.key + class="medium" + placeholderKey="admin.wizard.translation_placeholder"}} +
+
+ + {{#if validations}} + {{wizard-realtime-validations field=field validations=validations}} + {{/if}} {{/if}} diff --git a/assets/javascripts/discourse/templates/components/wizard-pro-subscription.hbs b/assets/javascripts/discourse/templates/components/wizard-pro-subscription.hbs index 72631bfd..2bebc9ed 100644 --- a/assets/javascripts/discourse/templates/components/wizard-pro-subscription.hbs +++ b/assets/javascripts/discourse/templates/components/wizard-pro-subscription.hbs @@ -21,8 +21,11 @@ {{#if subscribed}}
{{stateLabel}}
-
- {{{i18n 'admin.wizard.pro.subscription.last_updated' updated_at=(format-date subscription.updated_at leaveAgo="true")}}} -
+ + {{#if subscription.updated_at}} +
+ {{{i18n 'admin.wizard.pro.subscription.last_updated' updated_at=(format-date subscription.updated_at leaveAgo="true")}}} +
+ {{/if}}
{{/if}} \ No newline at end of file diff --git a/assets/javascripts/discourse/templates/components/wizard-realtime-validations.hbs b/assets/javascripts/discourse/templates/components/wizard-realtime-validations.hbs index cd1298a9..0cc34f35 100644 --- a/assets/javascripts/discourse/templates/components/wizard-realtime-validations.hbs +++ b/assets/javascripts/discourse/templates/components/wizard-realtime-validations.hbs @@ -1,50 +1,54 @@ -

{{i18n "admin.wizard.field.validations.header"}}

- -
    - {{#each-in field.validations as |type props|}} -
  • - -

    {{i18n (concat "admin.wizard.field.validations." type)}}

    - {{input type="checkbox" checked=props.status}} - {{i18n "admin.wizard.field.validations.enabled"}} -
    -
    -
    -
    - +
    + + {{i18n "admin.wizard.pro.label"}} +
    +
    +
      + {{#each-in field.validations as |type props|}} +
    • + +

      {{i18n (concat "admin.wizard.field.validations." type)}}

      + {{input type="checkbox" checked=props.status}} + {{i18n "admin.wizard.field.validations.enabled"}} +
      +
      +
      +
      + +
      +
      + {{category-selector + categories=(get this (concat "validationBuffer." type ".categories")) + onChange=(action "updateValidationCategories" type props) + class="wizard"}} +
      -
      - {{category-selector - categories=(get this (concat "validationBuffer." type ".categories")) - onChange=(action "updateValidationCategories" type props) - class="wizard"}} +
      +
      + +
      +
      + {{input type="number" class="time-n-value" value=props.time_n_value}} + {{combo-box + value=(readonly props.time_unit) + content=timeUnits + class="time-unit-selector" + onChange=(action (mut props.time_unit))}} +
      +
      +
      +
      + +
      +
      + {{radio-button name=(concat type field.id) value="above" selection=props.position}} + {{i18n "admin.wizard.field.validations.above"}} + {{radio-button name=(concat type field.id) value="below" selection=props.position}} + {{i18n "admin.wizard.field.validations.below"}} +
      -
      -
      - -
      -
      - {{input type="number" class="time-n-value" value=props.time_n_value}} - {{combo-box - value=(readonly props.time_unit) - content=timeUnits - class="time-unit-selector" - onChange=(action (mut props.time_unit))}} -
      -
      -
      -
      - -
      -
      - {{radio-button name=(concat type field.id) value="above" selection=props.position}} - {{i18n "admin.wizard.field.validations.above"}} - {{radio-button name=(concat type field.id) value="below" selection=props.position}} - {{i18n "admin.wizard.field.validations.below"}} -
      -
      -
      -
    • - {{/each-in}} -
    +
  • + {{/each-in}} +
+
\ No newline at end of file diff --git a/assets/stylesheets/common/wizard-admin.scss b/assets/stylesheets/common/wizard-admin.scss index cca03c74..178dbc81 100644 --- a/assets/stylesheets/common/wizard-admin.scss +++ b/assets/stylesheets/common/wizard-admin.scss @@ -694,27 +694,59 @@ } } -.realtime-validations > ul { +.admin-wizard-container.settings .realtime-validations .setting-value > ul { list-style: none; margin: 0; + width: 100%; + display: flex; + flex-wrap: wrap; > li { background-color: var(--primary-low); padding: 1em; margin: 0 0 1em 0; + + .setting-title { + display: flex; + align-items: center; - input { - margin-bottom: 0; + h4 { + margin: 0 15px 0 0; + } + + input[type="checkbox"] { + margin: 0 5px 0 0; + } + } + + .setting-label { + width: 100px; + } + + .setting-value { + display: flex; + align-items: center; + + .input .select-kit, + > .select-kit { + max-width: unset !important; + } + + > span { + margin-right: 1em; + } } } } .validation-container { display: flex; + flex-direction: column; padding: 1em 0; .validation-section { - width: 250px; + min-width: 250px; + margin: .5em 0; } } diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 4a2bf28f..bfea46ce 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -192,7 +192,7 @@ en: label: "Format" instructions: "Moment.js format" validations: - header: "Realtime Validations" + header: "Validations" enabled: "Enabled" similar_topics: "Similar Topics" position: "Position" diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index 86968fda..fffa01cc 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -17,7 +17,7 @@ en: name_too_short: "'%{name}' is too short for a custom field name (min length is #{min_length})" name_already_taken: "'%{name}' is already taken as a custom field name" save_default: "Failed to save custom field '%{name}'" - pro_required: "PRO Actions require a PRO Subscription" + pro_type: "%{type} custom fields require PRO Subscription" field: too_short: "%{label} must be at least %{min} characters" @@ -50,7 +50,7 @@ en: required: "%{property} is required" conflict: "Wizard with id '%{wizard_id}' already exists" after_time: "After time setting is invalid" - pro: "%{property} is PRO only" + pro: "%{type} %{property} is PRO only" site_settings: custom_wizard_enabled: "Enable custom wizards." diff --git a/config/routes.rb b/config/routes.rb index 94cc5858..abe36479 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -47,7 +47,7 @@ Discourse::Application.routes.append do get 'admin/wizards/pro' => 'admin_pro#index' get 'admin/wizards/pro/authorize' => 'admin_pro#authorize' get 'admin/wizards/pro/authorize/callback' => 'admin_pro#authorize_callback' - delete 'admin/wizards/pro/authorize' => 'admin_pro#destroy' + delete 'admin/wizards/pro/authorize' => 'admin_pro#destroy_authentication' post 'admin/wizards/pro/subscription' => 'admin_pro#update_subscription' end end diff --git a/controllers/custom_wizard/admin/pro.rb b/controllers/custom_wizard/admin/pro.rb index 9f32045e..15e9e50f 100644 --- a/controllers/custom_wizard/admin/pro.rb +++ b/controllers/custom_wizard/admin/pro.rb @@ -4,27 +4,27 @@ class CustomWizard::AdminProController < CustomWizard::AdminController skip_before_action :check_xhr, :preload_json, :verify_authenticity_token, only: [:authorize, :authorize_callback] def index - render_serialized(CustomWizard::Pro.new, CustomWizard::ProSerializer, root: false) + render_serialized(pro, CustomWizard::ProSerializer, root: false) end def authorize request_id = SecureRandom.hex(32) cookies[:user_api_request_id] = request_id - redirect_to CustomWizard::Pro.auth_request(current_user.id, request_id).to_s + redirect_to pro.authentication_request(current_user.id, request_id).to_s end def authorize_callback payload = params[:payload] request_id = cookies[:user_api_request_id] - CustomWizard::Pro.auth_response(request_id, payload) - CustomWizard::Pro.update_subscription + pro.authentication_response(request_id, payload) + pro.update_subscription redirect_to '/admin/wizards/pro' end - def destroy - if CustomWizard::ProAuthentication.destroy + def destroy_authentication + if pro.destroy_authentication render json: success_json else render json: failed_json @@ -32,15 +32,17 @@ class CustomWizard::AdminProController < CustomWizard::AdminController end def update_subscription - if CustomWizard::Pro.update - render json: success_json.merge( - subscription: CustomWizard::ProSubscriptionSerializer.new( - CustomWizard::ProSubscription.new, - root: false - ) - ) + if pro.update_subscription + subscription = CustomWizard::ProSubscriptionSerializer.new(pro.subscription, root: false) + render json: success_json.merge(subscription: subscription) else render json: failed_json end end + + protected + + def pro + @pro ||= CustomWizard::Pro.new + end end \ No newline at end of file diff --git a/controllers/custom_wizard/wizard.rb b/controllers/custom_wizard/wizard.rb index e0cf669d..804fc422 100644 --- a/controllers/custom_wizard/wizard.rb +++ b/controllers/custom_wizard/wizard.rb @@ -5,6 +5,7 @@ class CustomWizard::WizardController < ::ApplicationController layout 'wizard' before_action :ensure_plugin_enabled + before_action :update_pro_subscription helper_method :wizard_page_title helper_method :wizard_theme_id helper_method :wizard_theme_lookup @@ -81,4 +82,8 @@ class CustomWizard::WizardController < ::ApplicationController redirect_to path("/") end end + + def update_pro_subscription + CustomWizard::Pro.update_subscription + end end diff --git a/jobs/scheduled/update_pro_status.rb b/jobs/scheduled/update_pro_subscription.rb similarity index 89% rename from jobs/scheduled/update_pro_status.rb rename to jobs/scheduled/update_pro_subscription.rb index 6f947304..d00b2560 100644 --- a/jobs/scheduled/update_pro_status.rb +++ b/jobs/scheduled/update_pro_subscription.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class CustomWizard::UpdateProSubscription < ::Jobs::Scheduled - every 10.minutes + every 1.hour def execute(args) CustomWizard::Pro.update_subscription diff --git a/lib/custom_wizard/action.rb b/lib/custom_wizard/action.rb index 1cadbb5b..74dc4680 100644 --- a/lib/custom_wizard/action.rb +++ b/lib/custom_wizard/action.rb @@ -14,15 +14,9 @@ class CustomWizard::Action @submission = opts[:submission] @log = [] @result = CustomWizard::ActionResult.new - @pro = CustomWizard::Pro.new end def perform - if pro_actions.include?(action['type']) && !@pro.subscribed? - log_error(I18n.t("wizard.custom_field.error.pro_required")) - return - end - ActiveRecord::Base.transaction do self.send(action['type'].to_sym) end diff --git a/lib/custom_wizard/builder.rb b/lib/custom_wizard/builder.rb index 78a960b9..2ecdf12d 100644 --- a/lib/custom_wizard/builder.rb +++ b/lib/custom_wizard/builder.rb @@ -6,7 +6,6 @@ class CustomWizard::Builder @template = CustomWizard::Template.create(wizard_id) return nil if @template.nil? @wizard = CustomWizard::Wizard.new(template.data, user) - @pro = CustomWizard::Pro.new end def self.sorted_handlers @@ -226,11 +225,6 @@ class CustomWizard::Builder end def check_condition(template) - unless @pro.subscribed? - CustomWizard::Log.create(I18n.t("wizard.custom_field.error.pro_required")) - return false - end - if template['condition'].present? result = CustomWizard::Mapper.new( inputs: template['condition'], diff --git a/lib/custom_wizard/custom_field.rb b/lib/custom_wizard/custom_field.rb index 9cc185ba..cc20ee95 100644 --- a/lib/custom_wizard/custom_field.rb +++ b/lib/custom_wizard/custom_field.rb @@ -37,6 +37,8 @@ class ::CustomWizard::CustomField send("#{attr}=", value) end end + + @pro = CustomWizard::Pro.new end def save @@ -92,6 +94,10 @@ class ::CustomWizard::CustomField add_error(I18n.t("#{i18n_key}.unsupported_type", type: value)) end + if attr == 'type' && value == 'json' && !@pro.subscribed? + add_error(I18n.t("wizard.custom_field.error.pro_type", type: value)) + end + if attr == 'name' unless value.is_a?(String) add_error(I18n.t("#{i18n_key}.name_invalid", name: value)) diff --git a/lib/custom_wizard/mapper.rb b/lib/custom_wizard/mapper.rb index 0c3543cf..b418b4b9 100644 --- a/lib/custom_wizard/mapper.rb +++ b/lib/custom_wizard/mapper.rb @@ -47,6 +47,7 @@ class CustomWizard::Mapper @data = params[:data] || {} @user = params[:user] @opts = params[:opts] || {} + @pro = CustomWizard::Pro.new end def perform @@ -251,7 +252,7 @@ class CustomWizard::Mapper end end - if opts[:template] + if @pro.subscribed? && opts[:template] template = Liquid::Template.parse(string) string = template.render(data) end diff --git a/lib/custom_wizard/pro.rb b/lib/custom_wizard/pro.rb index f54dd92d..3726e9b4 100644 --- a/lib/custom_wizard/pro.rb +++ b/lib/custom_wizard/pro.rb @@ -1,12 +1,22 @@ # frozen_string_literal: true class CustomWizard::Pro - attr_reader :authentication, - :subscription + include ActiveModel::Serialization + + attr_accessor :authentication, + :subscription def initialize - @authentication = CustomWizard::ProAuthentication.new - @subscription = CustomWizard::ProSubscription.new + @authentication = CustomWizard::ProAuthentication.new(get_authentication) + @subscription = CustomWizard::ProSubscription.new(get_subscription) + end + + def authorized? + @authentication.active? + end + + def subscribed? + @subscription.active? end def server @@ -21,12 +31,8 @@ class CustomWizard::Pro "custom-wizard" end - def authorized? - @authentication.active? - end - - def subscribed? - @subscription.active? + def scope + "discourse-subscription-server:user_subscription" end def update_subscription @@ -45,28 +51,35 @@ class CustomWizard::Pro return false end - return @subscription.update(data) + return false unless data && data.is_a?(Hash) + subscriptions = data[:subscriptions] + + if subscriptions.present? + subscription = subscriptions.first + type = subscription[:price_nickname] + + @subscription = set_subscription(type) + return true + end end end - @subscription.destroy - false + remove_subscription end - def destroy - @authentication.destroy + def destroy_subscription + remove_subscription end - def auth_request(user_id, request_id) + def authentication_request(user_id, request_id) keys = @authentication.generate_keys(user_id, request_id) - params = { public_key: keys.public_key, nonce: keys.nonce, client_id: @authentication.client_id, auth_redirect: "#{Discourse.base_url}/admin/wizards/pro/authorize/callback", application_name: SiteSetting.title, - scopes: "discourse-subscription-server:user_subscription" + scopes: scope } uri = URI.parse("https://#{server}/user-api-key/new") @@ -74,26 +87,24 @@ class CustomWizard::Pro uri.to_s end - def auth_response(request_id, payload) + def authentication_response(request_id, payload) data = @authentication.decrypt_payload(request_id, payload) return unless data.is_a?(Hash) && data[:key] && data[:user_id] - @authentication.update(data) + + api_key = data[:key] + user_id = data[:user_id] + user = User.find(user_id) + + if user&.admin + @authentication = set_authentication(api_key, user.id) + true + else + false + end end - def self.update - self.new.update - end - - def self.destroy - self.new.destroy - end - - def self.generate_request - self.new.generate_request - end - - def self.handle_response - self.new.handle_response + def destroy_authentication + remove_authentication end def self.subscribed? @@ -103,4 +114,56 @@ class CustomWizard::Pro def self.namespace "custom_wizard_pro" end + + private + + def subscription_db_key + "subscription" + end + + def authentication_db_key + "authentication" + end + + def get_subscription + raw = PluginStore.get(self.class.namespace, subscription_db_key) + + if raw.present? + OpenStruct.new( + type: raw['type'], + updated_at: raw['updated_at'] + ) + end + end + + def remove_subscription + PluginStore.remove(self.class.namespace, subscription_db_key) + end + + def set_subscription(type) + PluginStore.set(CustomWizard::Pro.namespace, subscription_db_key, type: type, updated_at: Time.now) + CustomWizard::ProSubscription.new(get_subscription) + end + + def get_authentication + raw = PluginStore.get(self.class.namespace, authentication_db_key) + OpenStruct.new( + key: raw && raw['key'], + auth_by: raw && raw['auth_by'], + auth_at: raw && raw['auth_at'] + ) + end + + def set_authentication(key, user_id) + PluginStore.set(self.class.namespace, authentication_db_key, + key: key, + auth_by: user_id, + auth_at: Time.now + ) + CustomWizard::ProAuthentication.new(get_authentication) + end + + def remove_authentication + PluginStore.remove(self.class.namespace, authentication_db_key) + end end \ No newline at end of file diff --git a/lib/custom_wizard/pro/authentication.rb b/lib/custom_wizard/pro/authentication.rb index 0d89ab06..0f3546eb 100644 --- a/lib/custom_wizard/pro/authentication.rb +++ b/lib/custom_wizard/pro/authentication.rb @@ -6,12 +6,13 @@ class CustomWizard::ProAuthentication :auth_at, :api_key - def initialize - api = get_api_key + def initialize(auth) + if auth + @api_key = auth.key + @auth_at = auth.auth_at + @auth_by = auth.auth_by + end - @api_key = api.key - @auth_at = api.auth_at - @auth_by = api.auth_by @client_id = get_client_id || set_client_id end @@ -19,22 +20,6 @@ class CustomWizard::ProAuthentication @api_key.present? end - def update(data) - api_key = data[:key] - user_id = data[:user_id] - user = User.find(user_id) - - if user&.admin - set_api_key(api_key, user.id) - else - false - end - end - - def destroy - remove - end - def generate_keys(user_id, request_id) rsa = OpenSSL::PKey::RSA.generate(2048) nonce = SecureRandom.hex(32) @@ -63,50 +48,15 @@ class CustomWizard::ProAuthentication data end - + private - def api_key_db_key - "api_key" - end - - def api_client_id_db_key - "api_client_id" - end - def keys_db_key "keys" end - def get_api_key - raw = PluginStore.get(CustomWizard::Pro.namespace, api_key_db_key) - OpenStruct.new( - key: raw && raw['key'], - auth_by: raw && raw['auth_by'], - auth_at: raw && raw['auth_at'] - ) - end - - def set_api_key(key, user_id) - PluginStore.set(CustomWizard::Pro.namespace, api_key_db_key, - key: key, - auth_by: user_id, - auth_at: Time.now - ) - end - - def remove - PluginStore.remove(CustomWizard::Pro.namespace, api_key_db_key) - end - - def get_client_id - PluginStore.get(CustomWizard::Pro.namespace, api_client_id_db_key) - end - - def set_client_id - client_id = SecureRandom.hex(32) - PluginStore.set(CustomWizard::Pro.namespace, api_client_id_db_key, client_id) - client_id + def client_id_db_key + "client_id" end def set_keys(request_id, user_id, rsa, nonce) @@ -129,4 +79,14 @@ class CustomWizard::ProAuthentication def delete_keys(request_id) PluginStore.remove(CustomWizard::Pro.namespace, "#{keys_db_key}_#{request_id}") end + + def get_client_id + PluginStore.get(CustomWizard::Pro.namespace, client_id_db_key) + end + + def set_client_id + client_id = SecureRandom.hex(32) + PluginStore.set(CustomWizard::Pro.namespace, client_id_db_key, client_id) + client_id + end end \ No newline at end of file diff --git a/lib/custom_wizard/pro/subscription.rb b/lib/custom_wizard/pro/subscription.rb index f82b58df..a5782357 100644 --- a/lib/custom_wizard/pro/subscription.rb +++ b/lib/custom_wizard/pro/subscription.rb @@ -4,12 +4,10 @@ class CustomWizard::ProSubscription attr_reader :type, :updated_at - def initialize - raw = get - - if raw - @type = raw['type'] - @updated_at = raw['updated_at'] + def initialize(subscription) + if subscription + @type = subscription.type + @updated_at = subscription.updated_at end end @@ -18,40 +16,6 @@ class CustomWizard::ProSubscription end def active? - types.include?(type) && updated_at.to_datetime > (Date.today - 15.minutes).to_datetime - end - - def update(data) - return false unless data && data.is_a?(Hash) - subscriptions = data[:subscriptions] - - if subscriptions.present? - subscription = subscriptions.first - type = subscription[:price_nickname] - - set(type) - end - end - - def destroy - remove - end - - private - - def key - "custom_wizard_pro_subscription" - end - - def set(type) - PluginStore.set(CustomWizard::Pro.namespace, key, type: type, updated_at: Time.now) - end - - def get - PluginStore.get(CustomWizard::Pro.namespace, key) - end - - def remove - PluginStore.remove(CustomWizard::Pro.namespace, key) + types.include?(type) && updated_at.to_datetime > (Time.zone.now - 2.hours).to_datetime end end \ No newline at end of file diff --git a/lib/custom_wizard/validators/template.rb b/lib/custom_wizard/validators/template.rb index deaa5119..180958ba 100644 --- a/lib/custom_wizard/validators/template.rb +++ b/lib/custom_wizard/validators/template.rb @@ -31,6 +31,7 @@ class CustomWizard::TemplateValidator if data[:actions].present? data[:actions].each do |action| + validate_pro_action(action) check_required(action, :action) end end @@ -53,15 +54,31 @@ class CustomWizard::TemplateValidator def self.pro { - step: ['condition'], - field: ['conition'] + wizard: {}, + step: { + condition: 'present', + index: 'conditional' + }, + field: { + condition: 'present', + index: 'conditional' + }, + action: { + type: %w[ + send_message + create_category + create_group + watch_categories + send_to_api + ] + } } end private def check_required(object, type) - CustomWizard::TemplateValidator.required[type].each do |property| + self.class.required[type].each do |property| if object[property].blank? errors.add :base, I18n.t("wizard.validation.required", property: property) end @@ -69,9 +86,13 @@ class CustomWizard::TemplateValidator end def validate_pro(object, type) - CustomWizard::TemplateValidator.required[type].each do |property| - if object[property].present? && !@pro.subscribed? - errors.add :base, I18n.t("wizard.validation.pro", property: property) + self.class.pro[type].each do |property, pro_type| + is_pro = (pro_type === 'present' && object[property].present?) || + (pro_type === 'conditional' && object[property].is_a?(Hash)) || + (pro_type.is_a?(Array) && pro_type.includes?(object[property])) + + if is_pro && @pro.subscribed? + errors.add :base, I18n.t("wizard.validation.pro", type: type.to_s, property: property) end end end diff --git a/plugin.rb b/plugin.rb index 7c056b5d..b2c6d54c 100644 --- a/plugin.rb +++ b/plugin.rb @@ -69,7 +69,7 @@ after_initialize do ../controllers/custom_wizard/realtime_validations.rb ../jobs/regular/refresh_api_access_token.rb ../jobs/regular/set_after_time_wizard.rb - ../jobs/scheduled/update_pro_status.rb + ../jobs/scheduled/update_pro_subscription.rb ../lib/custom_wizard/validators/template.rb ../lib/custom_wizard/validators/update.rb ../lib/custom_wizard/action_result.rb @@ -111,9 +111,9 @@ after_initialize do ../serializers/custom_wizard/log_serializer.rb ../serializers/custom_wizard/submission_serializer.rb ../serializers/custom_wizard/realtime_validation/similar_topics_serializer.rb - ../serializers/custom_wizard/pro_serializer.rb ../serializers/custom_wizard/pro/authentication_serializer.rb ../serializers/custom_wizard/pro/subscription_serializer.rb + ../serializers/custom_wizard/pro_serializer.rb ../extensions/extra_locales_controller.rb ../extensions/invites_controller.rb ../extensions/users_controller.rb @@ -126,18 +126,6 @@ after_initialize do Liquid::Template.register_filter(::CustomWizard::LiquidFilter::FirstNonEmpty) - class CustomWizard::UnpermittedOverride < StandardError; end - - CustomWizard.constants.each do |class_name| - klass = CustomWizard.const_get(class_name) - next if !klass.is_a?(Class) || klass.superclass.name.to_s.split("::").first == 'CustomWizard' - - klass.define_singleton_method(:prepend) { |klass| raise CustomWizard::UnpermittedOverride.new } - klass.define_singleton_method(:include) { |klass| raise CustomWizard::UnpermittedOverride.new } - klass.define_singleton_method(:define_method) { |name, &block| raise CustomWizard::UnpermittedOverride.new } - klass.define_singleton_method(:define_singleton_method) { |name, &block| raise CustomWizard::UnpermittedOverride.new } - end - add_class_method(:wizard, :user_requires_completion?) do |user| wizard_result = self.new(user).requires_completion? return wizard_result if wizard_result diff --git a/serializers/custom_wizard/pro_serializer.rb b/serializers/custom_wizard/pro_serializer.rb index 5b351f29..a9623974 100644 --- a/serializers/custom_wizard/pro_serializer.rb +++ b/serializers/custom_wizard/pro_serializer.rb @@ -1,26 +1,6 @@ # frozen_string_literal: true class CustomWizard::ProSerializer < ApplicationSerializer - attributes :server, - :authentication, - :subscription - - def server - CustomWizard::ProSubscription::SUBSCRIPTION_SERVER - end - - def authentication - if object.authentication - CustomWizard::ProAuthenticationSerializer.new(object.authentication, root: false) - else - nil - end - end - - def subscription - if object.subscription - CustomWizard::ProSubscriptionSerializer.new(object.subscription, root: false) - else - nil - end - end + attributes :server + has_one :authentication, serializer: CustomWizard::ProAuthenticationSerializer, embed: :objects + has_one :subscription, serializer: CustomWizard::ProSubscriptionSerializer, embed: :objects end \ No newline at end of file From 0e5fc756df74b61db0c2f5b967827c0b7a73fdfc Mon Sep 17 00:00:00 2001 From: angusmcleod Date: Wed, 1 Sep 2021 10:46:52 +0800 Subject: [PATCH 026/160] Fix merge issues --- lib/custom_wizard/submission.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/custom_wizard/submission.rb b/lib/custom_wizard/submission.rb index 050ec000..89de50bf 100644 --- a/lib/custom_wizard/submission.rb +++ b/lib/custom_wizard/submission.rb @@ -45,7 +45,7 @@ class CustomWizard::Submission validate submission_list = self.class.list(wizard, user_id: user.id) - submissions = submission_list.select { |submission| submission.id != self.id } + submissions = submission_list.submissions.select { |submission| submission.id != self.id } self.updated_at = Time.now.iso8601 submissions.push(self) @@ -112,7 +112,7 @@ class CustomWizard::Submission def self.cleanup_incomplete_submissions(wizard) user_id = wizard.user.id all_submissions = list(wizard, user_id: user_id) - sorted_submissions = all_submissions.sort_by do |submission| + sorted_submissions = all_submissions.submissions.sort_by do |submission| zero_epoch_time = DateTime.strptime("0", '%s') [ submission.submitted_at ? Time.iso8601(submission.submitted_at) : zero_epoch_time, @@ -132,7 +132,7 @@ class CustomWizard::Submission PluginStore.set("#{wizard.id}_#{KEY}", user_id, valid_data) end - def self.list(wizard, user_id: nil, order_by: nil) + def self.list(wizard, user_id: nil, order_by: nil, page: nil) params = { plugin_name: "#{wizard.id}_#{KEY}" } params[:key] = user_id if user_id.present? From 403f063b0f1c2799df47784f8f52f68fb19f11dc Mon Sep 17 00:00:00 2001 From: angusmcleod Date: Wed, 1 Sep 2021 11:10:49 +0800 Subject: [PATCH 027/160] Fix failing specs --- .../custom_wizard/submission_spec.rb | 69 ++++++++++--------- .../custom_wizard/wizard_controller_spec.rb | 4 +- 2 files changed, 39 insertions(+), 34 deletions(-) diff --git a/spec/components/custom_wizard/submission_spec.rb b/spec/components/custom_wizard/submission_spec.rb index a9caaa0a..4e8d86c0 100644 --- a/spec/components/custom_wizard/submission_spec.rb +++ b/spec/components/custom_wizard/submission_spec.rb @@ -13,39 +13,44 @@ describe CustomWizard::Submission do before do CustomWizard::Template.save(template_json, skip_jobs: true) - - template_json_2 = template_json.dup - template_json_2["id"] = "super_mega_fun_wizard_2" - CustomWizard::Template.save(template_json_2, skip_jobs: true) - @wizard = CustomWizard::Wizard.create(template_json["id"], user) - @wizard2 = CustomWizard::Wizard.create(template_json["id"], user2) - @wizard3 = CustomWizard::Wizard.create(template_json_2["id"], user) - @count = CustomWizard::Submission::PAGE_LIMIT + 20 - - @count.times do |index| - described_class.new(@wizard, step_1_field_1: "I am user submission #{index + 1}").save - end - described_class.new(@wizard2, step_1_field_1: "I am another user's submission").save - described_class.new(@wizard3, step_1_field_1: "I am a user submission on another wizard").save + described_class.new(@wizard, step_1_field_1: "I am user submission").save end it "saves a user's submission" do expect( described_class.get(@wizard, user.id).fields["step_1_field_1"] - ).to eq("I am user submission #{@count}") + ).to eq("I am user submission") end - it "list submissions by wizard" do - expect(described_class.list(@wizard).total).to eq(@count + 1) - end + context "#list" do + before do + template_json_2 = template_json.dup + template_json_2["id"] = "super_mega_fun_wizard_2" + CustomWizard::Template.save(template_json_2, skip_jobs: true) - it "list submissions by wizard and user" do - expect(described_class.list(@wizard, user_id: user.id).total).to eq(@count) - end + @wizard2 = CustomWizard::Wizard.create(template_json["id"], user2) + @wizard3 = CustomWizard::Wizard.create(template_json_2["id"], user) + @count = CustomWizard::Submission::PAGE_LIMIT + 20 - it "paginates submission lists" do - expect(described_class.list(@wizard, page: 1).submissions.size).to eq((@count + 1) - CustomWizard::Submission::PAGE_LIMIT) + @count.times do |index| + described_class.new(@wizard, step_1_field_1: "I am user submission #{index + 1}").save + end + described_class.new(@wizard2, step_1_field_1: "I am another user's submission").save + described_class.new(@wizard3, step_1_field_1: "I am a user submission on another wizard").save + end + + it "list submissions by wizard" do + expect(described_class.list(@wizard).total).to eq(@count + 2) + end + + it "list submissions by wizard and user" do + expect(described_class.list(@wizard, user_id: user.id).total).to eq(@count + 1) + end + + it "paginates submission lists" do + expect(described_class.list(@wizard, page: 1).submissions.size).to eq((@count + 2) - CustomWizard::Submission::PAGE_LIMIT) + end end context "#cleanup_incomplete_submissions" do @@ -54,10 +59,10 @@ describe CustomWizard::Submission do described_class.new(@wizard, step_1_field_1: "I am the second submission").save builder = CustomWizard::Builder.new(@wizard.id, @wizard.user) builder.build - sub_list = described_class.list(@wizard, user_id: @wizard.user.id) + submissions = described_class.list(@wizard, user_id: @wizard.user.id).submissions - expect(sub_list.length).to eq(1) - expect(sub_list.first.fields["step_1_field_1"]).to eq("I am the second submission") + expect(submissions.length).to eq(1) + expect(submissions.first.fields["step_1_field_1"]).to eq("I am the second submission") end it "handles submissions without 'updated_at' field correctly" do @@ -70,10 +75,10 @@ describe CustomWizard::Submission do PluginStore.set("#{@wizard.id}_submissions", @wizard.user.id, sub_data) builder = CustomWizard::Builder.new(@wizard.id, @wizard.user) builder.build - sub_list = described_class.list(@wizard, user_id: @wizard.user.id) + submissions = described_class.list(@wizard, user_id: @wizard.user.id).submissions - expect(sub_list.length).to eq(1) - expect(sub_list.first.fields["step_1_field_1"]).to eq("I am the third submission") + expect(submissions.length).to eq(1) + expect(submissions.first.fields["step_1_field_1"]).to eq("I am the third submission") end it "handles submissions with and without 'updated_at' field correctly" do @@ -87,10 +92,10 @@ describe CustomWizard::Submission do builder = CustomWizard::Builder.new(@wizard.id, @wizard.user) builder.build - sub_list = described_class.list(@wizard, user_id: @wizard.user.id) + submissions = described_class.list(@wizard, user_id: @wizard.user.id).submissions - expect(sub_list.length).to eq(1) - expect(sub_list.first.fields["step_1_field_1"]).to eq("I am the third submission") + expect(submissions.length).to eq(1) + expect(submissions.first.fields["step_1_field_1"]).to eq("I am the third submission") end end end diff --git a/spec/requests/custom_wizard/wizard_controller_spec.rb b/spec/requests/custom_wizard/wizard_controller_spec.rb index 87ff7efe..26f90040 100644 --- a/spec/requests/custom_wizard/wizard_controller_spec.rb +++ b/spec/requests/custom_wizard/wizard_controller_spec.rb @@ -84,9 +84,9 @@ describe CustomWizard::WizardController do CustomWizard::Submission.new(@wizard, step_1_field_1: "Hello World").save current_submission = @wizard.current_submission put '/w/super-mega-fun-wizard/skip.json' - list = CustomWizard::Submission.list(@wizard) + submissions = CustomWizard::Submission.list(@wizard).submissions - expect(list.any? { |submission| submission.id == current_submission.id }).to eq(false) + expect(submissions.any? { |submission| submission.id == current_submission.id }).to eq(false) end it "starts from the first step if user visits after skipping the wizard" do From 7b13605c7bb51f3bd3ca351b65f7f1726b6b64d4 Mon Sep 17 00:00:00 2001 From: Keegan George Date: Wed, 1 Sep 2021 12:46:39 -0700 Subject: [PATCH 028/160] FIX: Resolve linting issues --- .../discourse/components/submission-field.js.es6 | 9 ++++----- .../controllers/admin-wizards-submissions-columns.js.es6 | 2 +- .../controllers/admin-wizards-submissions-show.js.es6 | 6 +++--- assets/javascripts/discourse/models/custom-wizard.js.es6 | 5 ++--- 4 files changed, 10 insertions(+), 12 deletions(-) diff --git a/assets/javascripts/discourse/components/submission-field.js.es6 b/assets/javascripts/discourse/components/submission-field.js.es6 index f76376a5..ea9e34cf 100644 --- a/assets/javascripts/discourse/components/submission-field.js.es6 +++ b/assets/javascripts/discourse/components/submission-field.js.es6 @@ -64,21 +64,21 @@ export default Component.extend({ submittedUsers(value) { const isUserSelector = this.get("isUserSelector"); const users = []; - + if (isUserSelector) { const userData = value.value; const usernames = []; - if (userData.indexOf(',')) { + if (userData.indexOf(',')) { usernames.push(...userData.split(',')); usernames.forEach(u => { const user = { username: u, url: `/u/${u}` - } + }; users.push(user); - }) + }); } } return users; @@ -87,7 +87,6 @@ export default Component.extend({ @discourseComputed('value') userProfileUrl(value) { const isUser = this.get("isUser"); - const isUserSelector = this.get("isUserSelector"); if (isUser) { return `/u/${value.username}`; diff --git a/assets/javascripts/discourse/controllers/admin-wizards-submissions-columns.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards-submissions-columns.js.es6 index 5627dacf..981d1139 100644 --- a/assets/javascripts/discourse/controllers/admin-wizards-submissions-columns.js.es6 +++ b/assets/javascripts/discourse/controllers/admin-wizards-submissions-columns.js.es6 @@ -2,7 +2,7 @@ import Controller from "@ember/controller"; import ModalFunctionality from "discourse/mixins/modal-functionality"; export default Controller.extend(ModalFunctionality, { - + actions: { save() { this.send("closeModal"); diff --git a/assets/javascripts/discourse/controllers/admin-wizards-submissions-show.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards-submissions-show.js.es6 index 2667b6b2..450f7d53 100644 --- a/assets/javascripts/discourse/controllers/admin-wizards-submissions-show.js.es6 +++ b/assets/javascripts/discourse/controllers/admin-wizards-submissions-show.js.es6 @@ -27,8 +27,8 @@ export default Controller.extend({ this.set("loadingMore", false); }); }, - - + + @discourseComputed('submissions', 'fields.@each.enabled') displaySubmissions(submissions, fields) { let result = []; @@ -56,7 +56,7 @@ export default Controller.extend({ }, showEditColumnsModal() { - const controller = showModal("admin-wizards-submissions-columns", { + return showModal("admin-wizards-submissions-columns", { model: { fields: this.get('fields'), submissions: this.get('submissions') diff --git a/assets/javascripts/discourse/models/custom-wizard.js.es6 b/assets/javascripts/discourse/models/custom-wizard.js.es6 index d9a2bafa..660ba4ee 100644 --- a/assets/javascripts/discourse/models/custom-wizard.js.es6 +++ b/assets/javascripts/discourse/models/custom-wizard.js.es6 @@ -224,7 +224,6 @@ CustomWizard.reopenClass({ }) .then((result) => { if (result.wizard) { - console.log(result); let fields = [{ id: "username", label: "User"}]; let submissions = []; let wizard = result.wizard; @@ -248,8 +247,8 @@ CustomWizard.reopenClass({ let submittedAt = { id: "submitted_at", label: "Submitted At" - } - + }; + fields.push(submittedAt); return { From 9fc2092951bb22a2daf61883552c3dc13f680488 Mon Sep 17 00:00:00 2001 From: Keegan George Date: Thu, 2 Sep 2021 15:38:30 -0700 Subject: [PATCH 029/160] =?UTF-8?q?DEV:=20Run=20prettier=20=F0=9F=92=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/submission-field.js.es6 | 31 +++++++++---------- .../admin-wizards-submissions-show.js.es6 | 22 ++++++------- .../discourse/models/custom-wizard.js.es6 | 16 +++++----- .../admin-wizards-submissions-columns.hbs | 2 +- 4 files changed, 34 insertions(+), 37 deletions(-) diff --git a/assets/javascripts/discourse/components/submission-field.js.es6 b/assets/javascripts/discourse/components/submission-field.js.es6 index ea9e34cf..5ebc0ccd 100644 --- a/assets/javascripts/discourse/components/submission-field.js.es6 +++ b/assets/javascripts/discourse/components/submission-field.js.es6 @@ -1,10 +1,9 @@ -import { action } from "@ember/object"; import Component from "@ember/component"; +import { action } from "@ember/object"; import { equal } from "@ember/object/computed"; import discourseComputed from "discourse-common/utils/decorators"; import I18n from "I18n"; - export default Component.extend({ isText: equal("value.type", "text"), isComposer: equal("value.type", "composer"), @@ -25,7 +24,7 @@ export default Component.extend({ isTextArea: equal("value.type", "textarea"), isComposerPreview: equal("value.type", "composer_preview"), textState: "text-collapsed", - toggleText: I18n.t('admin.wizard.submissions.expand_text'), + toggleText: I18n.t("admin.wizard.submissions.expand_text"), @discourseComputed("value") checkboxValue(value) { @@ -45,14 +44,14 @@ export default Component.extend({ if (state === "text-collapsed") { this.set("textState", "text-expanded"); - this.set("toggleText", I18n.t('admin.wizard.submissions.collapse_text')); + this.set("toggleText", I18n.t("admin.wizard.submissions.collapse_text")); } else if (state === "text-expanded") { this.set("textState", "text-collapsed"); - this.set("toggleText", I18n.t('admin.wizard.submissions.expand_text')); + this.set("toggleText", I18n.t("admin.wizard.submissions.expand_text")); } }, - @discourseComputed('value') + @discourseComputed("value") file(value) { const isUpload = this.get("isUpload"); if (isUpload) { @@ -60,7 +59,7 @@ export default Component.extend({ } }, - @discourseComputed('value') + @discourseComputed("value") submittedUsers(value) { const isUserSelector = this.get("isUserSelector"); const users = []; @@ -69,13 +68,13 @@ export default Component.extend({ const userData = value.value; const usernames = []; - if (userData.indexOf(',')) { - usernames.push(...userData.split(',')); + if (userData.indexOf(",")) { + usernames.push(...userData.split(",")); - usernames.forEach(u => { + usernames.forEach((u) => { const user = { username: u, - url: `/u/${u}` + url: `/u/${u}`, }; users.push(user); }); @@ -84,7 +83,7 @@ export default Component.extend({ return users; }, - @discourseComputed('value') + @discourseComputed("value") userProfileUrl(value) { const isUser = this.get("isUser"); @@ -93,18 +92,18 @@ export default Component.extend({ } }, - @discourseComputed('value') + @discourseComputed("value") categoryUrl(value) { - const isCategory = this.get('isCategory'); + const isCategory = this.get("isCategory"); if (isCategory) { return `/c/${value.value}`; } }, - @discourseComputed('value') + @discourseComputed("value") groupUrl(value) { - const isGroup = this.get('isGroup'); + const isGroup = this.get("isGroup"); if (isGroup) { return `/g/${value.value}`; diff --git a/assets/javascripts/discourse/controllers/admin-wizards-submissions-show.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards-submissions-show.js.es6 index 450f7d53..7ba0050f 100644 --- a/assets/javascripts/discourse/controllers/admin-wizards-submissions-show.js.es6 +++ b/assets/javascripts/discourse/controllers/admin-wizards-submissions-show.js.es6 @@ -1,10 +1,9 @@ import Controller from "@ember/controller"; -import { fmt } from "discourse/lib/computed"; import { empty } from "@ember/object/computed"; -import CustomWizard from "../models/custom-wizard"; -import showModal from "discourse/lib/show-modal"; import discourseComputed from "discourse-common/utils/decorators"; - +import { fmt } from "discourse/lib/computed"; +import showModal from "discourse/lib/show-modal"; +import CustomWizard from "../models/custom-wizard"; export default Controller.extend({ downloadUrl: fmt("wizard.id", "/admin/wizards/submissions/%@/download"), @@ -28,16 +27,15 @@ export default Controller.extend({ }); }, - - @discourseComputed('submissions', 'fields.@each.enabled') + @discourseComputed("submissions", "fields.@each.enabled") displaySubmissions(submissions, fields) { let result = []; - submissions.forEach(submission => { + submissions.forEach((submission) => { let sub = {}; - Object.keys(submission).forEach(fieldId => { - if (fields.some(f => f.id === fieldId && f.enabled)) { + Object.keys(submission).forEach((fieldId) => { + if (fields.some((f) => f.id === fieldId && f.enabled)) { sub[fieldId] = submission[fieldId]; } }); @@ -58,9 +56,9 @@ export default Controller.extend({ showEditColumnsModal() { return showModal("admin-wizards-submissions-columns", { model: { - fields: this.get('fields'), - submissions: this.get('submissions') - } + fields: this.get("fields"), + submissions: this.get("submissions"), + }, }); }, }, diff --git a/assets/javascripts/discourse/models/custom-wizard.js.es6 b/assets/javascripts/discourse/models/custom-wizard.js.es6 index 660ba4ee..d0b49079 100644 --- a/assets/javascripts/discourse/models/custom-wizard.js.es6 +++ b/assets/javascripts/discourse/models/custom-wizard.js.es6 @@ -1,10 +1,10 @@ -import { ajax } from "discourse/lib/ajax"; import EmberObject from "@ember/object"; -import { buildProperties, mapped, present } from "../lib/wizard-json"; -import { listProperties, snakeCase } from "../lib/wizard"; -import wizardSchema from "../lib/wizard-schema"; -import { Promise } from "rsvp"; +import { ajax } from "discourse/lib/ajax"; import { popupAjaxError } from "discourse/lib/ajax-error"; +import { Promise } from "rsvp"; +import { listProperties, snakeCase } from "../lib/wizard"; +import { buildProperties, mapped, present } from "../lib/wizard-json"; +import wizardSchema from "../lib/wizard-schema"; const CustomWizard = EmberObject.extend({ save(opts) { @@ -224,7 +224,7 @@ CustomWizard.reopenClass({ }) .then((result) => { if (result.wizard) { - let fields = [{ id: "username", label: "User"}]; + let fields = [{ id: "username", label: "User" }]; let submissions = []; let wizard = result.wizard; let total = result.total; @@ -235,7 +235,7 @@ CustomWizard.reopenClass({ }; Object.keys(s.fields).forEach((fieldId) => { - if (!fields.some(field => field.id === fieldId)) { + if (!fields.some((field) => field.id === fieldId)) { fields.push({ id: fieldId, label: s.fields[fieldId].label }); } submission[fieldId] = s.fields[fieldId]; @@ -246,7 +246,7 @@ CustomWizard.reopenClass({ let submittedAt = { id: "submitted_at", - label: "Submitted At" + label: "Submitted At", }; fields.push(submittedAt); diff --git a/assets/javascripts/discourse/templates/modal/admin-wizards-submissions-columns.hbs b/assets/javascripts/discourse/templates/modal/admin-wizards-submissions-columns.hbs index a167a954..24743a92 100644 --- a/assets/javascripts/discourse/templates/modal/admin-wizards-submissions-columns.hbs +++ b/assets/javascripts/discourse/templates/modal/admin-wizards-submissions-columns.hbs @@ -29,4 +29,4 @@ label="directory.edit_columns.reset_to_default" action=(action "resetToDefault") }} -
+
\ No newline at end of file From 1e38001942fc13e5e379d6415dd1033fb49d9d89 Mon Sep 17 00:00:00 2001 From: Keegan George Date: Thu, 2 Sep 2021 15:41:55 -0700 Subject: [PATCH 030/160] =?UTF-8?q?DEV:=20Run=20Prettier=20=F0=9F=92=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controllers/admin-wizards-submissions-columns.js.es6 | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/assets/javascripts/discourse/controllers/admin-wizards-submissions-columns.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards-submissions-columns.js.es6 index 981d1139..4af487ee 100644 --- a/assets/javascripts/discourse/controllers/admin-wizards-submissions-columns.js.es6 +++ b/assets/javascripts/discourse/controllers/admin-wizards-submissions-columns.js.es6 @@ -2,15 +2,14 @@ import Controller from "@ember/controller"; import ModalFunctionality from "discourse/mixins/modal-functionality"; export default Controller.extend(ModalFunctionality, { - actions: { save() { this.send("closeModal"); }, resetToDefault() { - this.get('model.fields').forEach(field => { + this.get("model.fields").forEach((field) => { field.set("enabled", true); }); - } - } -}); \ No newline at end of file + }, + }, +}); From 4abe30464c1b29ff2234aab1bc0387022e7c07a4 Mon Sep 17 00:00:00 2001 From: Keegan George Date: Thu, 2 Sep 2021 15:43:34 -0700 Subject: [PATCH 031/160] =?UTF-8?q?DEV:=20Run=20Prettier=F0=9F=92=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin-wizards-submissions-show.hbs | 32 +++++++++++++------ 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/assets/javascripts/discourse/templates/admin-wizards-submissions-show.hbs b/assets/javascripts/discourse/templates/admin-wizards-submissions-show.hbs index 2f0689b1..6f4996f6 100644 --- a/assets/javascripts/discourse/templates/admin-wizards-submissions-show.hbs +++ b/assets/javascripts/discourse/templates/admin-wizards-submissions-show.hbs @@ -1,16 +1,24 @@ {{#if submissions}}
- +
{{d-button - icon="sliders-h" - label="admin.wizard.submissions.edit_columns" + icon="sliders-h" + label="admin.wizard.submissions.edit_columns" action=(action "showEditColumnsModal") - class="btn-default open-edit-columns-btn download-link"}} + class="btn-default open-edit-columns-btn download-link" + }}
- + {{d-icon "download"}} {{i18n "admin.wizard.submissions.download"}} @@ -21,14 +29,18 @@
{{#load-more selector=".wizard-submissions tr" action=(action "loadMore")}} {{#if noResults}} -

{{i18n "search.no_results"}}

+

+ {{i18n "search.no_results"}} +

{{else}}
{{f}}{{field.label}}
{{v}}{{submission-field fieldName=field value=value}}
{{#each fields as |field|}} {{#if field.enabled}} - + {{/if}} {{/each}} @@ -37,7 +49,9 @@ {{#each displaySubmissions as |submission|}} {{#each-in submission as |field value|}} - + {{/each-in}} {{/each}} @@ -48,4 +62,4 @@ {{conditional-loading-spinner condition=loadingMore}} {{/load-more}} -{{/if}} +{{/if}} \ No newline at end of file From c60d1e33386fa48cc31dde63ffca0f3a4f51bad9 Mon Sep 17 00:00:00 2001 From: Keegan George Date: Thu, 2 Sep 2021 15:46:49 -0700 Subject: [PATCH 032/160] =?UTF-8?q?DEV:=20Run=20Prettier=20=F0=9F=92=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../templates/components/submission-field.hbs | 97 ++++++++++++++----- 1 file changed, 73 insertions(+), 24 deletions(-) diff --git a/assets/javascripts/discourse/templates/components/submission-field.hbs b/assets/javascripts/discourse/templates/components/submission-field.hbs index 52cde1dd..831dcc3a 100644 --- a/assets/javascripts/discourse/templates/components/submission-field.hbs +++ b/assets/javascripts/discourse/templates/components/submission-field.hbs @@ -4,20 +4,35 @@ {{#if isTextArea}} {{/if}} {{#if isComposer}}
-

{{value.value}}

- {{toggleText}} +

+ {{value.value}} +

+ + {{toggleText}} +
{{/if}} {{#if isComposerPreview}} - {{d-icon "comment-alt" }} {{i18n "admin.wizard.submissions.composer_preview"}}: {{value.value}} + {{d-icon "comment-alt"}} + + {{i18n "admin.wizard.submissions.composer_preview"}}: {{value.value}} + {{/if}} {{#if isTextOnly}} @@ -48,35 +63,41 @@ {{#if isCheckbox}} {{#if checkboxValue}} - - {{d-icon "check"}}{{value.value}} - + + {{d-icon "check"}}{{value.value}} + {{else}} - - {{d-icon "times"}}{{value.value}} - + + {{d-icon "times"}}{{value.value}} + {{/if}} {{/if}} {{#if isUrl}} - {{ d-icon "link" }} - + {{d-icon "link"}} + {{value.value}} {{/if}} {{#if isUpload}} - + {{file.original_filename}} {{/if}} {{#if isDropdown}} - {{ d-icon "check-square" }} - {{ value.value }} + {{d-icon "check-square"}} + {{value.value}} {{/if}} @@ -87,28 +108,56 @@ {{/if}} {{#if isCategory}} - {{i18n "admin.wizard.submissions.category_id"}}: {{value.value}} + + {{i18n "admin.wizard.submissions.category_id"}}: + + + {{value.value}} + {{/if}} {{#if isGroup}} - {{i18n "admin.wizard.submissions.group_id"}}: {{ value.value }} + + {{i18n "admin.wizard.submissions.group_id"}}: + + {{value.value}} {{/if}} - {{#if isUserSelector}} {{#each submittedUsers as |user|}} - {{ d-icon "user" }} - {{user.username}} + {{d-icon "user"}} + + {{user.username}} + {{/each}} {{/if}} {{#if isUser}} - {{#link-to "user" value}}{{avatar value imageSize="tiny"}}{{/link-to}} - {{value.username}} + {{#link-to "user" value}} + {{avatar value imageSize="tiny"}} + {{/link-to}} + + {{value.username}} + {{/if}} {{#if isSubmittedAt}} {{d-icon "clock"}}{{format-date value format="tiny"}} -{{/if}} +{{/if}} \ No newline at end of file From 6ef333a6573df53b67c50951f383f32aeb19b4c8 Mon Sep 17 00:00:00 2001 From: angusmcleod Date: Fri, 3 Sep 2021 16:46:32 +0800 Subject: [PATCH 033/160] Complete pro-feature functionality --- .../components/custom-field-input.js.es6 | 42 ++-- .../components/wizard-advanced-toggle.js.es6 | 21 -- .../components/wizard-custom-action.js.es6 | 25 ++- .../components/wizard-custom-field.js.es6 | 1 - .../components/wizard-pro-selector.js.es6 | 19 ++ .../wizard-pro-selector-header.js.es6 | 17 ++ .../wizard-pro-selector-row.js.es6 | 3 + .../admin-wizards-wizard-show.js.es6 | 5 - .../discourse/lib/wizard-json.js.es6 | 15 -- .../discourse/lib/wizard-schema.js.es6 | 18 +- .../routes/admin-wizards-custom-fields.js.es6 | 9 +- .../templates/admin-wizards-custom-fields.hbs | 3 +- .../templates/admin-wizards-wizard-show.hbs | 47 ++--- .../components/custom-field-input.hbs | 4 +- .../components/wizard-advanced-toggle.hbs | 4 - .../components/wizard-custom-action.hbs | 184 +++++++++--------- .../components/wizard-custom-field.hbs | 3 +- .../components/wizard-custom-step.hbs | 104 +++++----- .../wizard-pro-selector-header.hbs | 15 ++ .../wizard-pro-selector-row.hbs | 15 ++ assets/stylesheets/common/wizard-admin.scss | 34 ++-- config/locales/client.en.yml | 4 +- .../custom_wizard/admin/custom_fields.rb | 5 +- lib/custom_wizard/custom_field.rb | 8 +- lib/custom_wizard/pro.rb | 2 +- lib/custom_wizard/validators/template.rb | 2 +- 26 files changed, 329 insertions(+), 280 deletions(-) delete mode 100644 assets/javascripts/discourse/components/wizard-advanced-toggle.js.es6 create mode 100644 assets/javascripts/discourse/components/wizard-pro-selector.js.es6 create mode 100644 assets/javascripts/discourse/components/wizard-pro-selector/wizard-pro-selector-header.js.es6 create mode 100644 assets/javascripts/discourse/components/wizard-pro-selector/wizard-pro-selector-row.js.es6 delete mode 100644 assets/javascripts/discourse/templates/components/wizard-advanced-toggle.hbs create mode 100644 assets/javascripts/discourse/templates/components/wizard-pro-selector/wizard-pro-selector-header.hbs create mode 100644 assets/javascripts/discourse/templates/components/wizard-pro-selector/wizard-pro-selector-row.hbs diff --git a/assets/javascripts/discourse/components/custom-field-input.js.es6 b/assets/javascripts/discourse/components/custom-field-input.js.es6 index e49c6f1d..c389fb62 100644 --- a/assets/javascripts/discourse/components/custom-field-input.js.es6 +++ b/assets/javascripts/discourse/components/custom-field-input.js.es6 @@ -1,13 +1,29 @@ import Component from "@ember/component"; import discourseComputed, { observes } from "discourse-common/utils/decorators"; import { alias, equal, or } from "@ember/object/computed"; +import { computed } from "@ember/object"; import I18n from "I18n"; -const generateContent = function (array, type) { - return array.map((key) => ({ - id: key, - name: I18n.t(`admin.wizard.custom_field.${type}.${key}`), - })); +const klasses = ["topic", "post", "group", "category"]; +const types = ["string", "boolean", "integer", "json"]; +const proTypes = { + klass: ["group", "category"], + type: ["json"] +} + +const generateContent = function (array, type, proSubscribed = false) { + return array.reduce((result, key) => { + let proArr = proTypes[type]; + let pro = proArr && proArr.includes(key); + if (!pro || proSubscribed) { + result.push({ + id: key, + name: I18n.t(`admin.wizard.custom_field.${type}.${key}`), + pro + }); + } + return result; + }, []); }; export default Component.extend({ @@ -16,14 +32,12 @@ export default Component.extend({ postSerializers: ["post"], groupSerializers: ["basic_group"], categorySerializers: ["basic_category"], - klassContent: generateContent( - ["topic", "post", "group", "category"], - "klass" - ), - typeContent: generateContent( - ["string", "boolean", "integer", "json"], - "type" - ), + klassContent: computed("proSubscribed", function() { + return generateContent(klasses, "klass", this.proSubscribed); + }), + typeContent: computed("proSubscribed", function() { + return generateContent(types, "type", this.proSubscribed); + }), showInputs: or("field.new", "field.edit"), classNames: ["custom-field-input"], loading: or("saving", "destroying"), @@ -40,7 +54,7 @@ export default Component.extend({ const serializers = this.get(`${klass}Serializers`); if (serializers) { - return generateContent(serializers, "serializers"); + return generateContent(serializers, "serializers", this.proSubscribed); } else { return []; } diff --git a/assets/javascripts/discourse/components/wizard-advanced-toggle.js.es6 b/assets/javascripts/discourse/components/wizard-advanced-toggle.js.es6 deleted file mode 100644 index c6e1fd9c..00000000 --- a/assets/javascripts/discourse/components/wizard-advanced-toggle.js.es6 +++ /dev/null @@ -1,21 +0,0 @@ -import { default as discourseComputed } from "discourse-common/utils/decorators"; -import Component from "@ember/component"; - -export default Component.extend({ - classNames: "wizard-advanced-toggle", - - @discourseComputed("showAdvanced") - toggleClass(showAdvanced) { - let classes = "btn"; - if (showAdvanced) { - classes += " btn-primary"; - } - return classes; - }, - - actions: { - toggleAdvanced() { - this.toggleProperty("showAdvanced"); - }, - }, -}); diff --git a/assets/javascripts/discourse/components/wizard-custom-action.js.es6 b/assets/javascripts/discourse/components/wizard-custom-action.js.es6 index feb83754..a04ff563 100644 --- a/assets/javascripts/discourse/components/wizard-custom-action.js.es6 +++ b/assets/javascripts/discourse/components/wizard-custom-action.js.es6 @@ -1,8 +1,8 @@ import { default as discourseComputed } from "discourse-common/utils/decorators"; +import wizardSchema from "discourse/plugins/discourse-custom-wizard/discourse/lib/wizard-schema"; import { and, empty, equal, or } from "@ember/object/computed"; import { notificationLevels, selectKitContent } from "../lib/wizard"; import { computed } from "@ember/object"; -import wizardSchema from "../lib/wizard-schema"; import UndoChanges from "../mixins/undo-changes"; import Component from "@ember/component"; import I18n from "I18n"; @@ -25,8 +25,6 @@ export default Component.extend(UndoChanges, { createGroup: equal("action.type", "create_group"), apiEmpty: empty("action.api"), groupPropertyTypes: selectKitContent(["id", "name"]), - hasAdvanced: or("hasCustomFields", "routeTo"), - showAdvanced: and("hasAdvanced", "action.type"), hasCustomFields: or( "basicTopicFields", "updateProfile", @@ -36,12 +34,6 @@ export default Component.extend(UndoChanges, { basicTopicFields: or("createTopic", "sendMessage", "openComposer"), publicTopicFields: or("createTopic", "openComposer"), showPostAdvanced: or("createTopic", "sendMessage"), - actionTypes: Object.keys(wizardSchema.action.types).map((type) => { - return { - id: type, - name: I18n.t(`admin.wizard.action.${type}.label`), - }; - }), availableNotificationLevels: notificationLevels.map((type) => { return { id: type, @@ -101,4 +93,19 @@ export default Component.extend(UndoChanges, { } return apis.find((a) => a.name === api).endpoints; }, + + @discourseComputed("proSubscribed") + actionTypes(proSubscribed) { + return Object.keys(wizardSchema.action.types).reduce((result, type) => { + let pro = wizardSchema.action.proTypes.includes(type); + if (proSubscribed || !pro) { + result.push({ + id: type, + name: I18n.t(`admin.wizard.action.${type}.label`), + pro + }); + } + return result; + }, []); + } }); diff --git a/assets/javascripts/discourse/components/wizard-custom-field.js.es6 b/assets/javascripts/discourse/components/wizard-custom-field.js.es6 index b5c10c65..807f90cd 100644 --- a/assets/javascripts/discourse/components/wizard-custom-field.js.es6 +++ b/assets/javascripts/discourse/components/wizard-custom-field.js.es6 @@ -27,7 +27,6 @@ export default Component.extend(UndoChanges, { isTextType: or("isText", "isTextarea", "isComposer"), isComposerPreview: equal("field.type", "composer_preview"), categoryPropertyTypes: selectKitContent(["id", "slug"]), - showAdvanced: alias("field.type"), messageUrl: "https://thepavilion.io/t/2809", @discourseComputed("field.type") diff --git a/assets/javascripts/discourse/components/wizard-pro-selector.js.es6 b/assets/javascripts/discourse/components/wizard-pro-selector.js.es6 new file mode 100644 index 00000000..b06b5943 --- /dev/null +++ b/assets/javascripts/discourse/components/wizard-pro-selector.js.es6 @@ -0,0 +1,19 @@ +import SingleSelectComponent from "select-kit/components/single-select"; +import { computed } from "@ember/object"; + +export default SingleSelectComponent.extend({ + classNames: ["combo-box", 'wizard-pro-selector'], + + selectKitOptions: { + autoFilterable: false, + filterable: false, + showFullTitle: true, + headerComponent: "wizard-pro-selector/wizard-pro-selector-header", + caretUpIcon: "caret-up", + caretDownIcon: "caret-down" + }, + + modifyComponentForRow() { + return "wizard-pro-selector/wizard-pro-selector-row"; + } +}); \ No newline at end of file diff --git a/assets/javascripts/discourse/components/wizard-pro-selector/wizard-pro-selector-header.js.es6 b/assets/javascripts/discourse/components/wizard-pro-selector/wizard-pro-selector-header.js.es6 new file mode 100644 index 00000000..c1f7251c --- /dev/null +++ b/assets/javascripts/discourse/components/wizard-pro-selector/wizard-pro-selector-header.js.es6 @@ -0,0 +1,17 @@ +import SingleSelectHeaderComponent from "select-kit/components/select-kit/single-select-header"; +import { computed } from "@ember/object"; +import { reads } from "@ember/object/computed"; + +export default SingleSelectHeaderComponent.extend({ + classNames: ["combo-box-header", "wizard-pro-selector-header"], + caretUpIcon: reads("selectKit.options.caretUpIcon"), + caretDownIcon: reads("selectKit.options.caretDownIcon"), + caretIcon: computed( + "selectKit.isExpanded", + "caretUpIcon", + "caretDownIcon", + function () { + return this.selectKit.isExpanded ? this.caretUpIcon : this.caretDownIcon; + } + ), +}); diff --git a/assets/javascripts/discourse/components/wizard-pro-selector/wizard-pro-selector-row.js.es6 b/assets/javascripts/discourse/components/wizard-pro-selector/wizard-pro-selector-row.js.es6 new file mode 100644 index 00000000..9640cd0c --- /dev/null +++ b/assets/javascripts/discourse/components/wizard-pro-selector/wizard-pro-selector-row.js.es6 @@ -0,0 +1,3 @@ +import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row"; + +export default SelectKitRowComponent.extend(); \ No newline at end of file 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 edb06ad9..a4be0667 100644 --- a/assets/javascripts/discourse/controllers/admin-wizards-wizard-show.js.es6 +++ b/assets/javascripts/discourse/controllers/admin-wizards-wizard-show.js.es6 @@ -77,7 +77,6 @@ export default Controller.extend({ wizard .save(opts) .then((result) => { - console.log(result) if (result.wizard_id) { this.send("afterSave", result.wizard_id); } else if (result.errors) { @@ -119,10 +118,6 @@ export default Controller.extend({ controller.setup(); }, - toggleAdvanced() { - this.toggleProperty("wizard.showAdvanced"); - }, - copyUrl() { const $copyRange = $('

'); $copyRange.html(this.wizardUrl); diff --git a/assets/javascripts/discourse/lib/wizard-json.js.es6 b/assets/javascripts/discourse/lib/wizard-json.js.es6 index 79da60cb..95eaba49 100644 --- a/assets/javascripts/discourse/lib/wizard-json.js.es6 +++ b/assets/javascripts/discourse/lib/wizard-json.js.es6 @@ -97,11 +97,6 @@ function buildObjectArray(json, type) { if (present(json)) { json.forEach((objJson, objectIndex) => { let object = buildObject(objJson, type, objectIndex); - - if (hasAdvancedProperties(object, type)) { - object.set("showAdvanced", true); - } - array.pushObject(object); }); } @@ -112,21 +107,11 @@ function buildObjectArray(json, type) { function buildBasicProperties(json, type, props, objectIndex = null) { listProperties(type).forEach((p) => { props[p] = buildProperty(json, p, type, objectIndex); - - if (hasAdvancedProperties(json, type)) { - props.showAdvanced = true; - } }); return props; } -function hasAdvancedProperties(object, type) { - return Object.keys(object).some((p) => { - return wizardSchema[type].advanced.indexOf(p) > -1 && present(object[p]); - }); -} - /// to be removed: necessary due to action array being moved from step to wizard function actionPatch(json) { let actions = json.actions || []; diff --git a/assets/javascripts/discourse/lib/wizard-schema.js.es6 b/assets/javascripts/discourse/lib/wizard-schema.js.es6 index 8b8200b0..613e6569 100644 --- a/assets/javascripts/discourse/lib/wizard-schema.js.es6 +++ b/assets/javascripts/discourse/lib/wizard-schema.js.es6 @@ -18,7 +18,6 @@ const wizard = { permitted: null, }, mapped: ["permitted"], - advanced: ["restart_on_revisit"], required: ["id"], dependent: { after_time: "after_time_scheduled", @@ -50,7 +49,6 @@ const step = { force_final: false, }, mapped: ["required_data", "permitted_params", "condition", "index"], - advanced: ["required_data", "permitted_params", "condition", "index"], required: ["id"], dependent: {}, objectArrays: { @@ -68,6 +66,7 @@ const field = { label: null, image: null, description: null, + property: null, required: null, key: null, type: null, @@ -75,7 +74,6 @@ const field = { }, types: {}, mapped: ["prefill", "content", "condition", "index"], - advanced: ["property", "key", "condition", "index"], required: ["id", "type"], dependent: {}, objectArrays: {}, @@ -196,14 +194,14 @@ const action = { "visibility_level", "members_visibility_level", ], - advanced: [ - "code", - "custom_fields", - "skip_redirect", - "suppress_notifications", - "required", - ], required: ["id", "type"], + proTypes: [ + 'send_message', + 'create_category', + 'create_group', + 'watch_categories', + 'send_to_api' + ], dependent: {}, objectArrays: {}, }; diff --git a/assets/javascripts/discourse/routes/admin-wizards-custom-fields.js.es6 b/assets/javascripts/discourse/routes/admin-wizards-custom-fields.js.es6 index a1c625ad..198f9a14 100644 --- a/assets/javascripts/discourse/routes/admin-wizards-custom-fields.js.es6 +++ b/assets/javascripts/discourse/routes/admin-wizards-custom-fields.js.es6 @@ -8,7 +8,12 @@ export default DiscourseRoute.extend({ }, setupController(controller, model) { - const customFields = A(model || []); - controller.set("customFields", customFields); + const customFields = A(model.custom_fields || []); + const proSubscribed = model.pro_subscribed; + + controller.setProperties({ + customFields, + proSubscribed + }); }, }); diff --git a/assets/javascripts/discourse/templates/admin-wizards-custom-fields.hbs b/assets/javascripts/discourse/templates/admin-wizards-custom-fields.hbs index 10501498..09a0d569 100644 --- a/assets/javascripts/discourse/templates/admin-wizards-custom-fields.hbs +++ b/assets/javascripts/discourse/templates/admin-wizards-custom-fields.hbs @@ -32,7 +32,8 @@ {{custom-field-input field=field removeField=(action "removeField") - saveField=(action "saveField")}} + saveField=(action "saveField") + proSubscribed=proSubscribed}} {{/each}}
{{field.label}} + {{field.label}} +
{{submission-field fieldName=field value=value}} + {{submission-field fieldName=field value=value}} +
diff --git a/assets/javascripts/discourse/templates/admin-wizards-wizard-show.hbs b/assets/javascripts/discourse/templates/admin-wizards-wizard-show.hbs index 9d91cb0b..7645c20e 100644 --- a/assets/javascripts/discourse/templates/admin-wizards-wizard-show.hbs +++ b/assets/javascripts/discourse/templates/admin-wizards-wizard-show.hbs @@ -126,33 +126,25 @@
- {{wizard-advanced-toggle showAdvanced=wizard.showAdvanced}} - - {{#if wizard.showAdvanced}} -
- -
-
- -
-
- {{input type="checkbox" checked=wizard.save_submissions}} - {{i18n "admin.wizard.save_submissions_label"}} -
-
- -
-
- -
-
- {{input type="checkbox" checked=wizard.restart_on_revisit}} - {{i18n "admin.wizard.restart_on_revisit_label"}} -
-
- +
+
+
- {{/if}} +
+ {{input type="checkbox" checked=wizard.save_submissions}} + {{i18n "admin.wizard.save_submissions_label"}} +
+
+ +
+
+ +
+
+ {{input type="checkbox" checked=wizard.restart_on_revisit}} + {{i18n "admin.wizard.restart_on_revisit_label"}} +
+
{{wizard-links @@ -183,7 +175,8 @@ wizard=wizard apis=apis removeAction="removeAction" - wizardFields=wizardFields}} + wizardFields=wizardFields + proSubscribed=proSubscribed}} {{/each}}
diff --git a/assets/javascripts/discourse/templates/components/custom-field-input.hbs b/assets/javascripts/discourse/templates/components/custom-field-input.hbs index 43a97be8..2bb9acce 100644 --- a/assets/javascripts/discourse/templates/components/custom-field-input.hbs +++ b/assets/javascripts/discourse/templates/components/custom-field-input.hbs @@ -1,13 +1,13 @@ {{#if showInputs}} - {{combo-box + {{wizard-pro-selector value=field.klass content=klassContent none="admin.wizard.custom_field.klass.select" onChange=(action (mut field.klass))}} - {{combo-box + {{wizard-pro-selector value=field.type content=typeContent none="admin.wizard.custom_field.type.select" diff --git a/assets/javascripts/discourse/templates/components/wizard-advanced-toggle.hbs b/assets/javascripts/discourse/templates/components/wizard-advanced-toggle.hbs deleted file mode 100644 index ec2bcb76..00000000 --- a/assets/javascripts/discourse/templates/components/wizard-advanced-toggle.hbs +++ /dev/null @@ -1,4 +0,0 @@ -{{d-button - action="toggleAdvanced" - label="admin.wizard.advanced" - class=toggleClass}} diff --git a/assets/javascripts/discourse/templates/components/wizard-custom-action.hbs b/assets/javascripts/discourse/templates/components/wizard-custom-action.hbs index 4c645cf7..f5d53200 100644 --- a/assets/javascripts/discourse/templates/components/wizard-custom-action.hbs +++ b/assets/javascripts/discourse/templates/components/wizard-custom-action.hbs @@ -12,13 +12,14 @@
- {{combo-box + {{wizard-pro-selector value=action.type content=actionTypes onChange=(action "changeType") options=(hash none="admin.wizard.select_type" - )}} + ) + }}
@@ -714,99 +715,90 @@
{{/if}} -{{#if showAdvanced}} - {{wizard-advanced-toggle showAdvanced=action.showAdvanced}} - - {{#if action.showAdvanced}} -
- - {{#if hasCustomFields}} -
-
- -
- -
- {{wizard-mapper - inputs=action.custom_fields - property="custom_fields" - onUpdate=(action "mappedFieldUpdated") - options=(hash - inputTypes="association" - customFieldSelection="key" - wizardFieldSelection="value" - wizardActionSelection="value" - userFieldSelection="value" - keyPlaceholder="admin.wizard.action.custom_fields.key" - context=customFieldsContext - )}} -
-
- {{/if}} - - {{#if sendMessage}} -
-
- -
- -
- {{wizard-mapper - inputs=action.required - property="required" - onUpdate=(action "mappedFieldUpdated") - options=(hash - textSelection="value" - wizardFieldSelection=true - userFieldSelection=true - groupSelection=true - context="action" - )}} -
-
- {{/if}} - - {{#if showPostAdvanced}} -
-
- -
- -
- {{input type="checkbox" checked=action.skip_redirect}} - - - {{i18n "admin.wizard.action.skip_redirect.description" type="topic"}} - -
-
- -
-
- -
- -
- {{input type="checkbox" checked=action.suppress_notifications}} - - - {{i18n "admin.wizard.action.suppress_notifications.description" type="topic"}} - -
-
- {{/if}} - - {{#if routeTo}} -
-
- -
- -
- {{input value=action.code}} -
-
- {{/if}} +{{#if hasCustomFields}} +
+
+
- {{/if}} + +
+ {{wizard-mapper + inputs=action.custom_fields + property="custom_fields" + onUpdate=(action "mappedFieldUpdated") + options=(hash + inputTypes="association" + customFieldSelection="key" + wizardFieldSelection="value" + wizardActionSelection="value" + userFieldSelection="value" + keyPlaceholder="admin.wizard.action.custom_fields.key" + context=customFieldsContext + )}} +
+
+{{/if}} + +{{#if sendMessage}} +
+
+ +
+ +
+ {{wizard-mapper + inputs=action.required + property="required" + onUpdate=(action "mappedFieldUpdated") + options=(hash + textSelection="value" + wizardFieldSelection=true + userFieldSelection=true + groupSelection=true + context="action" + )}} +
+
+{{/if}} + +{{#if showPostAdvanced}} +
+
+ +
+ +
+ {{input type="checkbox" checked=action.skip_redirect}} + + + {{i18n "admin.wizard.action.skip_redirect.description" type="topic"}} + +
+
+ +
+
+ +
+ +
+ {{input type="checkbox" checked=action.suppress_notifications}} + + + {{i18n "admin.wizard.action.suppress_notifications.description" type="topic"}} + +
+
+{{/if}} + +{{#if routeTo}} +
+
+ +
+ +
+ {{input value=action.code}} +
+
{{/if}} diff --git a/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs b/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs index afdbd767..8c8bb6d4 100644 --- a/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs +++ b/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs @@ -258,11 +258,10 @@ {{i18n "admin.wizard.pro.label"}}
-
+
{{input name="key" value=field.key - class="medium" placeholderKey="admin.wizard.translation_placeholder"}}
diff --git a/assets/javascripts/discourse/templates/components/wizard-custom-step.hbs b/assets/javascripts/discourse/templates/components/wizard-custom-step.hbs index 3081be66..91476ae3 100644 --- a/assets/javascripts/discourse/templates/components/wizard-custom-step.hbs +++ b/assets/javascripts/discourse/templates/components/wizard-custom-step.hbs @@ -55,66 +55,62 @@ {{i18n "admin.wizard.step.force_final.description"}}
-{{/if}} -{{wizard-advanced-toggle showAdvanced=step.showAdvanced}} - -{{#if step.showAdvanced}} -
- -
-
- -
-
- {{wizard-mapper - inputs=step.required_data - options=(hash - inputTypes="validation" - inputConnector="and" - wizardFieldSelection="value" - userFieldSelection="value" - keyPlaceholder="admin.wizard.submission_key" - context="step" - )}} - {{#if step.required_data}} -
-
- {{i18n "admin.wizard.step.required_data.not_permitted_message"}} -
- {{input value=step.required_data_message}} +
+
+ + {{i18n "admin.wizard.pro.label"}} +
+
+ {{wizard-mapper + inputs=step.required_data + options=(hash + inputTypes="validation" + inputConnector="and" + wizardFieldSelection="value" + userFieldSelection="value" + keyPlaceholder="admin.wizard.submission_key" + context="step" + )}} + {{#if step.required_data}} +
+
+ {{i18n "admin.wizard.step.required_data.not_permitted_message"}}
- {{/if}} -
+ {{input value=step.required_data_message}} +
+ {{/if}}
+
-
-
- -
-
- {{wizard-mapper - inputs=step.permitted_params - options=(hash - pairConnector="set" - inputTypes="association" - keyPlaceholder="admin.wizard.param_key" - valuePlaceholder="admin.wizard.submission_key" - context="step" - )}} -
+
+
+ + {{i18n "admin.wizard.pro.label"}}
+
+ {{wizard-mapper + inputs=step.permitted_params + options=(hash + pairConnector="set" + inputTypes="association" + keyPlaceholder="admin.wizard.param_key" + valuePlaceholder="admin.wizard.submission_key" + context="step" + )}} +
+
-
-
- -
-
- {{input - name="key" - value=step.key - placeholderKey="admin.wizard.translation_placeholder"}} -
+
+
+ + {{i18n "admin.wizard.pro.label"}} +
+
+ {{input + name="key" + value=step.key + placeholderKey="admin.wizard.translation_placeholder"}}
{{/if}} diff --git a/assets/javascripts/discourse/templates/components/wizard-pro-selector/wizard-pro-selector-header.hbs b/assets/javascripts/discourse/templates/components/wizard-pro-selector/wizard-pro-selector-header.hbs new file mode 100644 index 00000000..a02b0d9c --- /dev/null +++ b/assets/javascripts/discourse/templates/components/wizard-pro-selector/wizard-pro-selector-header.hbs @@ -0,0 +1,15 @@ +
+ + {{component selectKit.options.selectedNameComponent + tabindex=tabindex + item=selectedContent + selectKit=selectKit + shouldDisplayClearableButton=shouldDisplayClearableButton + }} + + {{#if selectedContent.pro}} + {{i18n "admin.wizard.pro.label"}} + {{/if}} + + {{d-icon caretIcon class="caret-icon"}} +
\ No newline at end of file diff --git a/assets/javascripts/discourse/templates/components/wizard-pro-selector/wizard-pro-selector-row.hbs b/assets/javascripts/discourse/templates/components/wizard-pro-selector/wizard-pro-selector-row.hbs new file mode 100644 index 00000000..077265ef --- /dev/null +++ b/assets/javascripts/discourse/templates/components/wizard-pro-selector/wizard-pro-selector-row.hbs @@ -0,0 +1,15 @@ +{{#if icons}} +
+ + {{#each icons as |icon|}} + {{d-icon icon translatedtitle=(dasherize title)}} + {{/each}} +
+{{/if}} + +
+ {{html-safe label}} + {{#if item.pro}} + {{i18n "admin.wizard.pro.label"}} + {{/if}} +
diff --git a/assets/stylesheets/common/wizard-admin.scss b/assets/stylesheets/common/wizard-admin.scss index 178dbc81..3146da13 100644 --- a/assets/stylesheets/common/wizard-admin.scss +++ b/assets/stylesheets/common/wizard-admin.scss @@ -243,7 +243,7 @@ font-size: 0.85em; } - span { + > span { font-size: 0.929em; } @@ -382,11 +382,6 @@ margin: 0; } } - - .pro-label { - color: $tertiary; - font-size: .75em; - } } } @@ -764,6 +759,11 @@ vertical-align: middle; } +.pro-label { + color: var(--tertiary); + font-size: .75em; +} + .admin-wizards-pro { .admin-wizard-controls { h3, label { @@ -773,8 +773,8 @@ label { padding: .4em .5em; margin-left: .75em; - background-color: $success; - color: $secondary; + background-color: var(--success); + color: var(--secondary); } .buttons { @@ -807,17 +807,29 @@ display: flex; align-items: center; padding: 1em; - background-color: $primary-very-low; + background-color: var(--primary-very-low); .subscription-state { padding: .25em .5em; margin-right: .75em; &.active { - background-color: $success; - color: $secondary; + background-color: var(--success); + color: var(--secondary); } } } } } + +.wizard-pro-selector.select-kit.single-select { + .select-kit-row .texts { + display: flex; + align-items: center; + } + + .pro-label { + margin-left: .75em; + padding-top: .25em; + } +} diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index bfea46ce..451c7786 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -179,9 +179,9 @@ en: min_length_placeholder: "Minimum length in characters" max_length: "Max Length" max_length_placeholder: "Maximum length in characters" - char_counter: "Character Counter" + char_counter: "Counter" char_counter_placeholder: "Display Character Counter" - field_placeholder: "Field Placeholder" + field_placeholder: "Placeholder" file_types: "File Types" preview_template: "Preview Template" limit: "Limit" diff --git a/controllers/custom_wizard/admin/custom_fields.rb b/controllers/custom_wizard/admin/custom_fields.rb index c52759c9..40ff64be 100644 --- a/controllers/custom_wizard/admin/custom_fields.rb +++ b/controllers/custom_wizard/admin/custom_fields.rb @@ -1,7 +1,10 @@ # frozen_string_literal: true class CustomWizard::AdminCustomFieldsController < CustomWizard::AdminController def index - render_json_dump(custom_field_list) + render_json_dump( + custom_fields: custom_field_list, + pro_subscribed: CustomWizard::Pro.subscribed? + ) end def update diff --git a/lib/custom_wizard/custom_field.rb b/lib/custom_wizard/custom_field.rb index cc20ee95..bc1a146d 100644 --- a/lib/custom_wizard/custom_field.rb +++ b/lib/custom_wizard/custom_field.rb @@ -17,7 +17,9 @@ class ::CustomWizard::CustomField category: ["basic_category"], post: ["post"] } + PRO_CLASSES ||= ['category', 'group'] TYPES ||= ["string", "boolean", "integer", "json"] + PRO_TYPES ||= ["json"] LIST_CACHE_KEY ||= 'custom_field_list' def self.serializers @@ -83,6 +85,10 @@ class ::CustomWizard::CustomField next end + if attr == 'klass' && PRO_CLASSES.include?(value) && !@pro.subscribed? + add_error(I18n.t("wizard.custom_field.error.pro_type", type: value)) + end + if attr == 'serializers' && (unsupported = value - CLASSES[klass.to_sym]).length > 0 add_error(I18n.t("#{i18n_key}.unsupported_serializers", class: klass, @@ -94,7 +100,7 @@ class ::CustomWizard::CustomField add_error(I18n.t("#{i18n_key}.unsupported_type", type: value)) end - if attr == 'type' && value == 'json' && !@pro.subscribed? + if attr == 'type' && PRO_TYPES.include?(value) && !@pro.subscribed? add_error(I18n.t("wizard.custom_field.error.pro_type", type: value)) end diff --git a/lib/custom_wizard/pro.rb b/lib/custom_wizard/pro.rb index 3726e9b4..2e2f859d 100644 --- a/lib/custom_wizard/pro.rb +++ b/lib/custom_wizard/pro.rb @@ -16,7 +16,7 @@ class CustomWizard::Pro end def subscribed? - @subscription.active? + false #@subscription.active? end def server diff --git a/lib/custom_wizard/validators/template.rb b/lib/custom_wizard/validators/template.rb index 180958ba..0f5f686c 100644 --- a/lib/custom_wizard/validators/template.rb +++ b/lib/custom_wizard/validators/template.rb @@ -31,7 +31,7 @@ class CustomWizard::TemplateValidator if data[:actions].present? data[:actions].each do |action| - validate_pro_action(action) + validate_pro(action, :action) check_required(action, :action) end end From 8dbb3990e449d9db7c961d939840a37021bc27de Mon Sep 17 00:00:00 2001 From: angusmcleod Date: Fri, 3 Sep 2021 16:46:43 +0800 Subject: [PATCH 034/160] Remove test change --- lib/custom_wizard/pro.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/custom_wizard/pro.rb b/lib/custom_wizard/pro.rb index 2e2f859d..3726e9b4 100644 --- a/lib/custom_wizard/pro.rb +++ b/lib/custom_wizard/pro.rb @@ -16,7 +16,7 @@ class CustomWizard::Pro end def subscribed? - false #@subscription.active? + @subscription.active? end def server From f6c3c98d7183aed6efc84e22e550af60526414af Mon Sep 17 00:00:00 2001 From: angusmcleod Date: Mon, 6 Sep 2021 17:25:08 +0800 Subject: [PATCH 035/160] Add submission serializer spec --- .../custom_wizard/submission_serializer.rb | 1 - .../submission_serializer_spec.rb | 51 +++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 spec/serializers/custom_wizard/submission_serializer_spec.rb diff --git a/serializers/custom_wizard/submission_serializer.rb b/serializers/custom_wizard/submission_serializer.rb index f9cc7230..e5e88867 100644 --- a/serializers/custom_wizard/submission_serializer.rb +++ b/serializers/custom_wizard/submission_serializer.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true class CustomWizard::SubmissionSerializer < ApplicationSerializer attributes :id, - :user, :fields, :submitted_at diff --git a/spec/serializers/custom_wizard/submission_serializer_spec.rb b/spec/serializers/custom_wizard/submission_serializer_spec.rb new file mode 100644 index 00000000..d3f3e7fc --- /dev/null +++ b/spec/serializers/custom_wizard/submission_serializer_spec.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +require_relative '../../plugin_helper' + +describe CustomWizard::SubmissionSerializer do + fab!(:user) { Fabricate(:user) } + + let(:template_json) { + JSON.parse(File.open( + "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json" + ).read) + } + + before do + CustomWizard::Template.save(template_json, skip_jobs: true) + wizard = CustomWizard::Wizard.create(template_json["id"], user) + CustomWizard::Submission.new(wizard, + step_1_field_1: "I am user submission", + submitted_at: Time.now.iso8601 + ).save + @list = CustomWizard::Submission.list(wizard, page: 0) + end + + it 'should return submission attributes' do + json_array = ActiveModel::ArraySerializer.new( + @list.submissions, + each_serializer: described_class + ).as_json + + expect(json_array.length).to eq(1) + expect(json_array[0][:id].present?).to eq(true) + expect(json_array[0][:user].present?).to eq(true) + expect(json_array[0][:submitted_at].present?).to eq(true) + end + + it "should return field values, types and labels" do + json_array = ActiveModel::ArraySerializer.new( + @list.submissions, + each_serializer: described_class + ).as_json + + expect(json_array.length).to eq(1) + expect(json_array[0][:fields].as_json).to eq({ + "step_1_field_1": { + "value": "I am user submission", + "type": "text", + "label": "Text" + } + }.as_json) + end +end From 31b9e48c8ff11340d1cfa437c0fea1f4afe39833 Mon Sep 17 00:00:00 2001 From: angusmcleod Date: Mon, 6 Sep 2021 17:26:22 +0800 Subject: [PATCH 036/160] Apply rubocop --- spec/serializers/custom_wizard/submission_serializer_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/serializers/custom_wizard/submission_serializer_spec.rb b/spec/serializers/custom_wizard/submission_serializer_spec.rb index d3f3e7fc..02d8be8a 100644 --- a/spec/serializers/custom_wizard/submission_serializer_spec.rb +++ b/spec/serializers/custom_wizard/submission_serializer_spec.rb @@ -18,7 +18,7 @@ describe CustomWizard::SubmissionSerializer do step_1_field_1: "I am user submission", submitted_at: Time.now.iso8601 ).save - @list = CustomWizard::Submission.list(wizard, page: 0) + @list = CustomWizard::Submission.list(wizard, page: 0) end it 'should return submission attributes' do From 23c4b45195c4efebfaea454a15081ae405f3ed64 Mon Sep 17 00:00:00 2001 From: angusmcleod Date: Tue, 7 Sep 2021 20:06:13 +0800 Subject: [PATCH 037/160] Add pro feature specs --- .../components/wizard-custom-action.js.es6 | 2 +- .../discourse/lib/wizard-schema.js.es6 | 2 +- controllers/custom_wizard/admin/pro.rb | 2 +- controllers/custom_wizard/wizard.rb | 2 +- coverage/.last_run.json | 2 +- jobs/scheduled/update_pro_subscription.rb | 2 +- lib/custom_wizard/builder.rb | 34 +-- lib/custom_wizard/mapper.rb | 2 +- lib/custom_wizard/pro.rb | 35 ++- lib/custom_wizard/pro/authentication.rb | 22 +- lib/custom_wizard/validators/template.rb | 16 +- lib/custom_wizard/wizard.rb | 10 +- spec/components/custom_wizard/action_spec.rb | 199 ++++++------ spec/components/custom_wizard/builder_spec.rb | 46 +-- .../custom_wizard/custom_field_spec.rb | 76 +++-- spec/components/custom_wizard/field_spec.rb | 6 +- spec/components/custom_wizard/mapper_spec.rb | 177 ++++++----- spec/components/custom_wizard/pro_spec.rb | 125 ++++++++ spec/components/custom_wizard/step_spec.rb | 17 +- .../custom_wizard/submission_spec.rb | 7 +- .../components/custom_wizard/template_spec.rb | 13 +- .../custom_wizard/template_validator_spec.rb | 57 +++- .../custom_wizard/update_validator_spec.rb | 7 +- spec/components/custom_wizard/wizard_spec.rb | 18 +- .../custom_field_extensions_spec.rb | 74 +++-- .../extra_locales_controller_spec.rb | 14 +- spec/extensions/invites_controller_spec.rb | 7 +- spec/extensions/users_controller_spec.rb | 6 +- spec/fixtures/actions/add_to_group.json | 13 + spec/fixtures/actions/create_category.json | 51 ++++ spec/fixtures/actions/create_group.json | 104 +++++++ spec/fixtures/actions/send_message.json | 25 ++ spec/fixtures/actions/send_message_multi.json | 28 ++ spec/fixtures/custom_field/custom_fields.json | 16 - .../custom_field/pro_custom_fields.json | 28 ++ spec/fixtures/wizard.json | 285 ++---------------- spec/jobs/set_after_time_wizard_spec.rb | 6 +- spec/jobs/update_pro_subscription_spec.rb | 11 + spec/plugin_helper.rb | 41 +++ .../admin/custom_fields_controller_spec.rb | 9 +- .../admin/manager_controller_spec.rb | 7 +- .../admin/pro_controller_spec.rb | 71 +++++ .../admin/submissions_controller_spec.rb | 7 +- .../admin/wizard_controller_spec.rb | 7 +- .../application_controller_spec.rb | 16 +- .../custom_field_extensions_spec.rb | 91 +++--- .../custom_wizard/steps_controller_spec.rb | 273 +++++++---------- .../custom_wizard/wizard_controller_spec.rb | 25 +- .../basic_wizard_serializer_spec.rb | 7 +- .../custom_field_serializer_spec.rb | 7 +- .../pro/authentication_serializer_spec.rb | 17 ++ .../pro/subscription_serializer_spec.rb | 14 + .../custom_wizard/pro_serializer_spec.rb | 14 + .../wizard_field_serializer_spec.rb | 7 +- .../custom_wizard/wizard_serializer_spec.rb | 14 +- .../wizard_step_serializer_spec.rb | 18 +- 56 files changed, 1199 insertions(+), 993 deletions(-) create mode 100644 spec/components/custom_wizard/pro_spec.rb create mode 100644 spec/fixtures/actions/add_to_group.json create mode 100644 spec/fixtures/actions/create_category.json create mode 100644 spec/fixtures/actions/create_group.json create mode 100644 spec/fixtures/actions/send_message.json create mode 100644 spec/fixtures/actions/send_message_multi.json create mode 100644 spec/fixtures/custom_field/pro_custom_fields.json create mode 100644 spec/jobs/update_pro_subscription_spec.rb create mode 100644 spec/requests/custom_wizard/admin/pro_controller_spec.rb create mode 100644 spec/serializers/custom_wizard/pro/authentication_serializer_spec.rb create mode 100644 spec/serializers/custom_wizard/pro/subscription_serializer_spec.rb create mode 100644 spec/serializers/custom_wizard/pro_serializer_spec.rb diff --git a/assets/javascripts/discourse/components/wizard-custom-action.js.es6 b/assets/javascripts/discourse/components/wizard-custom-action.js.es6 index a04ff563..b0831d5d 100644 --- a/assets/javascripts/discourse/components/wizard-custom-action.js.es6 +++ b/assets/javascripts/discourse/components/wizard-custom-action.js.es6 @@ -1,6 +1,6 @@ import { default as discourseComputed } from "discourse-common/utils/decorators"; import wizardSchema from "discourse/plugins/discourse-custom-wizard/discourse/lib/wizard-schema"; -import { and, empty, equal, or } from "@ember/object/computed"; +import { empty, equal, or } from "@ember/object/computed"; import { notificationLevels, selectKitContent } from "../lib/wizard"; import { computed } from "@ember/object"; import UndoChanges from "../mixins/undo-changes"; diff --git a/assets/javascripts/discourse/lib/wizard-schema.js.es6 b/assets/javascripts/discourse/lib/wizard-schema.js.es6 index 613e6569..2e810634 100644 --- a/assets/javascripts/discourse/lib/wizard-schema.js.es6 +++ b/assets/javascripts/discourse/lib/wizard-schema.js.es6 @@ -197,9 +197,9 @@ const action = { required: ["id", "type"], proTypes: [ 'send_message', + 'add_to_group', 'create_category', 'create_group', - 'watch_categories', 'send_to_api' ], dependent: {}, diff --git a/controllers/custom_wizard/admin/pro.rb b/controllers/custom_wizard/admin/pro.rb index 15e9e50f..70e5de04 100644 --- a/controllers/custom_wizard/admin/pro.rb +++ b/controllers/custom_wizard/admin/pro.rb @@ -10,7 +10,7 @@ class CustomWizard::AdminProController < CustomWizard::AdminController def authorize request_id = SecureRandom.hex(32) cookies[:user_api_request_id] = request_id - redirect_to pro.authentication_request(current_user.id, request_id).to_s + redirect_to pro.authentication_url(current_user.id, request_id).to_s end def authorize_callback diff --git a/controllers/custom_wizard/wizard.rb b/controllers/custom_wizard/wizard.rb index 804fc422..78f2fd4a 100644 --- a/controllers/custom_wizard/wizard.rb +++ b/controllers/custom_wizard/wizard.rb @@ -5,7 +5,7 @@ class CustomWizard::WizardController < ::ApplicationController layout 'wizard' before_action :ensure_plugin_enabled - before_action :update_pro_subscription + before_action :update_pro_subscription, only: [:index] helper_method :wizard_page_title helper_method :wizard_theme_id helper_method :wizard_theme_lookup diff --git a/coverage/.last_run.json b/coverage/.last_run.json index 2d4d0378..cff5740b 100644 --- a/coverage/.last_run.json +++ b/coverage/.last_run.json @@ -1,5 +1,5 @@ { "result": { - "line": 91.83 + "line": 91.96 } } diff --git a/jobs/scheduled/update_pro_subscription.rb b/jobs/scheduled/update_pro_subscription.rb index d00b2560..773093b0 100644 --- a/jobs/scheduled/update_pro_subscription.rb +++ b/jobs/scheduled/update_pro_subscription.rb @@ -3,7 +3,7 @@ class CustomWizard::UpdateProSubscription < ::Jobs::Scheduled every 1.hour - def execute(args) + def execute(args={}) CustomWizard::Pro.update_subscription end end \ No newline at end of file diff --git a/lib/custom_wizard/builder.rb b/lib/custom_wizard/builder.rb index 2ecdf12d..99370f19 100644 --- a/lib/custom_wizard/builder.rb +++ b/lib/custom_wizard/builder.rb @@ -72,6 +72,23 @@ class CustomWizard::Builder @wizard end + def check_condition(template) + if template['condition'].present? + result = CustomWizard::Mapper.new( + inputs: template['condition'], + user: @wizard.user, + data: @wizard.current_submission&.fields_and_meta, + opts: { + multiple: true + } + ).perform + + result.any? + else + true + end + end + private def mapper @@ -224,23 +241,6 @@ class CustomWizard::Builder end end - def check_condition(template) - if template['condition'].present? - result = CustomWizard::Mapper.new( - inputs: template['condition'], - user: @wizard.user, - data: @wizard.current_submission&.fields_and_meta, - opts: { - multiple: true - } - ).perform - - result.any? - else - true - end - end - def check_if_permitted(step, step_template) step.permitted = true diff --git a/lib/custom_wizard/mapper.rb b/lib/custom_wizard/mapper.rb index b418b4b9..6700e37d 100644 --- a/lib/custom_wizard/mapper.rb +++ b/lib/custom_wizard/mapper.rb @@ -252,7 +252,7 @@ class CustomWizard::Mapper end end - if @pro.subscribed? && opts[:template] + if opts[:template] && @pro.subscribed? template = Liquid::Template.parse(string) string = template.render(data) end diff --git a/lib/custom_wizard/pro.rb b/lib/custom_wizard/pro.rb index 3726e9b4..5a813e21 100644 --- a/lib/custom_wizard/pro.rb +++ b/lib/custom_wizard/pro.rb @@ -54,24 +54,27 @@ class CustomWizard::Pro return false unless data && data.is_a?(Hash) subscriptions = data[:subscriptions] - if subscriptions.present? - subscription = subscriptions.first - type = subscription[:price_nickname] - + if subscriptions.present? && type = subscriptions.first[:price_nickname] @subscription = set_subscription(type) return true end end end - remove_subscription + destroy_subscription + false end def destroy_subscription - remove_subscription + if remove_subscription + @subscription = CustomWizard::ProSubscription.new(get_subscription) + !@subscription.active? + else + false + end end - def authentication_request(user_id, request_id) + def authentication_url(user_id, request_id) keys = @authentication.generate_keys(user_id, request_id) params = { public_key: keys.public_key, @@ -89,7 +92,7 @@ class CustomWizard::Pro def authentication_response(request_id, payload) data = @authentication.decrypt_payload(request_id, payload) - return unless data.is_a?(Hash) && data[:key] && data[:user_id] + return false unless data.is_a?(Hash) && data[:key] && data[:user_id] api_key = data[:key] user_id = data[:user_id] @@ -104,13 +107,26 @@ class CustomWizard::Pro end def destroy_authentication - remove_authentication + if remove_authentication + @authentication = CustomWizard::ProAuthentication.new(get_authentication) + !@authentication.active? + else + false + end end def self.subscribed? self.new.subscribed? end + def self.authorized? + self.new.authorized? + end + + def self.update_subscription + self.new.update_subscription + end + def self.namespace "custom_wizard_pro" end @@ -165,5 +181,6 @@ class CustomWizard::Pro def remove_authentication PluginStore.remove(self.class.namespace, authentication_db_key) + get_authentication end end \ No newline at end of file diff --git a/lib/custom_wizard/pro/authentication.rb b/lib/custom_wizard/pro/authentication.rb index 0f3546eb..8d96a017 100644 --- a/lib/custom_wizard/pro/authentication.rb +++ b/lib/custom_wizard/pro/authentication.rb @@ -30,11 +30,13 @@ class CustomWizard::ProAuthentication def decrypt_payload(request_id, payload) keys = get_keys(request_id) + return false unless keys.present? && keys.pem delete_keys(request_id) rsa = OpenSSL::PKey::RSA.new(keys.pem) decrypted_payload = rsa.private_decrypt(Base64.decode64(payload)) + return false unless decrypted_payload.present? begin @@ -48,7 +50,16 @@ class CustomWizard::ProAuthentication data end - + + def get_keys(request_id) + raw = PluginStore.get(CustomWizard::Pro.namespace, "#{keys_db_key}_#{request_id}") + OpenStruct.new( + user_id: raw && raw['user_id'], + pem: raw && raw['pem'], + nonce: raw && raw['nonce'] + ) + end + private def keys_db_key @@ -67,15 +78,6 @@ class CustomWizard::ProAuthentication ) end - def get_keys(request_id) - raw = PluginStore.get(CustomWizard::Pro.namespace, "#{keys_db_key}_#{request_id}") - OpenStruct.new( - user_id: raw && raw['user_id'], - pem: raw && raw['pem'], - nonce: raw && raw['nonce'] - ) - end - def delete_keys(request_id) PluginStore.remove(CustomWizard::Pro.namespace, "#{keys_db_key}_#{request_id}") end diff --git a/lib/custom_wizard/validators/template.rb b/lib/custom_wizard/validators/template.rb index 0f5f686c..839a070f 100644 --- a/lib/custom_wizard/validators/template.rb +++ b/lib/custom_wizard/validators/template.rb @@ -21,8 +21,8 @@ class CustomWizard::TemplateValidator check_required(step, :step) validate_pro(step, :step) - if data[:fields].present? - data[:fields].each do |field| + if step[:fields].present? + step[:fields].each do |field| validate_pro(field, :field) check_required(field, :field) end @@ -66,9 +66,9 @@ class CustomWizard::TemplateValidator action: { type: %w[ send_message + add_to_group create_category create_group - watch_categories send_to_api ] } @@ -87,11 +87,13 @@ class CustomWizard::TemplateValidator def validate_pro(object, type) self.class.pro[type].each do |property, pro_type| - is_pro = (pro_type === 'present' && object[property].present?) || - (pro_type === 'conditional' && object[property].is_a?(Hash)) || - (pro_type.is_a?(Array) && pro_type.includes?(object[property])) + is_pro = object[property.to_s].present? && ( + pro_type === 'present' || + (pro_type === 'conditional' && object[property.to_s].is_a?(Hash)) || + (pro_type.is_a?(Array) && pro_type.include?(object[property.to_s])) + ) - if is_pro && @pro.subscribed? + if is_pro && !@pro.subscribed? errors.add :base, I18n.t("wizard.validation.pro", type: type.to_s, property: property) end end diff --git a/lib/custom_wizard/wizard.rb b/lib/custom_wizard/wizard.rb index 8f5a897f..cd59f361 100644 --- a/lib/custom_wizard/wizard.rb +++ b/lib/custom_wizard/wizard.rb @@ -308,10 +308,10 @@ class CustomWizard::Wizard end end - def self.list(user, template_opts: {}, not_completed: false) + def self.list(user, template_opts = {}, not_completed = false) return [] unless user - CustomWizard::Template.list(template_opts).reduce([]) do |result, template| + CustomWizard::Template.list(**template_opts).reduce([]) do |result, template| wizard = new(template, user) result.push(wizard) if wizard.can_access? && ( !not_completed || !wizard.completed? @@ -323,7 +323,7 @@ class CustomWizard::Wizard def self.after_signup(user) wizards = list( user, - template_opts: { + { setting: 'after_signup', order: "(value::json ->> 'permitted') IS NOT NULL DESC" } @@ -334,11 +334,11 @@ class CustomWizard::Wizard def self.prompt_completion(user) wizards = list( user, - template_opts: { + { setting: 'prompt_completion', order: "(value::json ->> 'permitted') IS NOT NULL DESC" }, - not_completed: true + true ) if wizards.any? wizards.map do |w| diff --git a/spec/components/custom_wizard/action_spec.rb b/spec/components/custom_wizard/action_spec.rb index 8b617c39..438f29dd 100644 --- a/spec/components/custom_wizard/action_spec.rb +++ b/spec/components/custom_wizard/action_spec.rb @@ -6,26 +6,22 @@ describe CustomWizard::Action do fab!(:category) { Fabricate(:category, name: 'cat1', slug: 'cat-slug') } fab!(:group) { Fabricate(:group) } - let(:wizard_template) { - JSON.parse( - File.open( - "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json" - ).read - ) - } + let(:wizard_template) { get_wizard_fixture("wizard") } + let(:open_composer) { get_wizard_fixture("actions/open_composer") } + let(:create_category) { get_wizard_fixture("actions/create_category") } + let(:create_group) { get_wizard_fixture("actions/create_group") } + let(:add_to_group) { get_wizard_fixture("actions/add_to_group") } + let(:send_message) { get_wizard_fixture("actions/send_message") } + let(:send_message_multi) { get_wizard_fixture("actions/send_message_multi") } - let(:open_composer) { - JSON.parse( - File.open( - "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/actions/open_composer.json" - ).read - ) - } + def update_template(template) + CustomWizard::Template.save(template, skip_jobs: true) + @template = CustomWizard::Template.find('super_mega_fun_wizard') + end before do Group.refresh_automatic_group!(:trust_level_2) - CustomWizard::Template.save(wizard_template, skip_jobs: true) - @template = CustomWizard::Template.find('super_mega_fun_wizard') + update_template(wizard_template) end context 'creating a topic' do @@ -110,54 +106,6 @@ describe CustomWizard::Action do end end - context 'sending a message' do - it 'works' do - User.create(username: 'angus1', email: "angus1@email.com") - - wizard = CustomWizard::Builder.new(@template[:id], user).build - wizard.create_updater(wizard.steps[0].id, {}).update - wizard.create_updater(wizard.steps[1].id, {}).update - - topic = Topic.where( - archetype: Archetype.private_message, - title: "Message title" - ) - - post = Post.where( - topic_id: topic.pluck(:id), - raw: "I will interpolate some wizard fields" - ) - - expect(topic.exists?).to eq(true) - expect(topic.first.topic_allowed_users.first.user.username).to eq('angus1') - expect(post.exists?).to eq(true) - end - - it 'allows using multiple PM targets' do - User.create(username: 'angus1', email: "angus1@email.com") - User.create(username: 'faiz', email: "faiz@email.com") - Group.create(name: "cool_group") - Group.create(name: 'cool_group_1') - wizard = CustomWizard::Builder.new(@template[:id], user).build - wizard.create_updater(wizard.steps[0].id, {}).update - wizard.create_updater(wizard.steps[1].id, {}).update - - topic = Topic.where( - archetype: Archetype.private_message, - title: "Multiple Recipients title" - ) - - post = Post.where( - topic_id: topic.pluck(:id), - raw: "I will interpolate some wizard fields" - ) - expect(topic.exists?).to eq(true) - expect(topic.first.all_allowed_users.map(&:username)).to include('angus1', 'faiz') - expect(topic.first.allowed_groups.map(&:name)).to include('cool_group', 'cool_group_1') - expect(post.exists?).to eq(true) - end - end - it 'updates a profile' do wizard = CustomWizard::Builder.new(@template[:id], user).build upload = Upload.create!( @@ -182,10 +130,8 @@ describe CustomWizard::Action do updater = wizard.create_updater(wizard.steps[1].id, {}) updater.update - category = Category.find_by(id: wizard.current_submission.fields['action_8']) - expect(updater.result[:redirect_on_next]).to eq( - "/new-topic?title=Title%20of%20the%20composer%20topic&body=I%20am%20interpolating%20some%20user%20fields%20Angus%20angus%20angus%40email.com&category_id=#{category.id}&tags=tag1" + "/new-topic?title=Title%20of%20the%20composer%20topic&body=I%20am%20interpolating%20some%20user%20fields%20Angus%20angus%20angus%40email.com&tags=tag1" ) end @@ -210,35 +156,11 @@ describe CustomWizard::Action do end end - it 'creates a category' do - wizard = CustomWizard::Builder.new(@template[:id], user).build - wizard.create_updater(wizard.steps[0].id, step_1_field_1: "Text input").update - wizard.create_updater(wizard.steps[1].id, {}).update - expect(Category.where(id: wizard.current_submission.fields['action_8']).exists?).to eq(true) - end - - it 'creates a group' do - wizard = CustomWizard::Builder.new(@template[:id], user).build - wizard.create_updater(wizard.steps[0].id, step_1_field_1: "Text input").update - expect(Group.where(name: wizard.current_submission.fields['action_9']).exists?).to eq(true) - end - - it 'adds a user to a group' do - wizard = CustomWizard::Builder.new(@template[:id], user).build - step_id = wizard.steps[0].id - updater = wizard.create_updater(step_id, step_1_field_1: "Text input").update - group = Group.find_by(name: wizard.current_submission.fields['action_9']) - expect(group.users.first.username).to eq('angus') - end - it 'watches categories' do wizard = CustomWizard::Builder.new(@template[:id], user).build wizard.create_updater(wizard.steps[0].id, step_1_field_1: "Text input").update wizard.create_updater(wizard.steps[1].id, {}).update - expect(CategoryUser.where( - category_id: wizard.current_submission.fields['action_8'], - user_id: user.id - ).first.notification_level).to eq(2) + expect(CategoryUser.where( category_id: category.id, user_id: user.id @@ -251,4 +173,97 @@ describe CustomWizard::Action do updater.update expect(updater.result[:redirect_on_next]).to eq("https://google.com") end + + context "pro actions" do + before do + enable_pro + end + + it '#send_message' do + wizard_template['actions'] << send_message + update_template(wizard_template) + + User.create(username: 'angus1', email: "angus1@email.com") + + wizard = CustomWizard::Builder.new(@template[:id], user).build + wizard.create_updater(wizard.steps[0].id, {}).update + wizard.create_updater(wizard.steps[1].id, {}).update + + topic = Topic.where( + archetype: Archetype.private_message, + title: "Message title" + ) + + post = Post.where( + topic_id: topic.pluck(:id), + raw: "I will interpolate some wizard fields" + ) + + expect(topic.exists?).to eq(true) + expect(topic.first.topic_allowed_users.first.user.username).to eq('angus1') + expect(post.exists?).to eq(true) + end + + it '#send_message allows using multiple targets' do + wizard_template['actions'] << send_message_multi + update_template(wizard_template) + + User.create(username: 'angus1', email: "angus1@email.com") + User.create(username: 'faiz', email: "faiz@email.com") + Group.create(name: "cool_group") + Group.create(name: 'cool_group_1') + wizard = CustomWizard::Builder.new(@template[:id], user).build + wizard.create_updater(wizard.steps[0].id, {}).update + wizard.create_updater(wizard.steps[1].id, {}).update + + topic = Topic.where( + archetype: Archetype.private_message, + title: "Multiple Recipients title" + ) + + post = Post.where( + topic_id: topic.pluck(:id), + raw: "I will interpolate some wizard fields" + ) + + expect(topic.exists?).to eq(true) + expect(topic.first.all_allowed_users.map(&:username)).to include('angus1', 'faiz') + expect(topic.first.allowed_groups.map(&:name)).to include('cool_group', 'cool_group_1') + expect(post.exists?).to eq(true) + end + + it '#create_category' do + wizard_template['actions'] << create_category + update_template(wizard_template) + + wizard = CustomWizard::Builder.new(@template[:id], user).build + wizard.create_updater(wizard.steps[0].id, step_1_field_1: "Text input").update + wizard.create_updater(wizard.steps[1].id, {}).update + + expect(Category.where(id: wizard.current_submission.fields['action_8']).exists?).to eq(true) + end + + it '#create_group' do + wizard_template['actions'] << create_group + update_template(wizard_template) + + wizard = CustomWizard::Builder.new(@template[:id], user).build + wizard.create_updater(wizard.steps[0].id, step_1_field_1: "Text input").update + + expect(Group.where(name: wizard.current_submission.fields['action_9']).exists?).to eq(true) + end + + it '#add_to_group' do + wizard_template['actions'] << create_group + wizard_template['actions'] << add_to_group + update_template(wizard_template) + + wizard = CustomWizard::Builder.new(@template[:id], user).build + step_id = wizard.steps[0].id + updater = wizard.create_updater(step_id, step_1_field_1: "Text input").update + group = Group.find_by(name: wizard.current_submission.fields['action_9']) + + expect(group.users.first.username).to eq('angus') + end + end end diff --git a/spec/components/custom_wizard/builder_spec.rb b/spec/components/custom_wizard/builder_spec.rb index 8e80d806..19be5830 100644 --- a/spec/components/custom_wizard/builder_spec.rb +++ b/spec/components/custom_wizard/builder_spec.rb @@ -15,45 +15,15 @@ describe CustomWizard::Builder do fab!(:category2) { Fabricate(:category, name: 'cat2') } fab!(:group) { Fabricate(:group) } - let(:required_data_json) { - JSON.parse( - File.open( - "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/step/required_data.json" - ).read - ) - } - - let(:permitted_json) { - JSON.parse( - File.open( - "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard/permitted.json" - ).read - ) - } - - let(:permitted_param_json) { - JSON.parse( - File.open( - "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/step/permitted_params.json" - ).read - ) - } - - let(:user_condition_json) { - JSON.parse( - File.open( - "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/condition/user_condition.json" - ).read - ) - } + let(:wizard_template) { get_wizard_fixture("wizard") } + let(:required_data_json) { get_wizard_fixture("step/required_data") } + let(:permitted_json) { get_wizard_fixture("wizard/permitted") } + let(:permitted_param_json) { get_wizard_fixture("step/permitted_params") } + let(:user_condition_json) { get_wizard_fixture("condition/user_condition") } before do Group.refresh_automatic_group!(:trust_level_3) - CustomWizard::Template.save( - JSON.parse(File.open( - "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json" - ).read), - skip_jobs: true) + CustomWizard::Template.save(wizard_template, skip_jobs: true) @template = CustomWizard::Template.find('super_mega_fun_wizard') end @@ -281,8 +251,9 @@ describe CustomWizard::Builder do end end - context "with condition" do + context "with condition" do before do + enable_pro @template[:steps][0][:condition] = user_condition_json['condition'] CustomWizard::Template.save(@template.as_json) end @@ -321,6 +292,7 @@ describe CustomWizard::Builder do context "with condition" do before do + enable_pro @template[:steps][0][:fields][0][:condition] = user_condition_json['condition'] CustomWizard::Template.save(@template.as_json) end diff --git a/spec/components/custom_wizard/custom_field_spec.rb b/spec/components/custom_wizard/custom_field_spec.rb index b17e26c6..fb1799dd 100644 --- a/spec/components/custom_wizard/custom_field_spec.rb +++ b/spec/components/custom_wizard/custom_field_spec.rb @@ -3,12 +3,8 @@ require_relative '../../plugin_helper' describe CustomWizard::CustomField do - - let(:custom_field_json) { - JSON.parse(File.open( - "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/custom_field/custom_fields.json" - ).read) - } + let(:custom_field_json) { get_wizard_fixture("custom_field/custom_fields") } + let(:custom_field_pro_json) { get_wizard_fixture("custom_field/pro_custom_fields") } before do CustomWizard::CustomField.invalidate_cache @@ -104,8 +100,8 @@ describe CustomWizard::CustomField do it "does not save with an unsupported serializer" do invalid_field_json = custom_field_json['custom_fields'].first - invalid_field_json['klass'] = 'category' - invalid_field_json['serializers'] = ['category', 'site_category'] + invalid_field_json['klass'] = 'post' + invalid_field_json['serializers'] = ['post', 'post_revision'] custom_field = CustomWizard::CustomField.new(nil, invalid_field_json) @@ -113,8 +109,8 @@ describe CustomWizard::CustomField do expect(custom_field.valid?).to eq(false) expect(custom_field.errors.full_messages.first).to eq( I18n.t("wizard.custom_field.error.unsupported_serializers", - class: "category", - serializers: "category, site_category" + class: "post", + serializers: "post_revision" ) ) expect( @@ -196,6 +192,50 @@ describe CustomWizard::CustomField do ).exists? ).to eq(false) end + + it "does not save pro field types without a pro subscription" do + pro_field_json = custom_field_pro_json['custom_fields'].first + custom_field = CustomWizard::CustomField.new(nil, pro_field_json) + + expect(custom_field.save).to eq(false) + expect(custom_field.valid?).to eq(false) + expect(custom_field.errors.full_messages.first).to eq( + I18n.t("wizard.custom_field.error.pro_type", type: "json") + ) + end + + it "does not save pro field classes without a pro subscription" do + pro_field_json = custom_field_pro_json['custom_fields'].second + custom_field = CustomWizard::CustomField.new(nil, pro_field_json) + + expect(custom_field.save).to eq(false) + expect(custom_field.valid?).to eq(false) + expect(custom_field.errors.full_messages.first).to eq( + I18n.t("wizard.custom_field.error.pro_type", type: "category") + ) + end + + context "with a pro subscription" do + before do + enable_pro + end + + it "saves pro field types" do + pro_field_json = custom_field_pro_json['custom_fields'].first + custom_field = CustomWizard::CustomField.new(nil, pro_field_json) + + expect(custom_field.save).to eq(true) + expect(custom_field.valid?).to eq(true) + end + + it "saves pro field classes" do + pro_field_json = custom_field_pro_json['custom_fields'].second + custom_field = CustomWizard::CustomField.new(nil, pro_field_json) + + expect(custom_field.save).to eq(true) + expect(custom_field.valid?).to eq(true) + end + end end context "lists" do @@ -205,15 +245,15 @@ describe CustomWizard::CustomField do end end - it "lists saved custom field records" do - expect(CustomWizard::CustomField.list.length).to eq(4) + it "saved custom field records" do + expect(CustomWizard::CustomField.list.length).to eq(2) end - it "lists saved custom field records by attribute value" do + it "saved custom field records by attribute value" do expect(CustomWizard::CustomField.list_by(:klass, 'topic').length).to eq(1) end - it "lists saved custom field records by optional values" do + it "saved custom field records by optional values" do field_json = custom_field_json['custom_fields'].first field_json['serializers'] = nil @@ -221,12 +261,12 @@ describe CustomWizard::CustomField do expect(CustomWizard::CustomField.list_by(:serializers, ['post']).length).to eq(0) end - it "lists custom field records added by other plugins " do - expect(CustomWizard::CustomField.external_list.length).to eq(11) + it "custom field records added by other plugins " do + expect(CustomWizard::CustomField.external_list.length).to be > 10 end - it "lists all custom field records" do - expect(CustomWizard::CustomField.full_list.length).to eq(15) + it "all custom field records" do + expect(CustomWizard::CustomField.full_list.length).to be > 12 end end diff --git a/spec/components/custom_wizard/field_spec.rb b/spec/components/custom_wizard/field_spec.rb index 871c42cd..0fcf9fc2 100644 --- a/spec/components/custom_wizard/field_spec.rb +++ b/spec/components/custom_wizard/field_spec.rb @@ -2,11 +2,7 @@ require_relative '../../plugin_helper' describe CustomWizard::Field do - let(:field_hash) do - JSON.parse(File.open( - "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/field/field.json" - ).read).with_indifferent_access - end + let(:field_hash) { get_wizard_fixture("field/field") } before do CustomWizard::Field.register( diff --git a/spec/components/custom_wizard/mapper_spec.rb b/spec/components/custom_wizard/mapper_spec.rb index ed66d7c1..b210c588 100644 --- a/spec/components/custom_wizard/mapper_spec.rb +++ b/spec/components/custom_wizard/mapper_spec.rb @@ -31,16 +31,8 @@ describe CustomWizard::Mapper do ] ) } - let(:inputs) { - JSON.parse(File.open( - "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/mapper/inputs.json" - ).read) - } - let(:data) { - JSON.parse(File.open( - "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/mapper/data.json" - ).read) - } + let(:inputs) { get_wizard_fixture("mapper/inputs") } + let(:data) { get_wizard_fixture("mapper/data") } let(:template_params) { { "step_1_field_1" => "Hello" @@ -352,7 +344,7 @@ describe CustomWizard::Mapper do expect(result).to eq(template_params["step_1_field_1"]) end - it "treats replaced values as string literals" do + it "requires a pro subscription" do template = '{{ "w{step_1_field_1}" | size }}' mapper = create_template_mapper(template_params, user1) result = mapper.interpolate( @@ -362,60 +354,17 @@ describe CustomWizard::Mapper do wizard: true, value: true ) - expect(result).to eq(template_params["step_1_field_1"].size.to_s) + expect(result).to eq("{{ \"#{template_params["step_1_field_1"]}\" | size }}") end - it "allows the wizard values to be used inside conditionals" do - template = <<-LIQUID - {%- if "w{step_1_field_1}" contains "ello" -%} - Correct - {%- else -%} - Incorrect - {%-endif-%} - LIQUID - mapper = create_template_mapper(template_params, user1) - result = mapper.interpolate( - template.dup, - template: true, - user: true, - wizard: true, - value: true - ) - expect(result).to eq("Correct") - end + context "with a pro subscription" do + before do + enable_pro + end - it "can access data passed to render method as variable" do - template = "{{step_1_field_1.size}}" - mapper = create_template_mapper(template_params, user1) - result = mapper.interpolate( - template.dup, - template: true, - user: true, - wizard: true, - value: true - ) - expect(result).to eq(template_params["step_1_field_1"].size.to_s) - end - - it "doesn't parse the template when template param is false" do - template = <<-LIQUID.strip - {{ "w{step_1_field_1}" | size}} - LIQUID - mapper = create_template_mapper(template_params, user1) - result = mapper.interpolate( - template.dup, - template: false, - ) - expect(result).to eq(template) - end - - context "custom filter: 'first_non_empty'" do - it "gives first non empty element from list" do - template = <<-LIQUID.strip - {%- assign entry = "" | first_non_empty: step_1_field_1, step_1_field_2, step_1_field_3 -%} - {{ entry }} - LIQUID - mapper = create_template_mapper(template_params_non_empty, user1) + it "treats replaced values as string literals" do + template = '{{ "w{step_1_field_1}" | size }}' + mapper = create_template_mapper(template_params, user1) result = mapper.interpolate( template.dup, template: true, @@ -423,15 +372,18 @@ describe CustomWizard::Mapper do wizard: true, value: true ) - expect(result).to eq(template_params_non_empty["step_1_field_3"]) + expect(result).to eq(template_params["step_1_field_1"].size.to_s) end - it "gives first non empty element from list when multiple non empty values present" do - template = <<-LIQUID.strip - {%- assign entry = "" | first_non_empty: step_1_field_1, step_1_field_2, step_1_field_3 -%} - {{ entry }} + it "allows the wizard values to be used inside conditionals" do + template = <<-LIQUID + {%- if "w{step_1_field_1}" contains "ello" -%} + Correct + {%- else -%} + Incorrect + {%-endif-%} LIQUID - mapper = create_template_mapper(template_params_multiple_non_empty, user1) + mapper = create_template_mapper(template_params, user1) result = mapper.interpolate( template.dup, template: true, @@ -439,25 +391,84 @@ describe CustomWizard::Mapper do wizard: true, value: true ) - expect(result).to eq(template_params_multiple_non_empty["step_1_field_2"]) + expect(result).to eq("Correct") end - it "gives empty if all elements are empty" do + it "can access data passed to render method as variable" do + template = "{{step_1_field_1.size}}" + mapper = create_template_mapper(template_params, user1) + result = mapper.interpolate( + template.dup, + template: true, + user: true, + wizard: true, + value: true + ) + expect(result).to eq(template_params["step_1_field_1"].size.to_s) + end + + it "doesn't parse the template when template param is false" do template = <<-LIQUID.strip - {%- assign entry = "" | first_non_empty: step_1_field_1, step_1_field_2, step_1_field_3 -%} - {%- if entry -%} + {{ "w{step_1_field_1}" | size}} + LIQUID + mapper = create_template_mapper(template_params, user1) + result = mapper.interpolate( + template.dup, + template: false, + ) + expect(result).to eq(template) + end + + context "custom filter: 'first_non_empty'" do + it "gives first non empty element from list" do + template = <<-LIQUID.strip + {%- assign entry = "" | first_non_empty: step_1_field_1, step_1_field_2, step_1_field_3 -%} {{ entry }} - {%- endif -%} - LIQUID - mapper = create_template_mapper(template_params_empty, user1) - result = mapper.interpolate( - template.dup, - template: true, - user: true, - wizard: true, - value: true - ) - expect(result).to eq("") + LIQUID + mapper = create_template_mapper(template_params_non_empty, user1) + result = mapper.interpolate( + template.dup, + template: true, + user: true, + wizard: true, + value: true + ) + expect(result).to eq(template_params_non_empty["step_1_field_3"]) + end + + it "gives first non empty element from list when multiple non empty values present" do + template = <<-LIQUID.strip + {%- assign entry = "" | first_non_empty: step_1_field_1, step_1_field_2, step_1_field_3 -%} + {{ entry }} + LIQUID + mapper = create_template_mapper(template_params_multiple_non_empty, user1) + result = mapper.interpolate( + template.dup, + template: true, + user: true, + wizard: true, + value: true + ) + expect(result).to eq(template_params_multiple_non_empty["step_1_field_2"]) + end + + it "gives empty if all elements are empty" do + template = <<-LIQUID.strip + {%- assign entry = "" | first_non_empty: step_1_field_1, step_1_field_2, step_1_field_3 -%} + {%- if entry -%} + {{ entry }} + {%- endif -%} + LIQUID + mapper = create_template_mapper(template_params_empty, user1) + result = mapper.interpolate( + template.dup, + template: true, + user: true, + wizard: true, + value: true + ) + expect(result).to eq("") + end end end end diff --git a/spec/components/custom_wizard/pro_spec.rb b/spec/components/custom_wizard/pro_spec.rb new file mode 100644 index 00000000..6c83ae3b --- /dev/null +++ b/spec/components/custom_wizard/pro_spec.rb @@ -0,0 +1,125 @@ +# frozen_string_literal: true + +require_relative '../../plugin_helper' + +describe CustomWizard::Pro do + fab!(:user) { Fabricate(:user) } + + it "initializes pro authentication and subscription" do + pro = described_class.new + expect(pro.authentication.class).to eq(CustomWizard::ProAuthentication) + expect(pro.subscription.class).to eq(CustomWizard::ProSubscription) + end + + it "returns authorized and subscribed states" do + pro = described_class.new + expect(pro.authorized?).to eq(false) + expect(pro.subscribed?).to eq(false) + end + + context "subscription" do + before do + @pro = described_class.new + end + + it "updates valid subscriptions" do + stub_subscription_request(200, valid_subscription) + expect(@pro.update_subscription).to eq(true) + expect(@pro.subscribed?).to eq(true) + end + + it "handles invalid subscriptions" do + stub_subscription_request(200, invalid_subscription) + expect(@pro.update_subscription).to eq(false) + expect(@pro.subscribed?).to eq(false) + end + + it "handles subscription http errors" do + stub_subscription_request(404, {}) + expect(@pro.update_subscription).to eq(false) + expect(@pro.subscribed?).to eq(false) + end + + it "destroys subscriptions" do + stub_subscription_request(200, valid_subscription) + expect(@pro.update_subscription).to eq(true) + expect(@pro.destroy_subscription).to eq(true) + expect(@pro.subscribed?).to eq(false) + end + + it "has class aliases" do + authenticate_pro + stub_subscription_request(200, valid_subscription) + expect(described_class.update_subscription).to eq(true) + expect(described_class.subscribed?).to eq(true) + end + end + + context "authentication" do + before do + @pro = described_class.new + user.update!(admin: true) + end + + it "generates a valid authentication request url" do + request_id = SecureRandom.hex(32) + uri = URI(@pro.authentication_url(user.id, request_id)) + expect(uri.host).to eq(@pro.server) + + parsed_query = Rack::Utils.parse_query uri.query + expect(parsed_query['public_key'].present?).to eq(true) + expect(parsed_query['nonce'].present?).to eq(true) + expect(parsed_query['client_id'].present?).to eq(true) + expect(parsed_query['auth_redirect'].present?).to eq(true) + expect(parsed_query['application_name']).to eq(SiteSetting.title) + expect(parsed_query['scopes']).to eq(@pro.scope) + end + + def generate_payload(request_id, user_id) + uri = URI(@pro.authentication_url(user_id, request_id)) + keys = @pro.authentication.get_keys(request_id) + raw_payload = { + key: "12345", + nonce: keys.nonce, + push: false, + api: UserApiKeysController::AUTH_API_VERSION + }.to_json + public_key = OpenSSL::PKey::RSA.new(keys.pem) + Base64.encode64(public_key.public_encrypt(raw_payload)) + end + + it "handles authentication response if request and response is valid" do + request_id = SecureRandom.hex(32) + payload = generate_payload(request_id, user.id) + + expect(@pro.authentication_response(request_id, payload)).to eq(true) + expect(@pro.authorized?).to eq(true) + end + + it "discards authentication response if user who made request as not an admin" do + user.update!(admin: false) + + request_id = SecureRandom.hex(32) + payload = generate_payload(request_id, user.id) + + expect(@pro.authentication_response(request_id, payload)).to eq(false) + expect(@pro.authorized?).to eq(false) + end + + it "discards authentication response if request_id is invalid" do + payload = generate_payload(SecureRandom.hex(32), user.id) + + expect(@pro.authentication_response(SecureRandom.hex(32), payload)).to eq(false) + expect(@pro.authorized?).to eq(false) + end + + it "destroys authentication" do + request_id = SecureRandom.hex(32) + payload = generate_payload(request_id, user.id) + @pro.authentication_response(request_id, payload) + + expect(@pro.destroy_authentication).to eq(true) + expect(@pro.authorized?).to eq(false) + end + end +end \ No newline at end of file diff --git a/spec/components/custom_wizard/step_spec.rb b/spec/components/custom_wizard/step_spec.rb index bf4613a4..9ad02176 100644 --- a/spec/components/custom_wizard/step_spec.rb +++ b/spec/components/custom_wizard/step_spec.rb @@ -2,21 +2,8 @@ require_relative '../../plugin_helper' describe CustomWizard::Step do - let(:step_hash) do - JSON.parse( - File.open( - "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/step/step.json" - ).read - ).with_indifferent_access - end - - let(:field_hash) do - JSON.parse( - File.open( - "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/field/field.json" - ).read - ).with_indifferent_access - end + let(:step_hash) { get_wizard_fixture("step/step") } + let(:field_hash) { get_wizard_fixture("field/field") } before do @step = CustomWizard::Step.new(step_hash[:id]) diff --git a/spec/components/custom_wizard/submission_spec.rb b/spec/components/custom_wizard/submission_spec.rb index b85af243..a838820d 100644 --- a/spec/components/custom_wizard/submission_spec.rb +++ b/spec/components/custom_wizard/submission_spec.rb @@ -4,12 +4,7 @@ require_relative '../../plugin_helper' describe CustomWizard::Submission do fab!(:user) { Fabricate(:user) } fab!(:user2) { Fabricate(:user) } - - let(:template_json) { - JSON.parse(File.open( - "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json" - ).read) - } + let(:template_json) { get_wizard_fixture("wizard") } before do CustomWizard::Template.save(template_json, skip_jobs: true) diff --git a/spec/components/custom_wizard/template_spec.rb b/spec/components/custom_wizard/template_spec.rb index 0e3dbdbe..fca7f91e 100644 --- a/spec/components/custom_wizard/template_spec.rb +++ b/spec/components/custom_wizard/template_spec.rb @@ -3,17 +3,8 @@ require_relative '../../plugin_helper' describe CustomWizard::Template do fab!(:user) { Fabricate(:user) } - - let(:template_json) { - JSON.parse(File.open( - "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json" - ).read) - } - let(:permitted_json) { - JSON.parse(File.open( - "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard/permitted.json" - ).read) - } + let(:template_json) { get_wizard_fixture("wizard") } + let(:permitted_json) { get_wizard_fixture("wizard/permitted") } before do CustomWizard::Template.save(template_json, skip_jobs: true) diff --git a/spec/components/custom_wizard/template_validator_spec.rb b/spec/components/custom_wizard/template_validator_spec.rb index c8ce915a..7a84660c 100644 --- a/spec/components/custom_wizard/template_validator_spec.rb +++ b/spec/components/custom_wizard/template_validator_spec.rb @@ -3,12 +3,9 @@ require_relative '../../plugin_helper' describe CustomWizard::TemplateValidator do fab!(:user) { Fabricate(:user) } - - let(:template) { - JSON.parse(File.open( - "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json" - ).read).with_indifferent_access - } + let(:template) { get_wizard_fixture("wizard") } + let(:create_category) { get_wizard_fixture("actions/create_category") } + let(:user_condition) { get_wizard_fixture("condition/user_condition") } it "validates valid templates" do expect( @@ -45,4 +42,52 @@ describe CustomWizard::TemplateValidator do CustomWizard::TemplateValidator.new(template).perform ).to eq(false) end + + it "invalidates pro step attributes without a pro subscription" do + template[:steps][0][:condition] = user_condition['condition'] + expect( + CustomWizard::TemplateValidator.new(template).perform + ).to eq(false) + end + + it "invalidates pro field attributes without a pro subscription" do + template[:steps][0][:fields][0][:condition] = user_condition['condition'] + expect( + CustomWizard::TemplateValidator.new(template).perform + ).to eq(false) + end + + it "invalidates pro actions without a pro subscription" do + template[:actions] << create_category + expect( + CustomWizard::TemplateValidator.new(template).perform + ).to eq(false) + end + + context "with pro subscription" do + before do + enable_pro + end + + it "validates pro step attributes" do + template[:steps][0][:condition] = user_condition['condition'] + expect( + CustomWizard::TemplateValidator.new(template).perform + ).to eq(true) + end + + it "validates pro field attributes" do + template[:steps][0][:fields][0][:condition] = user_condition['condition'] + expect( + CustomWizard::TemplateValidator.new(template).perform + ).to eq(true) + end + + it "validates pro actions" do + template[:actions] << create_category + expect( + CustomWizard::TemplateValidator.new(template).perform + ).to eq(true) + end + end end diff --git a/spec/components/custom_wizard/update_validator_spec.rb b/spec/components/custom_wizard/update_validator_spec.rb index e976e1ff..c79a5b0b 100644 --- a/spec/components/custom_wizard/update_validator_spec.rb +++ b/spec/components/custom_wizard/update_validator_spec.rb @@ -3,12 +3,7 @@ require_relative '../../plugin_helper' describe CustomWizard::UpdateValidator do fab!(:user) { Fabricate(:user) } - - let(:template) { - JSON.parse(File.open( - "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json" - ).read).with_indifferent_access - } + let(:template) { get_wizard_fixture("wizard") } before do CustomWizard::Template.save(template, skip_jobs: true) diff --git a/spec/components/custom_wizard/wizard_spec.rb b/spec/components/custom_wizard/wizard_spec.rb index 67905f5a..9cccff97 100644 --- a/spec/components/custom_wizard/wizard_spec.rb +++ b/spec/components/custom_wizard/wizard_spec.rb @@ -6,22 +6,8 @@ describe CustomWizard::Wizard do fab!(:user) { Fabricate(:user) } fab!(:trusted_user) { Fabricate(:user, trust_level: TrustLevel[3]) } fab!(:admin_user) { Fabricate(:user, admin: true) } - - let(:template_json) { - JSON.parse( - File.open( - "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json" - ).read - ) - } - - let(:permitted_json) { - JSON.parse( - File.open( - "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard/permitted.json" - ).read - ) - } + let(:template_json) { get_wizard_fixture("wizard") } + let(:permitted_json) { get_wizard_fixture("wizard/permitted") } before do Group.refresh_automatic_group!(:trust_level_3) diff --git a/spec/extensions/custom_field_extensions_spec.rb b/spec/extensions/custom_field_extensions_spec.rb index f0ce32f5..bf1b3ff2 100644 --- a/spec/extensions/custom_field_extensions_spec.rb +++ b/spec/extensions/custom_field_extensions_spec.rb @@ -9,11 +9,8 @@ describe "custom field extensions" do fab!(:group) { Fabricate(:group) } fab!(:user) { Fabricate(:user) } - let(:custom_field_json) { - JSON.parse(File.open( - "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/custom_field/custom_fields.json" - ).read) - } + let(:custom_field_json) { get_wizard_fixture("custom_field/custom_fields") } + let(:pro_custom_field_json) { get_wizard_fixture("custom_field/pro_custom_fields") } before do custom_field_json['custom_fields'].each do |field_json| @@ -75,43 +72,54 @@ describe "custom field extensions" do end end - context "category" do - it "registers category custom fields" do - category - expect(Category.get_custom_field_type("category_field_1")).to eq(:json) + context "pro custom fields" do + before do + enable_pro + + pro_custom_field_json['custom_fields'].each do |field_json| + custom_field = CustomWizard::CustomField.new(nil, field_json) + custom_field.save + end end - it "adds category custom fields to the basic category serializer" do - category.custom_fields["category_field_1"] = { a: 1, b: 2 }.to_json - category.save_custom_fields(true) + context "category" do + it "registers" do + category + expect(Category.get_custom_field_type("category_field_1")).to eq(:json) + end - serializer = BasicCategorySerializer.new( - category, - scope: Guardian.new(user), - root: false - ).as_json + it "adds custom fields to the basic category serializer" do + category.custom_fields["category_field_1"] = { a: 1, b: 2 }.to_json + category.save_custom_fields(true) - expect(serializer[:category_field_1]).to eq({ a: 1, b: 2 }.to_json) - end - end + serializer = BasicCategorySerializer.new( + category, + scope: Guardian.new(user), + root: false + ).as_json - context "group" do - it "registers group custom fields" do - group - expect(Group.get_custom_field_type("group_field_1")).to eq(:string) + expect(serializer[:category_field_1]).to eq({ a: 1, b: 2 }.to_json) + end end - it "adds group custom fields to the basic group serializer" do - group.custom_fields["group_field_1"] = "Hello" - group.save_custom_fields(true) + context "group" do + it "registers" do + group + expect(Group.get_custom_field_type("group_field_1")).to eq(:string) + end - serializer = BasicGroupSerializer.new( - group, - scope: Guardian.new(user), - root: false - ).as_json + it "adds custom fields to the basic group serializer" do + group.custom_fields["group_field_1"] = "Hello" + group.save_custom_fields(true) - expect(serializer[:group_field_1]).to eq("Hello") + serializer = BasicGroupSerializer.new( + group, + scope: Guardian.new(user), + root: false + ).as_json + + expect(serializer[:group_field_1]).to eq("Hello") + end end end end diff --git a/spec/extensions/extra_locales_controller_spec.rb b/spec/extensions/extra_locales_controller_spec.rb index a71e39c4..bb451b7c 100644 --- a/spec/extensions/extra_locales_controller_spec.rb +++ b/spec/extensions/extra_locales_controller_spec.rb @@ -4,18 +4,8 @@ require_relative '../plugin_helper' describe ExtraLocalesControllerCustomWizard, type: :request do let(:new_user) { Fabricate(:user, trust_level: TrustLevel[0]) } let(:staff_user) { Fabricate(:moderator) } - - let(:template) { - JSON.parse(File.open( - "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json" - ).read) - } - - let(:permitted) { - JSON.parse(File.open( - "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard/permitted.json" - ).read) - } + let(:template) { get_wizard_fixture("wizard") } + let(:permitted) { get_wizard_fixture("wizard/permitted") } before do CustomWizard::Template.save(template, skip_jobs: true) diff --git a/spec/extensions/invites_controller_spec.rb b/spec/extensions/invites_controller_spec.rb index 47c4ca84..93fdd9d7 100644 --- a/spec/extensions/invites_controller_spec.rb +++ b/spec/extensions/invites_controller_spec.rb @@ -4,12 +4,7 @@ require_relative '../plugin_helper' describe InvitesControllerCustomWizard, type: :request do fab!(:topic) { Fabricate(:topic) } let(:invite) { Invite.generate(topic.user, email: "angus@mcleod.org", topic: topic) } - - let(:template) do - JSON.parse(File.open( - "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json" - ).read) - end + let(:template) { get_wizard_fixture("wizard") } before do @controller = InvitesController.new diff --git a/spec/extensions/users_controller_spec.rb b/spec/extensions/users_controller_spec.rb index f4ba8e51..e4cd972f 100644 --- a/spec/extensions/users_controller_spec.rb +++ b/spec/extensions/users_controller_spec.rb @@ -2,11 +2,7 @@ require_relative '../plugin_helper' describe CustomWizardUsersController, type: :request do - let(:template) do - JSON.parse(File.open( - "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json" - ).read) - end + let(:template) { get_wizard_fixture("wizard") } before do @controller = UsersController.new diff --git a/spec/fixtures/actions/add_to_group.json b/spec/fixtures/actions/add_to_group.json new file mode 100644 index 00000000..2a5af1c3 --- /dev/null +++ b/spec/fixtures/actions/add_to_group.json @@ -0,0 +1,13 @@ +{ + "id": "action_6", + "run_after": "step_1", + "type": "add_to_group", + "group": [ + { + "type": "assignment", + "output": "action_9", + "output_type": "wizard_action", + "output_connector": "set" + } + ] +} \ No newline at end of file diff --git a/spec/fixtures/actions/create_category.json b/spec/fixtures/actions/create_category.json new file mode 100644 index 00000000..c5f3d5af --- /dev/null +++ b/spec/fixtures/actions/create_category.json @@ -0,0 +1,51 @@ +{ + "id": "action_8", + "run_after": "step_1", + "type": "create_category", + "custom_fields": [ + { + "type": "association", + "pairs": [ + { + "index": 0, + "key": "category_custom_field", + "key_type": "text", + "value": "CC Val", + "value_type": "text", + "connector": "association" + } + ] + } + ], + "name": [ + { + "type": "assignment", + "output": "step_1_field_1", + "output_type": "wizard_field", + "output_connector": "set" + } + ], + "slug": [ + { + "type": "assignment", + "output": "step_1_field_1", + "output_type": "wizard_field", + "output_connector": "set" + } + ], + "permissions": [ + { + "type": "association", + "pairs": [ + { + "index": 0, + "key": "action_9", + "key_type": "wizard_action", + "value": "2", + "value_type": "text", + "connector": "association" + } + ] + } + ] +} \ No newline at end of file diff --git a/spec/fixtures/actions/create_group.json b/spec/fixtures/actions/create_group.json new file mode 100644 index 00000000..e2e52ef2 --- /dev/null +++ b/spec/fixtures/actions/create_group.json @@ -0,0 +1,104 @@ +{ + "id": "action_9", + "run_after": "step_1", + "type": "create_group", + "title": [ + { + "type": "assignment", + "output": "New Group Member", + "output_type": "text", + "output_connector": "set" + } + ], + "custom_fields": [ + { + "type": "association", + "pairs": [ + { + "index": 0, + "key": "group_custom_field", + "key_type": "text", + "value": "step_3_field_1", + "value_type": "wizard_field", + "connector": "association" + } + ] + } + ], + "name": [ + { + "type": "assignment", + "output": "step_1_field_1", + "output_type": "wizard_field", + "output_connector": "set" + } + ], + "full_name": [ + { + "type": "assignment", + "output": "step_1_field_1", + "output_type": "wizard_field", + "output_connector": "set" + } + ], + "usernames": [ + { + "type": "assignment", + "output_type": "user", + "output_connector": "set", + "output": [ + "angus1" + ] + } + ], + "owner_usernames": [ + { + "type": "assignment", + "output_type": "user", + "output_connector": "set", + "output": [ + "angus" + ] + } + ], + "grant_trust_level": [ + { + "type": "assignment", + "output": "3", + "output_type": "text", + "output_connector": "set" + } + ], + "mentionable_level": [ + { + "type": "assignment", + "output": "1", + "output_type": "text", + "output_connector": "set" + } + ], + "messageable_level": [ + { + "type": "assignment", + "output": "2", + "output_type": "text", + "output_connector": "set" + } + ], + "visibility_level": [ + { + "type": "assignment", + "output": "3", + "output_type": "text", + "output_connector": "set" + } + ], + "members_visibility_level": [ + { + "type": "assignment", + "output": "99", + "output_type": "text", + "output_connector": "set" + } + ] +} \ No newline at end of file diff --git a/spec/fixtures/actions/send_message.json b/spec/fixtures/actions/send_message.json new file mode 100644 index 00000000..ddc9bf10 --- /dev/null +++ b/spec/fixtures/actions/send_message.json @@ -0,0 +1,25 @@ +{ + "id": "action_2", + "run_after": "step_2", + "type": "send_message", + "post_builder": true, + "post_template": "I will interpolate some wizard fields w{step_1_field_1} w{step_1_field_2}", + "title": [ + { + "type": "assignment", + "output": "Message title", + "output_type": "text", + "output_connector": "set" + } + ], + "recipient": [ + { + "type": "assignment", + "output_type": "user", + "output_connector": "set", + "output": [ + "angus1" + ] + } + ] +} \ No newline at end of file diff --git a/spec/fixtures/actions/send_message_multi.json b/spec/fixtures/actions/send_message_multi.json new file mode 100644 index 00000000..8ab0fdb7 --- /dev/null +++ b/spec/fixtures/actions/send_message_multi.json @@ -0,0 +1,28 @@ +{ + "id": "action_11", + "run_after": "step_2", + "type": "send_message", + "post_builder": true, + "post_template": "I will interpolate some wizard fields w{step_1_field_1} w{step_1_field_2}", + "title": [ + { + "type": "assignment", + "output": "Multiple Recipients title", + "output_type": "text", + "output_connector": "set" + } + ], + "recipient": [ + { + "type": "assignment", + "output_type": "user", + "output_connector": "set", + "output": [ + "angus1", + "faiz", + "cool_group", + "cool_group_1" + ] + } + ] +} \ No newline at end of file diff --git a/spec/fixtures/custom_field/custom_fields.json b/spec/fixtures/custom_field/custom_fields.json index 0e7741ce..c9b98476 100644 --- a/spec/fixtures/custom_field/custom_fields.json +++ b/spec/fixtures/custom_field/custom_fields.json @@ -16,22 +16,6 @@ "serializers": [ "post" ] - }, - { - "klass": "category", - "name": "category_field_1", - "type": "json", - "serializers": [ - "basic_category" - ] - }, - { - "klass": "group", - "name": "group_field_1", - "type": "string", - "serializers": [ - "basic_group" - ] } ] } \ No newline at end of file diff --git a/spec/fixtures/custom_field/pro_custom_fields.json b/spec/fixtures/custom_field/pro_custom_fields.json new file mode 100644 index 00000000..b7060bdf --- /dev/null +++ b/spec/fixtures/custom_field/pro_custom_fields.json @@ -0,0 +1,28 @@ +{ + "custom_fields": [ + { + "klass": "topic", + "name": "topic_field_2", + "type": "json", + "serializers": [ + "topic_view" + ] + }, + { + "klass": "category", + "name": "category_field_1", + "type": "json", + "serializers": [ + "basic_category" + ] + }, + { + "klass": "group", + "name": "group_field_1", + "type": "string", + "serializers": [ + "basic_group" + ] + } + ] +} \ No newline at end of file diff --git a/spec/fixtures/wizard.json b/spec/fixtures/wizard.json index a505c0d3..1560acef 100644 --- a/spec/fixtures/wizard.json +++ b/spec/fixtures/wizard.json @@ -163,197 +163,6 @@ } ], "actions": [ - { - "id": "action_9", - "run_after": "step_1", - "type": "create_group", - "title": [ - { - "type": "assignment", - "output": "New Group Member", - "output_type": "text", - "output_connector": "set" - } - ], - "custom_fields": [ - { - "type": "association", - "pairs": [ - { - "index": 0, - "key": "group_custom_field", - "key_type": "text", - "value": "step_3_field_1", - "value_type": "wizard_field", - "connector": "association" - } - ] - } - ], - "name": [ - { - "type": "assignment", - "output": "step_1_field_1", - "output_type": "wizard_field", - "output_connector": "set" - } - ], - "full_name": [ - { - "type": "assignment", - "output": "step_1_field_1", - "output_type": "wizard_field", - "output_connector": "set" - } - ], - "usernames": [ - { - "type": "assignment", - "output_type": "user", - "output_connector": "set", - "output": [ - "angus1" - ] - } - ], - "owner_usernames": [ - { - "type": "assignment", - "output_type": "user", - "output_connector": "set", - "output": [ - "angus" - ] - } - ], - "grant_trust_level": [ - { - "type": "assignment", - "output": "3", - "output_type": "text", - "output_connector": "set" - } - ], - "mentionable_level": [ - { - "type": "assignment", - "output": "1", - "output_type": "text", - "output_connector": "set" - } - ], - "messageable_level": [ - { - "type": "assignment", - "output": "2", - "output_type": "text", - "output_connector": "set" - } - ], - "visibility_level": [ - { - "type": "assignment", - "output": "3", - "output_type": "text", - "output_connector": "set" - } - ], - "members_visibility_level": [ - { - "type": "assignment", - "output": "99", - "output_type": "text", - "output_connector": "set" - } - ] - }, - { - "id": "action_6", - "run_after": "step_1", - "type": "add_to_group", - "group": [ - { - "type": "assignment", - "output": "action_9", - "output_type": "wizard_action", - "output_connector": "set" - } - ] - }, - { - "id": "action_8", - "run_after": "step_1", - "type": "create_category", - "custom_fields": [ - { - "type": "association", - "pairs": [ - { - "index": 0, - "key": "category_custom_field", - "key_type": "text", - "value": "CC Val", - "value_type": "text", - "connector": "association" - } - ] - } - ], - "name": [ - { - "type": "assignment", - "output": "step_1_field_1", - "output_type": "wizard_field", - "output_connector": "set" - } - ], - "slug": [ - { - "type": "assignment", - "output": "step_1_field_1", - "output_type": "wizard_field", - "output_connector": "set" - } - ], - "permissions": [ - { - "type": "association", - "pairs": [ - { - "index": 0, - "key": "action_9", - "key_type": "wizard_action", - "value": "2", - "value_type": "text", - "connector": "association" - } - ] - } - ] - }, - { - "id": "action_5", - "run_after": "step_1", - "type": "watch_categories", - "notification_level": "tracking", - "wizard_user": true, - "categories": [ - { - "type": "assignment", - "output": "action_8", - "output_type": "wizard_action", - "output_connector": "set" - } - ], - "mute_remainder": [ - { - "type": "assignment", - "output": "true", - "output_type": "text", - "output_connector": "set" - } - ] - }, { "id": "action_1", "run_after": "step_3", @@ -442,6 +251,29 @@ } ] }, + { + "id": "action_5", + "run_after": "step_1", + "type": "watch_categories", + "notification_level": "tracking", + "wizard_user": true, + "categories": [ + { + "type": "assignment", + "output": "action_8", + "output_type": "wizard_action", + "output_connector": "set" + } + ], + "mute_remainder": [ + { + "type": "assignment", + "output": "true", + "output_type": "text", + "output_connector": "set" + } + ] + }, { "id": "action_4", "run_after": "step_2", @@ -462,59 +294,6 @@ } ] }, - { - "id": "action_2", - "run_after": "step_2", - "type": "send_message", - "post_builder": true, - "post_template": "I will interpolate some wizard fields w{step_1_field_1} w{step_1_field_2}", - "title": [ - { - "type": "assignment", - "output": "Message title", - "output_type": "text", - "output_connector": "set" - } - ], - "recipient": [ - { - "type": "assignment", - "output_type": "user", - "output_connector": "set", - "output": [ - "angus1" - ] - } - ] - }, - { - "id": "action_11", - "run_after": "step_2", - "type": "send_message", - "post_builder": true, - "post_template": "I will interpolate some wizard fields w{step_1_field_1} w{step_1_field_2}", - "title": [ - { - "type": "assignment", - "output": "Multiple Recipients title", - "output_type": "text", - "output_connector": "set" - } - ], - "recipient": [ - { - "type": "assignment", - "output_type": "user", - "output_connector": "set", - "output": [ - "angus1", - "faiz", - "cool_group", - "cool_group_1" - ] - } - ] - }, { "id": "action_3", "run_after": "step_2", @@ -529,24 +308,6 @@ "output_connector": "set" } ], - "category": [ - { - "type": "assignment", - "output": "action_8", - "output_type": "wizard_action", - "output_connector": "set", - "pairs": [ - { - "index": 0, - "key": "step_2_field_5", - "key_type": "wizard_field", - "value": "true", - "value_type": "text", - "connector": "is" - } - ] - } - ], "tags": [ { "type": "assignment", diff --git a/spec/jobs/set_after_time_wizard_spec.rb b/spec/jobs/set_after_time_wizard_spec.rb index 35576f01..93e46c8b 100644 --- a/spec/jobs/set_after_time_wizard_spec.rb +++ b/spec/jobs/set_after_time_wizard_spec.rb @@ -7,11 +7,7 @@ describe Jobs::SetAfterTimeWizard do fab!(:user2) { Fabricate(:user) } fab!(:user3) { Fabricate(:user) } - let(:template) { - JSON.parse(File.open( - "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json" - ).read).with_indifferent_access - } + let(:template) { get_wizard_fixture("wizard") } it "sets wizard redirect for all users " do after_time_template = template.dup diff --git a/spec/jobs/update_pro_subscription_spec.rb b/spec/jobs/update_pro_subscription_spec.rb new file mode 100644 index 00000000..0aae9668 --- /dev/null +++ b/spec/jobs/update_pro_subscription_spec.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +require_relative '../plugin_helper' + +describe CustomWizard::UpdateProSubscription do + it "updates the pro subscription" do + stub_subscription_request(200, valid_subscription) + described_class.new.execute + expect(CustomWizard::Pro.subscribed?).to eq(true) + end +end diff --git a/spec/plugin_helper.rb b/spec/plugin_helper.rb index 9e4bbbbe..4aa34029 100644 --- a/spec/plugin_helper.rb +++ b/spec/plugin_helper.rb @@ -15,3 +15,44 @@ require 'oj' Oj.default_options = Oj.default_options.merge(cache_str: -1) require 'rails_helper' + +def get_wizard_fixture(path) + JSON.parse( + File.open( + "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/#{path}.json" + ).read + ).with_indifferent_access +end + +def authenticate_pro + CustomWizard::ProAuthentication.any_instance.stubs(:active?).returns(true) +end + +def enable_pro + CustomWizard::Pro.any_instance.stubs(:subscribed?).returns(true) +end + +def disable_pro + CustomWizard::Pro.any_instance.stubs(:subscribed?).returns(false) +end + +def valid_subscription + { + product_id: "prod_CBTNpi3fqWWkq0", + price_id: "price_id", + price_nickname: "business" + } +end + +def invalid_subscription + { + product_id: "prod_CBTNpi3fqWWkq0", + price_id: "price_id" + } +end + +def stub_subscription_request(status, subscription) + authenticate_pro + pro = CustomWizard::Pro.new + stub_request(:get, "https://#{pro.server}/subscription-server/user-subscriptions/#{pro.subscription_type}/#{pro.client_name}").to_return(status: status, body: { subscriptions: [subscription] }.to_json) +end diff --git a/spec/requests/custom_wizard/admin/custom_fields_controller_spec.rb b/spec/requests/custom_wizard/admin/custom_fields_controller_spec.rb index 8c1a8550..d2e086d2 100644 --- a/spec/requests/custom_wizard/admin/custom_fields_controller_spec.rb +++ b/spec/requests/custom_wizard/admin/custom_fields_controller_spec.rb @@ -3,12 +3,7 @@ require_relative '../../../plugin_helper' describe CustomWizard::AdminCustomFieldsController do fab!(:admin_user) { Fabricate(:user, admin: true) } - - let(:custom_field_json) { - JSON.parse(File.open( - "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/custom_field/custom_fields.json" - ).read) - } + let(:custom_field_json) { get_wizard_fixture("custom_field/custom_fields") } before do custom_field_json['custom_fields'].each do |field_json| @@ -19,7 +14,7 @@ describe CustomWizard::AdminCustomFieldsController do it "returns the full list of custom fields" do get "/admin/wizards/custom-fields.json" - expect(response.parsed_body.length).to eq(15) + expect(response.parsed_body["custom_fields"].length).to be > 12 end it "saves custom fields" do diff --git a/spec/requests/custom_wizard/admin/manager_controller_spec.rb b/spec/requests/custom_wizard/admin/manager_controller_spec.rb index 87c980f2..6218ea7e 100644 --- a/spec/requests/custom_wizard/admin/manager_controller_spec.rb +++ b/spec/requests/custom_wizard/admin/manager_controller_spec.rb @@ -3,12 +3,7 @@ require_relative '../../../plugin_helper' describe CustomWizard::AdminManagerController do fab!(:admin_user) { Fabricate(:user, admin: true) } - - let(:template) { - JSON.parse(File.open( - "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json" - ).read) - } + let(:template) { get_wizard_fixture("wizard") } before do sign_in(admin_user) diff --git a/spec/requests/custom_wizard/admin/pro_controller_spec.rb b/spec/requests/custom_wizard/admin/pro_controller_spec.rb new file mode 100644 index 00000000..049bb88e --- /dev/null +++ b/spec/requests/custom_wizard/admin/pro_controller_spec.rb @@ -0,0 +1,71 @@ +# frozen_string_literal: true +require_relative '../../../plugin_helper' + +describe CustomWizard::AdminProController do + fab!(:admin_user) { Fabricate(:user, admin: true) } + + def generate_payload(request_id, user_id) + uri = URI(@pro.authentication_url(user_id, request_id)) + keys = @pro.authentication.get_keys(request_id) + raw_payload = { + key: "12345", + nonce: keys.nonce, + push: false, + api: UserApiKeysController::AUTH_API_VERSION + }.to_json + public_key = OpenSSL::PKey::RSA.new(keys.pem) + Base64.encode64(public_key.public_encrypt(raw_payload)) + end + + before do + @pro = CustomWizard::Pro.new + sign_in(admin_user) + end + + it "#index" do + get "/admin/wizards/pro.json" + expect(response.parsed_body['server']).to eq(@pro.server) + expect(response.parsed_body['authentication'].deep_symbolize_keys).to eq(CustomWizard::ProAuthenticationSerializer.new(@pro.authentication, root: false).as_json) + expect(response.parsed_body['subscription'].deep_symbolize_keys).to eq(CustomWizard::ProSubscriptionSerializer.new(@pro.subscription, root: false).as_json) + end + + it "#authorize" do + get "/admin/wizards/pro/authorize" + expect(response.status).to eq(302) + expect(cookies[:user_api_request_id].present?).to eq(true) + end + + it "#destroy_authentication" do + request_id = SecureRandom.hex(32) + payload = generate_payload(request_id, admin_user.id) + @pro.authentication_response(request_id, payload) + + delete "/admin/wizards/pro/authorize.json" + + expect(response.status).to eq(200) + expect(CustomWizard::Pro.authorized?).to eq(false) + end + + context "subscription" do + before do + stub_subscription_request(200, valid_subscription) + end + + it "handles authentication response and the updates subscription" do + request_id = cookies[:user_api_request_id] = SecureRandom.hex(32) + payload = generate_payload(request_id, admin_user.id) + get "/admin/wizards/pro/authorize/callback", params: { payload: payload } + + expect(response).to redirect_to("/admin/wizards/pro") + expect(CustomWizard::Pro.subscribed?).to eq(true) + end + + it "updates the subscription" do + authenticate_pro + post "/admin/wizards/pro/subscription.json" + + expect(response.status).to eq(200) + expect(CustomWizard::Pro.subscribed?).to eq(true) + end + end +end \ No newline at end of file diff --git a/spec/requests/custom_wizard/admin/submissions_controller_spec.rb b/spec/requests/custom_wizard/admin/submissions_controller_spec.rb index 36296e95..5a79679f 100644 --- a/spec/requests/custom_wizard/admin/submissions_controller_spec.rb +++ b/spec/requests/custom_wizard/admin/submissions_controller_spec.rb @@ -7,12 +7,7 @@ describe CustomWizard::AdminSubmissionsController do fab!(:user2) { Fabricate(:user) } fab!(:user3) { Fabricate(:user) } - let(:template) { - JSON.parse(File.open( - "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json" - ).read) - } - + let(:template) { get_wizard_fixture("wizard") } let(:template_2) { temp = template.dup temp["id"] = "super_mega_fun_wizard_2" diff --git a/spec/requests/custom_wizard/admin/wizard_controller_spec.rb b/spec/requests/custom_wizard/admin/wizard_controller_spec.rb index 82aa4fc5..9d7ed18d 100644 --- a/spec/requests/custom_wizard/admin/wizard_controller_spec.rb +++ b/spec/requests/custom_wizard/admin/wizard_controller_spec.rb @@ -5,12 +5,7 @@ describe CustomWizard::AdminWizardController do fab!(:admin_user) { Fabricate(:user, admin: true) } fab!(:user1) { Fabricate(:user) } fab!(:user2) { Fabricate(:user) } - - let(:template) { - JSON.parse(File.open( - "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json" - ).read) - } + let(:template) { get_wizard_fixture("wizard") } before do CustomWizard::Template.save(template, skip_jobs: true) diff --git a/spec/requests/custom_wizard/application_controller_spec.rb b/spec/requests/custom_wizard/application_controller_spec.rb index 0835f246..f92ac7c0 100644 --- a/spec/requests/custom_wizard/application_controller_spec.rb +++ b/spec/requests/custom_wizard/application_controller_spec.rb @@ -2,21 +2,11 @@ require_relative '../../plugin_helper' describe ApplicationController do - fab!(:user) { - Fabricate( - :user, - username: 'angus', - email: "angus@email.com", - trust_level: TrustLevel[3] - ) - } + fab!(:user) { Fabricate(:user, username: 'angus', email: "angus@email.com", trust_level: TrustLevel[3]) } + let(:wizard_template) { get_wizard_fixture("wizard") } before do - CustomWizard::Template.save( - JSON.parse(File.open( - "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json" - ).read), - skip_jobs: true) + CustomWizard::Template.save(wizard_template, skip_jobs: true) @template = CustomWizard::Template.find('super_mega_fun_wizard') end diff --git a/spec/requests/custom_wizard/custom_field_extensions_spec.rb b/spec/requests/custom_wizard/custom_field_extensions_spec.rb index b991769a..64d7c755 100644 --- a/spec/requests/custom_wizard/custom_field_extensions_spec.rb +++ b/spec/requests/custom_wizard/custom_field_extensions_spec.rb @@ -8,12 +8,8 @@ describe "custom field extensions" do let!(:category) { Fabricate(:category) } let!(:user) { Fabricate(:user) } let!(:group) { Fabricate(:group, users: [user]) } - - let(:custom_field_json) { - JSON.parse(File.open( - "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/custom_field/custom_fields.json" - ).read) - } + let(:custom_field_json) { get_wizard_fixture("custom_field/custom_fields") } + let(:pro_custom_field_json) { get_wizard_fixture("custom_field/pro_custom_fields") } before do custom_field_json['custom_fields'].each do |field_json| @@ -32,27 +28,6 @@ describe "custom field extensions" do expect(response.parsed_body["topic_field_1"]).to eq(true) end - it "adds category custom fields to the show categories response" do - category.custom_fields["category_field_1"] = { a: 1, b: 2 } - category.save_custom_fields(true) - - get "/c/#{category.id}/show.json" - - expect(response.status).to eq(200) - expect(response.parsed_body["category"]["category_field_1"]).to eq({ a: 1, b: 2 }.as_json) - end - - it "adds group custom fields to the show group response" do - group.custom_fields["group_field_1"] = "Group cf entry" - group.save_custom_fields(true) - - sign_in(user) - get "/groups/#{group.name}.json" - - expect(response.status).to eq(200) - expect(response.parsed_body['group']['group_field_1']).to eq("Group cf entry") - end - it "adds post custom fields to the show post response" do post.custom_fields["post_field_1"] = 7 post.save_custom_fields(true) @@ -63,32 +38,64 @@ describe "custom field extensions" do expect(response.parsed_body['post_field_1']).to eq(7) end - context "preloaded" do - it "preloads category custom fields on site categories" do - Site.preloaded_category_custom_fields << "other_field" + context "with a pro subscription" do + before do + enable_pro + pro_custom_field_json['custom_fields'].each do |field_json| + custom_field = CustomWizard::CustomField.new(nil, field_json) + custom_field.save + end + end + + it "adds category custom fields to the show categories response" do category.custom_fields["category_field_1"] = { a: 1, b: 2 } category.save_custom_fields(true) - get "/site.json" - expect(response.status).to eq(200) + get "/c/#{category.id}/show.json" - site_category = response.parsed_body['categories'].select { |c| c['id'] == category.id }.first - expect(site_category["category_field_1"]).to eq({ a: 1, b: 2 }.as_json) + expect(response.status).to eq(200) + expect(response.parsed_body["category"]["category_field_1"]).to eq({ a: 1, b: 2 }.as_json) end - it "preloads group custom fields on group index" do - Group.preloaded_custom_field_names << "other_field" - - group = Fabricate(:group) + it "adds group custom fields to the show group response" do group.custom_fields["group_field_1"] = "Group cf entry" group.save_custom_fields(true) - get "/groups.json" - expect(response.status).to eq(200) + sign_in(user) + get "/groups/#{group.name}.json" - group = response.parsed_body['groups'].select { |g| g['id'] == group.id }.first - expect(group['group_field_1']).to eq("Group cf entry") + expect(response.status).to eq(200) + expect(response.parsed_body['group']['group_field_1']).to eq("Group cf entry") + end + + context "preloaded" do + it "preloads category custom fields on site categories" do + Site.preloaded_category_custom_fields << "other_field" + + category.custom_fields["category_field_1"] = { a: 1, b: 2 } + category.save_custom_fields(true) + + get "/site.json" + expect(response.status).to eq(200) + + site_category = response.parsed_body['categories'].select { |c| c['id'] == category.id }.first + expect(site_category["category_field_1"]).to eq({ a: 1, b: 2 }.as_json) + end + + it "preloads group custom fields on group index" do + Group.preloaded_custom_field_names << "other_field" + + group = Fabricate(:group) + group.custom_fields["group_field_1"] = "Group cf entry" + group.save_custom_fields(true) + + get "/groups.json" + expect(response.status).to eq(200) + + group = response.parsed_body['groups'].select { |g| g['id'] == group.id }.first + expect(group['group_field_1']).to eq("Group cf entry") + end end end end diff --git a/spec/requests/custom_wizard/steps_controller_spec.rb b/spec/requests/custom_wizard/steps_controller_spec.rb index 5da75d8d..3564780f 100644 --- a/spec/requests/custom_wizard/steps_controller_spec.rb +++ b/spec/requests/custom_wizard/steps_controller_spec.rb @@ -2,55 +2,12 @@ require_relative '../../plugin_helper' describe CustomWizard::StepsController do - fab!(:user) { - Fabricate( - :user, - username: 'angus', - email: "angus@email.com", - trust_level: TrustLevel[3] - ) - } - - fab!(:user2) { - Fabricate( - :user, - username: 'bob', - email: "bob@email.com", - trust_level: TrustLevel[2] - ) - } - - let(:wizard_template) { - JSON.parse( - File.open( - "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json" - ).read - ) - } - - let(:wizard_field_condition_template) { - JSON.parse( - File.open( - "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/condition/wizard_field_condition.json" - ).read - ) - } - - let(:user_condition_template) { - JSON.parse( - File.open( - "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/condition/user_condition.json" - ).read - ) - } - - let(:permitted_json) { - JSON.parse( - File.open( - "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard/permitted.json" - ).read - ) - } + fab!(:user) { Fabricate(:user, username: 'angus', email: "angus@email.com", trust_level: TrustLevel[3]) } + fab!(:user2) { Fabricate(:user, username: 'bob', email: "bob@email.com", trust_level: TrustLevel[2]) } + let(:wizard_template) { get_wizard_fixture("wizard") } + let(:wizard_field_condition_template) { get_wizard_fixture("condition/wizard_field_condition") } + let(:user_condition_template) { get_wizard_fixture("condition/user_condition") } + let(:permitted_json) { get_wizard_fixture("wizard/permitted") } before do CustomWizard::Template.save(wizard_template, skip_jobs: true) @@ -90,17 +47,6 @@ describe CustomWizard::StepsController do put '/w/super-mega-fun-wizard/steps/step_10.json' expect(response.status).to eq(400) end - - it "when user cant see the step due to conditions" do - sign_in(user2) - - new_wizard_template = wizard_template.dup - new_wizard_template['steps'][0]['condition'] = user_condition_template['condition'] - CustomWizard::Template.save(new_wizard_template, skip_jobs: true) - - put '/w/super-mega-fun-wizard/steps/step_1.json' - expect(response.status).to eq(403) - end end it "works if the step has no fields" do @@ -123,64 +69,40 @@ describe CustomWizard::StepsController do expect(response.parsed_body['wizard']['start']).to eq("step_2") end - it "returns an updated wizard when condition doesnt pass" do - new_template = wizard_template.dup - new_template['steps'][1]['condition'] = wizard_field_condition_template['condition'] - CustomWizard::Template.save(new_template, skip_jobs: true) - - put '/w/super-mega-fun-wizard/steps/step_1.json', params: { - fields: { - step_1_field_1: "Condition wont pass" - } - } - expect(response.status).to eq(200) - expect(response.parsed_body['wizard']['start']).to eq("step_3") - end - it "runs completion actions if user has completed wizard" do new_template = wizard_template.dup ## route_to action new_template['actions'].last['run_after'] = 'wizard_completion' - new_template['steps'][1]['condition'] = wizard_field_condition_template['condition'] - new_template['steps'][2]['condition'] = wizard_field_condition_template['condition'] CustomWizard::Template.save(new_template, skip_jobs: true) - put '/w/super-mega-fun-wizard/steps/step_1.json', params: { - fields: { - step_1_field_1: "Condition wont pass" - } - } + put '/w/super-mega-fun-wizard/steps/step_1.json' + put '/w/super-mega-fun-wizard/steps/step_2.json' + put '/w/super-mega-fun-wizard/steps/step_3.json' expect(response.status).to eq(200) expect(response.parsed_body['redirect_on_complete']).to eq("https://google.com") end it "saves results of completion actions if user has completed wizard" do new_template = wizard_template.dup - - ## Create group action new_template['actions'].first['run_after'] = 'wizard_completion' - new_template['steps'][1]['condition'] = wizard_field_condition_template['condition'] CustomWizard::Template.save(new_template, skip_jobs: true) put '/w/super-mega-fun-wizard/steps/step_1.json', params: { fields: { - step_1_field_1: "My cool group" + step_1_field_1: "Topic title", + step_1_field_2: "Topic post" } } - expect(response.status).to eq(200) - - put '/w/super-mega-fun-wizard/steps/step_3.json' - expect(response.status).to eq(200) + put '/w/super-mega-fun-wizard/steps/step_2.json' + put '/w/super-mega-fun-wizard/steps/step_3.json' wizard_id = response.parsed_body['wizard']['id'] wizard = CustomWizard::Wizard.create(wizard_id, user) - group_name = wizard.submissions.first.fields['action_9'] - group = Group.find_by(name: group_name) - - expect(group.present?).to eq(true) - expect(group.full_name).to eq("My cool group") + topic_id = wizard.submissions.first.fields[new_template['actions'].first['id']] + topic = Topic.find(topic_id) + expect(topic.present?).to eq(true) end it "returns a final step without conditions" do @@ -197,88 +119,119 @@ describe CustomWizard::StepsController do expect(response.parsed_body['final']).to eq(true) end - it "returns the correct final step when the conditional final step and last step are the same" do - new_template = wizard_template.dup - new_template['steps'][0]['condition'] = user_condition_template['condition'] - new_template['steps'][2]['condition'] = wizard_field_condition_template['condition'] - CustomWizard::Template.save(new_template, skip_jobs: true) + context "pro" do + before do + enable_pro + end - put '/w/super-mega-fun-wizard/steps/step_1.json', params: { - fields: { - step_1_field_1: "Condition will pass" + it "raises an error when user cant see the step due to conditions" do + sign_in(user2) + + new_wizard_template = wizard_template.dup + new_wizard_template['steps'][0]['condition'] = user_condition_template['condition'] + CustomWizard::Template.save(new_wizard_template, skip_jobs: true) + + put '/w/super-mega-fun-wizard/steps/step_1.json' + expect(response.status).to eq(403) + end + + it "returns an updated wizard when condition doesnt pass" do + new_template = wizard_template.dup + new_template['steps'][1]['condition'] = wizard_field_condition_template['condition'] + CustomWizard::Template.save(new_template, skip_jobs: true) + + put '/w/super-mega-fun-wizard/steps/step_1.json', params: { + fields: { + step_1_field_1: "Condition wont pass" + } } - } - expect(response.status).to eq(200) - expect(response.parsed_body['final']).to eq(false) + expect(response.status).to eq(200) + expect(response.parsed_body['wizard']['start']).to eq("step_3") + end - put '/w/super-mega-fun-wizard/steps/step_2.json' - expect(response.status).to eq(200) - expect(response.parsed_body['final']).to eq(false) + it "returns the correct final step when the conditional final step and last step are the same" do + new_template = wizard_template.dup + new_template['steps'][0]['condition'] = user_condition_template['condition'] + new_template['steps'][2]['condition'] = wizard_field_condition_template['condition'] + CustomWizard::Template.save(new_template, skip_jobs: true) - put '/w/super-mega-fun-wizard/steps/step_3.json' - expect(response.status).to eq(200) - expect(response.parsed_body['final']).to eq(true) - end - - it "returns the correct final step when the conditional final step and last step are different" do - new_template = wizard_template.dup - new_template['steps'][2]['condition'] = wizard_field_condition_template['condition'] - CustomWizard::Template.save(new_template, skip_jobs: true) - - put '/w/super-mega-fun-wizard/steps/step_1.json', params: { - fields: { - step_1_field_1: "Condition will not pass" + put '/w/super-mega-fun-wizard/steps/step_1.json', params: { + fields: { + step_1_field_1: "Condition will pass" + } } - } - expect(response.status).to eq(200) - expect(response.parsed_body['final']).to eq(false) + expect(response.status).to eq(200) + expect(response.parsed_body['final']).to eq(false) - put '/w/super-mega-fun-wizard/steps/step_2.json' - expect(response.status).to eq(200) - expect(response.parsed_body['final']).to eq(true) - end + put '/w/super-mega-fun-wizard/steps/step_2.json' + expect(response.status).to eq(200) + expect(response.parsed_body['final']).to eq(false) - it "returns the correct final step when the conditional final step is determined in the same action" do - new_template = wizard_template.dup - new_template['steps'][1]['condition'] = wizard_field_condition_template['condition'] - new_template['steps'][2]['condition'] = wizard_field_condition_template['condition'] - CustomWizard::Template.save(new_template, skip_jobs: true) + put '/w/super-mega-fun-wizard/steps/step_3.json' + expect(response.status).to eq(200) + expect(response.parsed_body['final']).to eq(true) + end - put '/w/super-mega-fun-wizard/steps/step_1.json', params: { - fields: { - step_1_field_1: "Condition will not pass" + it "returns the correct final step when the conditional final step and last step are different" do + new_template = wizard_template.dup + new_template['steps'][2]['condition'] = wizard_field_condition_template['condition'] + CustomWizard::Template.save(new_template, skip_jobs: true) + + put '/w/super-mega-fun-wizard/steps/step_1.json', params: { + fields: { + step_1_field_1: "Condition will not pass" + } } - } - expect(response.status).to eq(200) - expect(response.parsed_body['final']).to eq(true) - end + expect(response.status).to eq(200) + expect(response.parsed_body['final']).to eq(false) - it "excludes the non-included conditional fields from the submissions" do - new_template = wizard_template.dup - new_template['steps'][1]['fields'][0]['condition'] = wizard_field_condition_template['condition'] - CustomWizard::Template.save(new_template, skip_jobs: true) + put '/w/super-mega-fun-wizard/steps/step_2.json' + expect(response.status).to eq(200) + expect(response.parsed_body['final']).to eq(true) + end - put '/w/super-mega-fun-wizard/steps/step_1.json', params: { - fields: { - step_1_field_1: "Condition will pass" + it "returns the correct final step when the conditional final step is determined in the same action" do + new_template = wizard_template.dup + new_template['steps'][1]['condition'] = wizard_field_condition_template['condition'] + new_template['steps'][2]['condition'] = wizard_field_condition_template['condition'] + CustomWizard::Template.save(new_template, skip_jobs: true) + + put '/w/super-mega-fun-wizard/steps/step_1.json', params: { + fields: { + step_1_field_1: "Condition will not pass" + } } - } + expect(response.status).to eq(200) + expect(response.parsed_body['final']).to eq(true) + end - put '/w/super-mega-fun-wizard/steps/step_2.json', params: { - fields: { - step_2_field_1: "1995-04-23" + it "excludes the non-included conditional fields from the submissions" do + new_template = wizard_template.dup + new_template['steps'][1]['fields'][0]['condition'] = wizard_field_condition_template['condition'] + CustomWizard::Template.save(new_template, skip_jobs: true) + + put '/w/super-mega-fun-wizard/steps/step_1.json', params: { + fields: { + step_1_field_1: "Condition will pass" + } } - } - put '/w/super-mega-fun-wizard/steps/step_1.json', params: { - fields: { - step_1_field_1: "Condition will not pass" + put '/w/super-mega-fun-wizard/steps/step_2.json', params: { + fields: { + step_2_field_1: "1995-04-23" + } } - } - wizard_id = response.parsed_body['wizard']['id'] - wizard = CustomWizard::Wizard.create(wizard_id, user) - submission = wizard.current_submission - expect(submission.fields.keys).not_to include("step_2_field_1") + put '/w/super-mega-fun-wizard/steps/step_1.json', params: { + fields: { + step_1_field_1: "Condition will not pass" + } + } + + wizard_id = response.parsed_body['wizard']['id'] + wizard = CustomWizard::Wizard.create(wizard_id, user) + submission = wizard.current_submission + expect(submission.fields.keys).not_to include("step_2_field_1") + end end end diff --git a/spec/requests/custom_wizard/wizard_controller_spec.rb b/spec/requests/custom_wizard/wizard_controller_spec.rb index f2000bda..c28f8783 100644 --- a/spec/requests/custom_wizard/wizard_controller_spec.rb +++ b/spec/requests/custom_wizard/wizard_controller_spec.rb @@ -2,29 +2,12 @@ require_relative '../../plugin_helper' describe CustomWizard::WizardController do - fab!(:user) { - Fabricate( - :user, - username: 'angus', - email: "angus@email.com", - trust_level: TrustLevel[3] - ) - } - - let(:permitted_json) { - JSON.parse( - File.open( - "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard/permitted.json" - ).read - ) - } + fab!(:user) { Fabricate(:user, username: 'angus', email: "angus@email.com", trust_level: TrustLevel[3]) } + let(:wizard_template) { get_wizard_fixture("wizard") } + let(:permitted_json) { get_wizard_fixture("wizard/permitted") } before do - CustomWizard::Template.save( - JSON.parse(File.open( - "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json" - ).read), - skip_jobs: true) + CustomWizard::Template.save(wizard_template, skip_jobs: true) @template = CustomWizard::Template.find("super_mega_fun_wizard") sign_in(user) end diff --git a/spec/serializers/custom_wizard/basic_wizard_serializer_spec.rb b/spec/serializers/custom_wizard/basic_wizard_serializer_spec.rb index bf575827..6694e979 100644 --- a/spec/serializers/custom_wizard/basic_wizard_serializer_spec.rb +++ b/spec/serializers/custom_wizard/basic_wizard_serializer_spec.rb @@ -4,13 +4,10 @@ require_relative '../../plugin_helper' describe CustomWizard::BasicWizardSerializer do fab!(:user) { Fabricate(:user) } + let(:template) { get_wizard_fixture("wizard") } it 'should return basic wizard attributes' do - CustomWizard::Template.save( - JSON.parse(File.open( - "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json" - ).read), - skip_jobs: true) + CustomWizard::Template.save(template, skip_jobs: true) json = CustomWizard::BasicWizardSerializer.new( CustomWizard::Builder.new("super_mega_fun_wizard", user).build, scope: Guardian.new(user) diff --git a/spec/serializers/custom_wizard/custom_field_serializer_spec.rb b/spec/serializers/custom_wizard/custom_field_serializer_spec.rb index 4f5ffd72..2cf92f52 100644 --- a/spec/serializers/custom_wizard/custom_field_serializer_spec.rb +++ b/spec/serializers/custom_wizard/custom_field_serializer_spec.rb @@ -4,12 +4,7 @@ require_relative '../../plugin_helper' describe CustomWizard::CustomFieldSerializer do fab!(:user) { Fabricate(:user) } - - let(:custom_field_json) { - JSON.parse(File.open( - "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/custom_field/custom_fields.json" - ).read) - } + let(:custom_field_json) { get_wizard_fixture("custom_field/custom_fields") } it 'should return custom field attributes' do custom_field_json['custom_fields'].each do |field_json| diff --git a/spec/serializers/custom_wizard/pro/authentication_serializer_spec.rb b/spec/serializers/custom_wizard/pro/authentication_serializer_spec.rb new file mode 100644 index 00000000..53dd74c2 --- /dev/null +++ b/spec/serializers/custom_wizard/pro/authentication_serializer_spec.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +require_relative '../../../plugin_helper' + +describe CustomWizard::ProAuthenticationSerializer do + fab!(:user) { Fabricate(:user) } + + it 'should return pro authentication attributes' do + auth = CustomWizard::ProAuthentication.new(OpenStruct.new(key: '1234', auth_at: Time.now, auth_by: user.id)) + serialized = described_class.new(auth, root: false).as_json + + expect(serialized[:active]).to eq(true) + expect(serialized[:client_id]).to eq(auth.client_id) + expect(serialized[:auth_by]).to eq(auth.auth_by) + expect(serialized[:auth_at]).to eq(auth.auth_at) + end +end diff --git a/spec/serializers/custom_wizard/pro/subscription_serializer_spec.rb b/spec/serializers/custom_wizard/pro/subscription_serializer_spec.rb new file mode 100644 index 00000000..a775863e --- /dev/null +++ b/spec/serializers/custom_wizard/pro/subscription_serializer_spec.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +require_relative '../../../plugin_helper' + +describe CustomWizard::ProSubscriptionSerializer do + it 'should return pro subscription attributes' do + sub = CustomWizard::ProSubscription.new(OpenStruct.new(type: 'community', updated_at: Time.now)) + serialized = described_class.new(sub, root: false).as_json + + expect(serialized[:active]).to eq(true) + expect(serialized[:type]).to eq('community') + expect(serialized[:updated_at]).to eq(sub.updated_at) + end +end diff --git a/spec/serializers/custom_wizard/pro_serializer_spec.rb b/spec/serializers/custom_wizard/pro_serializer_spec.rb new file mode 100644 index 00000000..45c1956e --- /dev/null +++ b/spec/serializers/custom_wizard/pro_serializer_spec.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +require_relative '../../plugin_helper' + +describe CustomWizard::ProSerializer do + it 'should return pro attributes' do + pro = CustomWizard::Pro.new + serialized = described_class.new(pro, root: false) + + expect(serialized.server).to eq(pro.server) + expect(serialized.authentication.class).to eq(CustomWizard::ProAuthentication) + expect(serialized.subscription.class).to eq(CustomWizard::ProSubscription) + end +end diff --git a/spec/serializers/custom_wizard/wizard_field_serializer_spec.rb b/spec/serializers/custom_wizard/wizard_field_serializer_spec.rb index 1fa9671c..349b21f8 100644 --- a/spec/serializers/custom_wizard/wizard_field_serializer_spec.rb +++ b/spec/serializers/custom_wizard/wizard_field_serializer_spec.rb @@ -4,13 +4,10 @@ require_relative '../../plugin_helper' describe CustomWizard::FieldSerializer do fab!(:user) { Fabricate(:user) } + let(:template) { get_wizard_fixture("wizard") } before do - CustomWizard::Template.save( - JSON.parse(File.open( - "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json" - ).read), - skip_jobs: true) + CustomWizard::Template.save(template, skip_jobs: true) @wizard = CustomWizard::Builder.new("super_mega_fun_wizard", user).build end diff --git a/spec/serializers/custom_wizard/wizard_serializer_spec.rb b/spec/serializers/custom_wizard/wizard_serializer_spec.rb index 2052639a..fe36d5a2 100644 --- a/spec/serializers/custom_wizard/wizard_serializer_spec.rb +++ b/spec/serializers/custom_wizard/wizard_serializer_spec.rb @@ -5,19 +5,11 @@ require_relative '../../plugin_helper' describe CustomWizard::WizardSerializer do fab!(:user) { Fabricate(:user) } fab!(:category) { Fabricate(:category) } - - let(:similar_topics_validation) { - JSON.parse(File.open( - "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/field/validation/similar_topics.json" - ).read) - } + let(:template) { get_wizard_fixture("wizard") } + let(:similar_topics_validation) { get_wizard_fixture("field/validation/similar_topics") } before do - CustomWizard::Template.save( - JSON.parse(File.open( - "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json" - ).read), - skip_jobs: true) + CustomWizard::Template.save(template, skip_jobs: true) @template = CustomWizard::Template.find('super_mega_fun_wizard') end diff --git a/spec/serializers/custom_wizard/wizard_step_serializer_spec.rb b/spec/serializers/custom_wizard/wizard_step_serializer_spec.rb index 35ce0fd2..53afa8e5 100644 --- a/spec/serializers/custom_wizard/wizard_step_serializer_spec.rb +++ b/spec/serializers/custom_wizard/wizard_step_serializer_spec.rb @@ -4,22 +4,8 @@ require_relative '../../plugin_helper' describe CustomWizard::StepSerializer do fab!(:user) { Fabricate(:user) } - - let(:wizard_template) { - JSON.parse( - File.open( - "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json" - ).read - ) - } - - let(:required_data_json) { - JSON.parse( - File.open( - "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/step/required_data.json" - ).read - ) - } + let(:wizard_template) { get_wizard_fixture("wizard") } + let(:required_data_json) { get_wizard_fixture("step/required_data") } before do CustomWizard::Template.save(wizard_template, skip_jobs: true) From 7c9a0ef862d5c2a301ae262443c76f12f29b03e5 Mon Sep 17 00:00:00 2001 From: angusmcleod Date: Tue, 7 Sep 2021 20:11:50 +0800 Subject: [PATCH 038/160] Apply rubocop --- controllers/custom_wizard/admin/pro.rb | 6 +++--- jobs/scheduled/update_pro_subscription.rb | 4 ++-- lib/custom_wizard/action.rb | 2 +- lib/custom_wizard/pro.rb | 2 +- lib/custom_wizard/pro/authentication.rb | 5 +++-- lib/custom_wizard/pro/subscription.rb | 3 ++- serializers/custom_wizard/pro/authentication_serializer.rb | 2 +- serializers/custom_wizard/pro/subscription_serializer.rb | 2 +- serializers/custom_wizard/pro_serializer.rb | 2 +- spec/components/custom_wizard/action_spec.rb | 2 +- spec/components/custom_wizard/builder_spec.rb | 2 +- spec/components/custom_wizard/custom_field_spec.rb | 2 +- spec/components/custom_wizard/pro_spec.rb | 2 +- spec/requests/custom_wizard/admin/pro_controller_spec.rb | 4 ++-- spec/requests/custom_wizard/steps_controller_spec.rb | 4 ++-- 15 files changed, 23 insertions(+), 21 deletions(-) diff --git a/controllers/custom_wizard/admin/pro.rb b/controllers/custom_wizard/admin/pro.rb index 70e5de04..650743e6 100644 --- a/controllers/custom_wizard/admin/pro.rb +++ b/controllers/custom_wizard/admin/pro.rb @@ -39,10 +39,10 @@ class CustomWizard::AdminProController < CustomWizard::AdminController render json: failed_json end end - + protected - + def pro @pro ||= CustomWizard::Pro.new end -end \ No newline at end of file +end diff --git a/jobs/scheduled/update_pro_subscription.rb b/jobs/scheduled/update_pro_subscription.rb index 773093b0..c790d529 100644 --- a/jobs/scheduled/update_pro_subscription.rb +++ b/jobs/scheduled/update_pro_subscription.rb @@ -3,7 +3,7 @@ class CustomWizard::UpdateProSubscription < ::Jobs::Scheduled every 1.hour - def execute(args={}) + def execute(args = {}) CustomWizard::Pro.update_subscription end -end \ No newline at end of file +end diff --git a/lib/custom_wizard/action.rb b/lib/custom_wizard/action.rb index 74dc4680..912f3d3a 100644 --- a/lib/custom_wizard/action.rb +++ b/lib/custom_wizard/action.rb @@ -752,7 +752,7 @@ class CustomWizard::Action CustomWizard::Log.create(log) end - + def pro_actions %w[send_message watch_categories send_to_api create_group create_category] end diff --git a/lib/custom_wizard/pro.rb b/lib/custom_wizard/pro.rb index 5a813e21..61097069 100644 --- a/lib/custom_wizard/pro.rb +++ b/lib/custom_wizard/pro.rb @@ -183,4 +183,4 @@ class CustomWizard::Pro PluginStore.remove(self.class.namespace, authentication_db_key) get_authentication end -end \ No newline at end of file +end diff --git a/lib/custom_wizard/pro/authentication.rb b/lib/custom_wizard/pro/authentication.rb index 8d96a017..23603898 100644 --- a/lib/custom_wizard/pro/authentication.rb +++ b/lib/custom_wizard/pro/authentication.rb @@ -1,3 +1,4 @@ +# frozen_string_literal: true class CustomWizard::ProAuthentication include ActiveModel::Serialization @@ -21,7 +22,7 @@ class CustomWizard::ProAuthentication end def generate_keys(user_id, request_id) - rsa = OpenSSL::PKey::RSA.generate(2048) + rsa = OpenSSL::PKey::RSA.generate(2048) nonce = SecureRandom.hex(32) set_keys(request_id, user_id, rsa, nonce) @@ -91,4 +92,4 @@ class CustomWizard::ProAuthentication PluginStore.set(CustomWizard::Pro.namespace, client_id_db_key, client_id) client_id end -end \ No newline at end of file +end diff --git a/lib/custom_wizard/pro/subscription.rb b/lib/custom_wizard/pro/subscription.rb index a5782357..7f5cf911 100644 --- a/lib/custom_wizard/pro/subscription.rb +++ b/lib/custom_wizard/pro/subscription.rb @@ -1,3 +1,4 @@ +# frozen_string_literal: true class CustomWizard::ProSubscription include ActiveModel::Serialization @@ -18,4 +19,4 @@ class CustomWizard::ProSubscription def active? types.include?(type) && updated_at.to_datetime > (Time.zone.now - 2.hours).to_datetime end -end \ No newline at end of file +end diff --git a/serializers/custom_wizard/pro/authentication_serializer.rb b/serializers/custom_wizard/pro/authentication_serializer.rb index b54f428f..0a2915e4 100644 --- a/serializers/custom_wizard/pro/authentication_serializer.rb +++ b/serializers/custom_wizard/pro/authentication_serializer.rb @@ -8,4 +8,4 @@ class CustomWizard::ProAuthenticationSerializer < ApplicationSerializer def active object.active? end -end \ No newline at end of file +end diff --git a/serializers/custom_wizard/pro/subscription_serializer.rb b/serializers/custom_wizard/pro/subscription_serializer.rb index 6be5ec6f..d3e04e4e 100644 --- a/serializers/custom_wizard/pro/subscription_serializer.rb +++ b/serializers/custom_wizard/pro/subscription_serializer.rb @@ -7,4 +7,4 @@ class CustomWizard::ProSubscriptionSerializer < ApplicationSerializer def active object.active? end -end \ No newline at end of file +end diff --git a/serializers/custom_wizard/pro_serializer.rb b/serializers/custom_wizard/pro_serializer.rb index a9623974..2f141c6d 100644 --- a/serializers/custom_wizard/pro_serializer.rb +++ b/serializers/custom_wizard/pro_serializer.rb @@ -3,4 +3,4 @@ class CustomWizard::ProSerializer < ApplicationSerializer attributes :server has_one :authentication, serializer: CustomWizard::ProAuthenticationSerializer, embed: :objects has_one :subscription, serializer: CustomWizard::ProSubscriptionSerializer, embed: :objects -end \ No newline at end of file +end diff --git a/spec/components/custom_wizard/action_spec.rb b/spec/components/custom_wizard/action_spec.rb index 438f29dd..34a08461 100644 --- a/spec/components/custom_wizard/action_spec.rb +++ b/spec/components/custom_wizard/action_spec.rb @@ -178,7 +178,7 @@ describe CustomWizard::Action do before do enable_pro end - + it '#send_message' do wizard_template['actions'] << send_message update_template(wizard_template) diff --git a/spec/components/custom_wizard/builder_spec.rb b/spec/components/custom_wizard/builder_spec.rb index 19be5830..9b9c6000 100644 --- a/spec/components/custom_wizard/builder_spec.rb +++ b/spec/components/custom_wizard/builder_spec.rb @@ -251,7 +251,7 @@ describe CustomWizard::Builder do end end - context "with condition" do + context "with condition" do before do enable_pro @template[:steps][0][:condition] = user_condition_json['condition'] diff --git a/spec/components/custom_wizard/custom_field_spec.rb b/spec/components/custom_wizard/custom_field_spec.rb index fb1799dd..2204264f 100644 --- a/spec/components/custom_wizard/custom_field_spec.rb +++ b/spec/components/custom_wizard/custom_field_spec.rb @@ -192,7 +192,7 @@ describe CustomWizard::CustomField do ).exists? ).to eq(false) end - + it "does not save pro field types without a pro subscription" do pro_field_json = custom_field_pro_json['custom_fields'].first custom_field = CustomWizard::CustomField.new(nil, pro_field_json) diff --git a/spec/components/custom_wizard/pro_spec.rb b/spec/components/custom_wizard/pro_spec.rb index 6c83ae3b..6499b668 100644 --- a/spec/components/custom_wizard/pro_spec.rb +++ b/spec/components/custom_wizard/pro_spec.rb @@ -122,4 +122,4 @@ describe CustomWizard::Pro do expect(@pro.authorized?).to eq(false) end end -end \ No newline at end of file +end diff --git a/spec/requests/custom_wizard/admin/pro_controller_spec.rb b/spec/requests/custom_wizard/admin/pro_controller_spec.rb index 049bb88e..563572bd 100644 --- a/spec/requests/custom_wizard/admin/pro_controller_spec.rb +++ b/spec/requests/custom_wizard/admin/pro_controller_spec.rb @@ -38,7 +38,7 @@ describe CustomWizard::AdminProController do it "#destroy_authentication" do request_id = SecureRandom.hex(32) payload = generate_payload(request_id, admin_user.id) - @pro.authentication_response(request_id, payload) + @pro.authentication_response(request_id, payload) delete "/admin/wizards/pro/authorize.json" @@ -68,4 +68,4 @@ describe CustomWizard::AdminProController do expect(CustomWizard::Pro.subscribed?).to eq(true) end end -end \ No newline at end of file +end diff --git a/spec/requests/custom_wizard/steps_controller_spec.rb b/spec/requests/custom_wizard/steps_controller_spec.rb index 3564780f..85353e4c 100644 --- a/spec/requests/custom_wizard/steps_controller_spec.rb +++ b/spec/requests/custom_wizard/steps_controller_spec.rb @@ -78,7 +78,7 @@ describe CustomWizard::StepsController do put '/w/super-mega-fun-wizard/steps/step_1.json' put '/w/super-mega-fun-wizard/steps/step_2.json' - put '/w/super-mega-fun-wizard/steps/step_3.json' + put '/w/super-mega-fun-wizard/steps/step_3.json' expect(response.status).to eq(200) expect(response.parsed_body['redirect_on_complete']).to eq("https://google.com") end @@ -95,7 +95,7 @@ describe CustomWizard::StepsController do } } put '/w/super-mega-fun-wizard/steps/step_2.json' - put '/w/super-mega-fun-wizard/steps/step_3.json' + put '/w/super-mega-fun-wizard/steps/step_3.json' wizard_id = response.parsed_body['wizard']['id'] wizard = CustomWizard::Wizard.create(wizard_id, user) From 0313c773e83ac55499058a69b43980d56b3be31d Mon Sep 17 00:00:00 2001 From: angusmcleod Date: Tue, 7 Sep 2021 20:13:01 +0800 Subject: [PATCH 039/160] Apply prettier --- .../components/custom-field-input.js.es6 | 10 +-- .../components/wizard-custom-action.js.es6 | 4 +- .../components/wizard-pro-selector.js.es6 | 8 +-- .../wizard-pro-selector-row.js.es6 | 2 +- .../components/wizard-pro-subscription.js.es6 | 61 ++++++++++-------- .../controllers/admin-wizards-pro.js.es6 | 64 ++++++++++--------- .../admin-wizards-wizard-show.js.es6 | 2 +- .../custom-wizard-admin-route-map.js.es6 | 2 +- .../discourse/lib/wizard-schema.js.es6 | 10 +-- .../discourse/models/custom-wizard-pro.js.es6 | 6 +- .../discourse/models/custom-wizard.js.es6 | 2 +- .../routes/admin-wizards-custom-fields.js.es6 | 2 +- .../discourse/routes/admin-wizards-pro.js.es6 | 6 +- .../routes/admin-wizards-wizard-show.js.es6 | 2 +- assets/stylesheets/common/wizard-admin.scss | 29 +++++---- 15 files changed, 111 insertions(+), 99 deletions(-) diff --git a/assets/javascripts/discourse/components/custom-field-input.js.es6 b/assets/javascripts/discourse/components/custom-field-input.js.es6 index c389fb62..877b83fb 100644 --- a/assets/javascripts/discourse/components/custom-field-input.js.es6 +++ b/assets/javascripts/discourse/components/custom-field-input.js.es6 @@ -8,8 +8,8 @@ const klasses = ["topic", "post", "group", "category"]; const types = ["string", "boolean", "integer", "json"]; const proTypes = { klass: ["group", "category"], - type: ["json"] -} + type: ["json"], +}; const generateContent = function (array, type, proSubscribed = false) { return array.reduce((result, key) => { @@ -19,7 +19,7 @@ const generateContent = function (array, type, proSubscribed = false) { result.push({ id: key, name: I18n.t(`admin.wizard.custom_field.${type}.${key}`), - pro + pro, }); } return result; @@ -32,10 +32,10 @@ export default Component.extend({ postSerializers: ["post"], groupSerializers: ["basic_group"], categorySerializers: ["basic_category"], - klassContent: computed("proSubscribed", function() { + klassContent: computed("proSubscribed", function () { return generateContent(klasses, "klass", this.proSubscribed); }), - typeContent: computed("proSubscribed", function() { + typeContent: computed("proSubscribed", function () { return generateContent(types, "type", this.proSubscribed); }), showInputs: or("field.new", "field.edit"), diff --git a/assets/javascripts/discourse/components/wizard-custom-action.js.es6 b/assets/javascripts/discourse/components/wizard-custom-action.js.es6 index b0831d5d..c50be2ba 100644 --- a/assets/javascripts/discourse/components/wizard-custom-action.js.es6 +++ b/assets/javascripts/discourse/components/wizard-custom-action.js.es6 @@ -102,10 +102,10 @@ export default Component.extend(UndoChanges, { result.push({ id: type, name: I18n.t(`admin.wizard.action.${type}.label`), - pro + pro, }); } return result; }, []); - } + }, }); diff --git a/assets/javascripts/discourse/components/wizard-pro-selector.js.es6 b/assets/javascripts/discourse/components/wizard-pro-selector.js.es6 index b06b5943..7c9567fe 100644 --- a/assets/javascripts/discourse/components/wizard-pro-selector.js.es6 +++ b/assets/javascripts/discourse/components/wizard-pro-selector.js.es6 @@ -2,7 +2,7 @@ import SingleSelectComponent from "select-kit/components/single-select"; import { computed } from "@ember/object"; export default SingleSelectComponent.extend({ - classNames: ["combo-box", 'wizard-pro-selector'], + classNames: ["combo-box", "wizard-pro-selector"], selectKitOptions: { autoFilterable: false, @@ -10,10 +10,10 @@ export default SingleSelectComponent.extend({ showFullTitle: true, headerComponent: "wizard-pro-selector/wizard-pro-selector-header", caretUpIcon: "caret-up", - caretDownIcon: "caret-down" + caretDownIcon: "caret-down", }, modifyComponentForRow() { return "wizard-pro-selector/wizard-pro-selector-row"; - } -}); \ No newline at end of file + }, +}); diff --git a/assets/javascripts/discourse/components/wizard-pro-selector/wizard-pro-selector-row.js.es6 b/assets/javascripts/discourse/components/wizard-pro-selector/wizard-pro-selector-row.js.es6 index 9640cd0c..23034ac1 100644 --- a/assets/javascripts/discourse/components/wizard-pro-selector/wizard-pro-selector-row.js.es6 +++ b/assets/javascripts/discourse/components/wizard-pro-selector/wizard-pro-selector-row.js.es6 @@ -1,3 +1,3 @@ import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row"; -export default SelectKitRowComponent.extend(); \ No newline at end of file +export default SelectKitRowComponent.extend(); diff --git a/assets/javascripts/discourse/components/wizard-pro-subscription.js.es6 b/assets/javascripts/discourse/components/wizard-pro-subscription.js.es6 index 8ea56699..1c015da5 100644 --- a/assets/javascripts/discourse/components/wizard-pro-subscription.js.es6 +++ b/assets/javascripts/discourse/components/wizard-pro-subscription.js.es6 @@ -4,44 +4,49 @@ import { notEmpty } from "@ember/object/computed"; import discourseComputed from "discourse-common/utils/decorators"; export default Component.extend({ - classNameBindings: [':custom-wizard-pro-subscription', 'subscription.active:active:inactive'], - subscribed: notEmpty('subscription'), + classNameBindings: [ + ":custom-wizard-pro-subscription", + "subscription.active:active:inactive", + ], + subscribed: notEmpty("subscription"), - @discourseComputed('subscription.type') + @discourseComputed("subscription.type") title(type) { - return type ? - I18n.t(`admin.wizard.pro.subscription.title.${type}`) : - I18n.t("admin.wizard.pro.not_subscribed"); + return type + ? I18n.t(`admin.wizard.pro.subscription.title.${type}`) + : I18n.t("admin.wizard.pro.not_subscribed"); }, - @discourseComputed('subscription.active') + @discourseComputed("subscription.active") stateClass(active) { - return active ? 'active' : 'inactive'; + return active ? "active" : "inactive"; }, - @discourseComputed('stateClass') + @discourseComputed("stateClass") stateLabel(stateClass) { return I18n.t(`admin.wizard.pro.subscription.status.${stateClass}`); }, actions: { update() { - this.set('updating', true); - CustomWizardPro.update_subscription().then(result => { - if (result.success) { - this.setProperties({ - updateIcon: 'check', - subscription: result.subscription - }); - } else { - this.set('updateIcon', 'times'); - } - }).finally(() => { - this.set('updating', false); - setTimeout(() => { - this.set('updateIcon', null); - }, 7000); - }) - } - } -}); \ No newline at end of file + this.set("updating", true); + CustomWizardPro.update_subscription() + .then((result) => { + if (result.success) { + this.setProperties({ + updateIcon: "check", + subscription: result.subscription, + }); + } else { + this.set("updateIcon", "times"); + } + }) + .finally(() => { + this.set("updating", false); + setTimeout(() => { + this.set("updateIcon", null); + }, 7000); + }); + }, + }, +}); diff --git a/assets/javascripts/discourse/controllers/admin-wizards-pro.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards-pro.js.es6 index 7c873c66..61012d8f 100644 --- a/assets/javascripts/discourse/controllers/admin-wizards-pro.js.es6 +++ b/assets/javascripts/discourse/controllers/admin-wizards-pro.js.es6 @@ -5,52 +5,58 @@ import { alias } from "@ember/object/computed"; export default Controller.extend({ messageUrl: "https://thepavilion.io/t/3652", - messageType: 'info', + messageType: "info", messageKey: null, - showSubscription: alias('model.authentication.active'), + showSubscription: alias("model.authentication.active"), setup() { - const authentication = this.get('model.authentication'); - const subscription = this.get('model.subscription'); + const authentication = this.get("model.authentication"); + const subscription = this.get("model.subscription"); const subscribed = subscription && subscription.active; const authenticated = authentication && authentication.active; if (!subscribed) { - this.set('messageKey', authenticated ? 'not_subscribed' : 'authorize'); + this.set("messageKey", authenticated ? "not_subscribed" : "authorize"); } else { - this.set('messageKey', !authenticated ? - 'subscription_expiring' : - subscribed ? 'subscription_active' : 'subscription_inactive' + this.set( + "messageKey", + !authenticated + ? "subscription_expiring" + : subscribed + ? "subscription_active" + : "subscription_inactive" ); } }, - @discourseComputed('model.server') + @discourseComputed("model.server") messageOpts(server) { return { server }; }, actions: { unauthorize() { - this.set('unauthorizing', true); + this.set("unauthorizing", true); - CustomWizardPro.unauthorize().then(result => { - if (result.success) { - this.setProperties({ - messageKey: 'unauthorized', - messageType: 'warn', - "model.authentication": null, - "model.subscription": null - }); - } else { - this.setProperties({ - messageKey: 'unauthorize_failed', - messageType: 'error' - }); - } - }).finally(() => { - this.set('unauthorizing', false); - }) - } - } + CustomWizardPro.unauthorize() + .then((result) => { + if (result.success) { + this.setProperties({ + messageKey: "unauthorized", + messageType: "warn", + "model.authentication": null, + "model.subscription": null, + }); + } else { + this.setProperties({ + messageKey: "unauthorize_failed", + messageType: "error", + }); + } + }) + .finally(() => { + this.set("unauthorizing", false); + }); + }, + }, }); 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 a4be0667..6aa5dea4 100644 --- a/assets/javascripts/discourse/controllers/admin-wizards-wizard-show.js.es6 +++ b/assets/javascripts/discourse/controllers/admin-wizards-wizard-show.js.es6 @@ -80,7 +80,7 @@ export default Controller.extend({ if (result.wizard_id) { this.send("afterSave", result.wizard_id); } else if (result.errors) { - this.set('error', result.errors.join(', ')); + this.set("error", result.errors.join(", ")); } }) .catch((result) => { diff --git a/assets/javascripts/discourse/custom-wizard-admin-route-map.js.es6 b/assets/javascripts/discourse/custom-wizard-admin-route-map.js.es6 index ec2f1b98..5468e863 100644 --- a/assets/javascripts/discourse/custom-wizard-admin-route-map.js.es6 +++ b/assets/javascripts/discourse/custom-wizard-admin-route-map.js.es6 @@ -45,7 +45,7 @@ export default { this.route("adminWizardsLogs", { path: "/logs", - resetNamespace: true + resetNamespace: true, }); this.route("adminWizardsManager", { diff --git a/assets/javascripts/discourse/lib/wizard-schema.js.es6 b/assets/javascripts/discourse/lib/wizard-schema.js.es6 index 2e810634..a25ab6d0 100644 --- a/assets/javascripts/discourse/lib/wizard-schema.js.es6 +++ b/assets/javascripts/discourse/lib/wizard-schema.js.es6 @@ -196,11 +196,11 @@ const action = { ], required: ["id", "type"], proTypes: [ - 'send_message', - 'add_to_group', - 'create_category', - 'create_group', - 'send_to_api' + "send_message", + "add_to_group", + "create_category", + "create_group", + "send_to_api", ], dependent: {}, objectArrays: {}, diff --git a/assets/javascripts/discourse/models/custom-wizard-pro.js.es6 b/assets/javascripts/discourse/models/custom-wizard-pro.js.es6 index 66d80572..83afa161 100644 --- a/assets/javascripts/discourse/models/custom-wizard-pro.js.es6 +++ b/assets/javascripts/discourse/models/custom-wizard-pro.js.es6 @@ -23,12 +23,12 @@ CustomWizardPro.reopenClass({ type: "DELETE", }).catch(popupAjaxError); }, - + update_subscription() { return ajax(`${basePath}/subscription`, { type: "POST", }).catch(popupAjaxError); - } + }, }); -export default CustomWizardPro; \ No newline at end of file +export default CustomWizardPro; diff --git a/assets/javascripts/discourse/models/custom-wizard.js.es6 b/assets/javascripts/discourse/models/custom-wizard.js.es6 index 80c4d86a..e6a8408d 100644 --- a/assets/javascripts/discourse/models/custom-wizard.js.es6 +++ b/assets/javascripts/discourse/models/custom-wizard.js.es6 @@ -221,7 +221,7 @@ CustomWizard.reopenClass({ const wizard = this._super.apply(this); wizard.setProperties(buildProperties(wizardJson)); return wizard; - } + }, }); export default CustomWizard; diff --git a/assets/javascripts/discourse/routes/admin-wizards-custom-fields.js.es6 b/assets/javascripts/discourse/routes/admin-wizards-custom-fields.js.es6 index 198f9a14..45ca9ae7 100644 --- a/assets/javascripts/discourse/routes/admin-wizards-custom-fields.js.es6 +++ b/assets/javascripts/discourse/routes/admin-wizards-custom-fields.js.es6 @@ -13,7 +13,7 @@ export default DiscourseRoute.extend({ controller.setProperties({ customFields, - proSubscribed + proSubscribed, }); }, }); diff --git a/assets/javascripts/discourse/routes/admin-wizards-pro.js.es6 b/assets/javascripts/discourse/routes/admin-wizards-pro.js.es6 index 2fa091c7..d5c7fbd7 100644 --- a/assets/javascripts/discourse/routes/admin-wizards-pro.js.es6 +++ b/assets/javascripts/discourse/routes/admin-wizards-pro.js.es6 @@ -7,13 +7,13 @@ export default DiscourseRoute.extend({ }, setupController(controller, model) { - controller.set('model', model); + controller.set("model", model); controller.setup(); }, actions: { authorize() { CustomWizardPro.authorize(); - } - } + }, + }, }); diff --git a/assets/javascripts/discourse/routes/admin-wizards-wizard-show.js.es6 b/assets/javascripts/discourse/routes/admin-wizards-wizard-show.js.es6 index f298fa1c..7032b974 100644 --- a/assets/javascripts/discourse/routes/admin-wizards-wizard-show.js.es6 +++ b/assets/javascripts/discourse/routes/admin-wizards-wizard-show.js.es6 @@ -39,7 +39,7 @@ export default DiscourseRoute.extend({ currentStep: wizard.steps[0], currentAction: wizard.actions[0], creating: model.create, - proSubscribed: parentModel.pro_subscribed + proSubscribed: parentModel.pro_subscribed, }; controller.setProperties(props); diff --git a/assets/stylesheets/common/wizard-admin.scss b/assets/stylesheets/common/wizard-admin.scss index 3146da13..86cb818a 100644 --- a/assets/stylesheets/common/wizard-admin.scss +++ b/assets/stylesheets/common/wizard-admin.scss @@ -700,7 +700,7 @@ background-color: var(--primary-low); padding: 1em; margin: 0 0 1em 0; - + .setting-title { display: flex; align-items: center; @@ -711,7 +711,7 @@ input[type="checkbox"] { margin: 0 5px 0 0; - } + } } .setting-label { @@ -729,7 +729,7 @@ > span { margin-right: 1em; - } + } } } } @@ -741,7 +741,7 @@ .validation-section { min-width: 250px; - margin: .5em 0; + margin: 0.5em 0; } } @@ -761,18 +761,19 @@ .pro-label { color: var(--tertiary); - font-size: .75em; + font-size: 0.75em; } .admin-wizards-pro { .admin-wizard-controls { - h3, label { + h3, + label { margin: 0; } label { - padding: .4em .5em; - margin-left: .75em; + padding: 0.4em 0.5em; + margin-left: 0.75em; background-color: var(--success); color: var(--secondary); } @@ -792,14 +793,14 @@ display: flex; justify-content: space-between; align-items: center; - margin-bottom: .5em; + margin-bottom: 0.5em; h3 { margin: 0; } .buttons > span { - margin-right: .5em; + margin-right: 0.5em; } } @@ -810,8 +811,8 @@ background-color: var(--primary-very-low); .subscription-state { - padding: .25em .5em; - margin-right: .75em; + padding: 0.25em 0.5em; + margin-right: 0.75em; &.active { background-color: var(--success); @@ -829,7 +830,7 @@ } .pro-label { - margin-left: .75em; - padding-top: .25em; + margin-left: 0.75em; + padding-top: 0.25em; } } From ba897abf8ee98aacca53bcaf0b192ab36419dbe8 Mon Sep 17 00:00:00 2001 From: angusmcleod Date: Tue, 7 Sep 2021 20:15:04 +0800 Subject: [PATCH 040/160] Apply eslint --- .../javascripts/discourse/components/wizard-custom-field.js.es6 | 2 +- .../javascripts/discourse/components/wizard-pro-selector.js.es6 | 1 - .../discourse/components/wizard-pro-subscription.js.es6 | 1 + assets/javascripts/discourse/models/custom-wizard-pro.js.es6 | 1 - 4 files changed, 2 insertions(+), 3 deletions(-) diff --git a/assets/javascripts/discourse/components/wizard-custom-field.js.es6 b/assets/javascripts/discourse/components/wizard-custom-field.js.es6 index 807f90cd..37266a6b 100644 --- a/assets/javascripts/discourse/components/wizard-custom-field.js.es6 +++ b/assets/javascripts/discourse/components/wizard-custom-field.js.es6 @@ -1,5 +1,5 @@ import { default as discourseComputed } from "discourse-common/utils/decorators"; -import { alias, equal, or } from "@ember/object/computed"; +import { equal, or } from "@ember/object/computed"; import { computed } from "@ember/object"; import { selectKitContent } from "../lib/wizard"; import UndoChanges from "../mixins/undo-changes"; diff --git a/assets/javascripts/discourse/components/wizard-pro-selector.js.es6 b/assets/javascripts/discourse/components/wizard-pro-selector.js.es6 index 7c9567fe..8e472782 100644 --- a/assets/javascripts/discourse/components/wizard-pro-selector.js.es6 +++ b/assets/javascripts/discourse/components/wizard-pro-selector.js.es6 @@ -1,5 +1,4 @@ import SingleSelectComponent from "select-kit/components/single-select"; -import { computed } from "@ember/object"; export default SingleSelectComponent.extend({ classNames: ["combo-box", "wizard-pro-selector"], diff --git a/assets/javascripts/discourse/components/wizard-pro-subscription.js.es6 b/assets/javascripts/discourse/components/wizard-pro-subscription.js.es6 index 1c015da5..7824cb83 100644 --- a/assets/javascripts/discourse/components/wizard-pro-subscription.js.es6 +++ b/assets/javascripts/discourse/components/wizard-pro-subscription.js.es6 @@ -2,6 +2,7 @@ import Component from "@ember/component"; import CustomWizardPro from "../models/custom-wizard-pro"; import { notEmpty } from "@ember/object/computed"; import discourseComputed from "discourse-common/utils/decorators"; +import I18n from "I18n"; export default Component.extend({ classNameBindings: [ diff --git a/assets/javascripts/discourse/models/custom-wizard-pro.js.es6 b/assets/javascripts/discourse/models/custom-wizard-pro.js.es6 index 83afa161..76429726 100644 --- a/assets/javascripts/discourse/models/custom-wizard-pro.js.es6 +++ b/assets/javascripts/discourse/models/custom-wizard-pro.js.es6 @@ -1,7 +1,6 @@ import { ajax } from "discourse/lib/ajax"; import { popupAjaxError } from "discourse/lib/ajax-error"; import EmberObject from "@ember/object"; -import DiscourseURL from "discourse/lib/url"; const CustomWizardPro = EmberObject.extend(); From 18c43f499e599a3cf8e0d43b881072f71d313b4e Mon Sep 17 00:00:00 2001 From: angusmcleod Date: Tue, 7 Sep 2021 20:53:45 +0800 Subject: [PATCH 041/160] Apply template lint --- assets/javascripts/discourse/templates/admin-wizards-pro.hbs | 2 +- .../discourse/templates/admin-wizards-submissions-show.hbs | 2 +- .../discourse/templates/components/submission-field.hbs | 2 +- .../wizard-pro-selector/wizard-pro-selector-header.hbs | 2 +- .../templates/components/wizard-pro-subscription.hbs | 4 ++-- .../templates/components/wizard-realtime-validations.hbs | 2 +- .../templates/modal/admin-wizards-submissions-columns.hbs | 2 +- config/locales/client.en.yml | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/assets/javascripts/discourse/templates/admin-wizards-pro.hbs b/assets/javascripts/discourse/templates/admin-wizards-pro.hbs index a0658ccb..b4f0691b 100644 --- a/assets/javascripts/discourse/templates/admin-wizards-pro.hbs +++ b/assets/javascripts/discourse/templates/admin-wizards-pro.hbs @@ -4,7 +4,7 @@
{{#if model.authentication.active}} {{conditional-loading-spinner size="small" condition=unauthorizing}} - + {{i18n "admin.wizard.pro.unauthorize"}}
-{{/if}} \ No newline at end of file +{{/if}} diff --git a/assets/javascripts/discourse/templates/components/submission-field.hbs b/assets/javascripts/discourse/templates/components/submission-field.hbs index 831dcc3a..c635ff47 100644 --- a/assets/javascripts/discourse/templates/components/submission-field.hbs +++ b/assets/javascripts/discourse/templates/components/submission-field.hbs @@ -160,4 +160,4 @@ {{d-icon "clock"}}{{format-date value format="tiny"}} -{{/if}} \ No newline at end of file +{{/if}} diff --git a/assets/javascripts/discourse/templates/components/wizard-pro-selector/wizard-pro-selector-header.hbs b/assets/javascripts/discourse/templates/components/wizard-pro-selector/wizard-pro-selector-header.hbs index a02b0d9c..db467b02 100644 --- a/assets/javascripts/discourse/templates/components/wizard-pro-selector/wizard-pro-selector-header.hbs +++ b/assets/javascripts/discourse/templates/components/wizard-pro-selector/wizard-pro-selector-header.hbs @@ -12,4 +12,4 @@ {{/if}} {{d-icon caretIcon class="caret-icon"}} -
\ No newline at end of file +
diff --git a/assets/javascripts/discourse/templates/components/wizard-pro-subscription.hbs b/assets/javascripts/discourse/templates/components/wizard-pro-subscription.hbs index 2bebc9ed..8eca5996 100644 --- a/assets/javascripts/discourse/templates/components/wizard-pro-subscription.hbs +++ b/assets/javascripts/discourse/templates/components/wizard-pro-subscription.hbs @@ -24,8 +24,8 @@ {{#if subscription.updated_at}}
- {{{i18n 'admin.wizard.pro.subscription.last_updated' updated_at=(format-date subscription.updated_at leaveAgo="true")}}} + {{i18n "admin.wizard.pro.subscription.last_updated"}} {{format-date subscription.updated_at leaveAgo="true"}}
{{/if}}
-{{/if}} \ No newline at end of file +{{/if}} diff --git a/assets/javascripts/discourse/templates/components/wizard-realtime-validations.hbs b/assets/javascripts/discourse/templates/components/wizard-realtime-validations.hbs index 0cc34f35..1aa0893b 100644 --- a/assets/javascripts/discourse/templates/components/wizard-realtime-validations.hbs +++ b/assets/javascripts/discourse/templates/components/wizard-realtime-validations.hbs @@ -51,4 +51,4 @@ {{/each-in}} -
\ No newline at end of file +
diff --git a/assets/javascripts/discourse/templates/modal/admin-wizards-submissions-columns.hbs b/assets/javascripts/discourse/templates/modal/admin-wizards-submissions-columns.hbs index 24743a92..a167a954 100644 --- a/assets/javascripts/discourse/templates/modal/admin-wizards-submissions-columns.hbs +++ b/assets/javascripts/discourse/templates/modal/admin-wizards-submissions-columns.hbs @@ -29,4 +29,4 @@ label="directory.edit_columns.reset_to_default" action=(action "resetToDefault") }} -
\ No newline at end of file +
diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index f24b5866..ea6f7571 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -467,7 +467,7 @@ en: active: Active inactive: Inactive update: Update - last_updated: Last updated {{updated_at}} + last_updated: Last updated wizard_js: group: From 4a0d176f1a195b4eeba1dddd9c14cb341ed724a5 Mon Sep 17 00:00:00 2001 From: angusmcleod Date: Tue, 7 Sep 2021 20:55:46 +0800 Subject: [PATCH 042/160] Fix locale file --- config/locales/client.en.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index ea6f7571..e7103957 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -109,7 +109,7 @@ en: documentation: "Check out the submissions documentation" logs: viewing: "View recent logs for wizards on the forum" - documentation: "Check out the logs documentation + documentation: "Check out the logs documentation" editor: show: "Show" From a8e81150f10ef426b3049b1bb88ae66136c1f09e Mon Sep 17 00:00:00 2001 From: angusmcleod Date: Wed, 8 Sep 2021 18:25:30 +0800 Subject: [PATCH 043/160] Remove additional sentence from submissions.viewing --- config/locales/client.en.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index e7103957..ae2594d2 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -105,7 +105,7 @@ en: unauthorize_failed: Failed to unauthorize. submissions: select: "Select a wizard to see its submissions" - viewing: "You're viewing the submissions of the %{wizardName}. Click 'Download' on the right to download them." + viewing: "You're viewing the submissions of the %{wizardName}" documentation: "Check out the submissions documentation" logs: viewing: "View recent logs for wizards on the forum" From 7b57e7fcab64fc037ec7b293dfaabc579034f105 Mon Sep 17 00:00:00 2001 From: angusmcleod Date: Thu, 9 Sep 2021 14:07:12 +0800 Subject: [PATCH 044/160] Apply new table style to wizard logs view --- ...field.js.es6 => wizard-table-field.js.es6} | 51 ++++-- ...ns.js.es6 => admin-wizards-columns.js.es6} | 5 +- .../admin-wizards-logs-show.js.es6 | 52 ++++++ .../controllers/admin-wizards-logs.js.es6 | 68 +++----- .../admin-wizards-submissions-show.js.es6 | 10 +- .../custom-wizard-admin-route-map.js.es6 | 14 +- .../models/custom-wizard-logs.js.es6 | 46 ++++- .../routes/admin-wizards-logs-show.js.es6 | 17 ++ .../routes/admin-wizards-logs.js.es6 | 18 +- .../templates/admin-wizards-logs-show.hbs | 45 +++++ .../templates/admin-wizards-logs.hbs | 43 ++--- .../admin-wizards-submissions-show.hbs | 14 +- .../templates/components/submission-field.hbs | 163 ------------------ .../components/wizard-table-field.hbs | 161 +++++++++++++++++ ...-columns.hbs => admin-wizards-columns.hbs} | 8 +- assets/stylesheets/common/wizard-admin.scss | 37 ++-- config/locales/client.en.yml | 14 +- config/routes.rb | 1 + controllers/custom_wizard/admin/logs.rb | 41 ++++- coverage/.last_run.json | 2 +- ...06135416_split_custom_wizard_log_fields.rb | 80 +++++---- lib/custom_wizard/log.rb | 31 ++-- serializers/custom_wizard/log_serializer.rb | 8 +- spec/components/custom_wizard/log_spec.rb | 12 +- .../admin/logs_controller_spec.rb | 31 +++- .../custom_wizard/log_serializer_spec.rb | 5 +- 26 files changed, 599 insertions(+), 378 deletions(-) rename assets/javascripts/discourse/components/{submission-field.js.es6 => wizard-table-field.js.es6} (69%) rename assets/javascripts/discourse/controllers/{admin-wizards-submissions-columns.js.es6 => admin-wizards-columns.js.es6} (73%) create mode 100644 assets/javascripts/discourse/controllers/admin-wizards-logs-show.js.es6 create mode 100644 assets/javascripts/discourse/routes/admin-wizards-logs-show.js.es6 create mode 100644 assets/javascripts/discourse/templates/admin-wizards-logs-show.hbs delete mode 100644 assets/javascripts/discourse/templates/components/submission-field.hbs create mode 100644 assets/javascripts/discourse/templates/components/wizard-table-field.hbs rename assets/javascripts/discourse/templates/modal/{admin-wizards-submissions-columns.hbs => admin-wizards-columns.hbs} (72%) diff --git a/assets/javascripts/discourse/components/submission-field.js.es6 b/assets/javascripts/discourse/components/wizard-table-field.js.es6 similarity index 69% rename from assets/javascripts/discourse/components/submission-field.js.es6 rename to assets/javascripts/discourse/components/wizard-table-field.js.es6 index 5ebc0ccd..ce6b1584 100644 --- a/assets/javascripts/discourse/components/submission-field.js.es6 +++ b/assets/javascripts/discourse/components/wizard-table-field.js.es6 @@ -1,10 +1,11 @@ import Component from "@ember/component"; import { action } from "@ember/object"; -import { equal } from "@ember/object/computed"; +import { equal, notEmpty } from "@ember/object/computed"; import discourseComputed from "discourse-common/utils/decorators"; import I18n from "I18n"; export default Component.extend({ + classNameBindings: ["value.type"], isText: equal("value.type", "text"), isComposer: equal("value.type", "composer"), isDate: equal("value.type", "date"), @@ -18,13 +19,29 @@ export default Component.extend({ isTag: equal("value.type", "tag"), isCategory: equal("value.type", "category"), isGroup: equal("value.type", "group"), - isUser: equal("fieldName", "username"), isUserSelector: equal("value.type", "user_selector"), - isSubmittedAt: equal("fieldName", "submitted_at"), - isTextArea: equal("value.type", "textarea"), + isSubmittedAt: equal("field", "submitted_at"), isComposerPreview: equal("value.type", "composer_preview"), textState: "text-collapsed", - toggleText: I18n.t("admin.wizard.submissions.expand_text"), + toggleText: I18n.t("admin.wizard.expand_text"), + + @discourseComputed("value", "isUser") + hasValue(value, isUser) { + if (isUser) { + return value; + } + return value && value.value; + }, + + @discourseComputed("field", "value.type") + isUser(field, type) { + return field === "username" || field === "user" || type === "user"; + }, + + @discourseComputed("value.type") + isLongtext(type) { + return type === "textarea" || type === "long_text"; + }, @discourseComputed("value") checkboxValue(value) { @@ -44,10 +61,10 @@ export default Component.extend({ if (state === "text-collapsed") { this.set("textState", "text-expanded"); - this.set("toggleText", I18n.t("admin.wizard.submissions.collapse_text")); + this.set("toggleText", I18n.t("admin.wizard.collapse_text")); } else if (state === "text-expanded") { this.set("textState", "text-collapsed"); - this.set("toggleText", I18n.t("admin.wizard.submissions.expand_text")); + this.set("toggleText", I18n.t("admin.wizard.expand_text")); } }, @@ -83,19 +100,24 @@ export default Component.extend({ return users; }, - @discourseComputed("value") - userProfileUrl(value) { - const isUser = this.get("isUser"); + @discourseComputed("isUser", "field", "value") + username(isUser, field, value) { + if (isUser) {return value.username;} + if (field === "username") {return value.value;} + return null; + }, - if (isUser) { - return `/u/${value.username}`; - } + showUsername: notEmpty("username"), + + @discourseComputed("username") + userProfileUrl(username) { + if (username) {return `/u/${username}`;} + return "/"; }, @discourseComputed("value") categoryUrl(value) { const isCategory = this.get("isCategory"); - if (isCategory) { return `/c/${value.value}`; } @@ -104,7 +126,6 @@ export default Component.extend({ @discourseComputed("value") groupUrl(value) { const isGroup = this.get("isGroup"); - if (isGroup) { return `/g/${value.value}`; } diff --git a/assets/javascripts/discourse/controllers/admin-wizards-submissions-columns.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards-columns.js.es6 similarity index 73% rename from assets/javascripts/discourse/controllers/admin-wizards-submissions-columns.js.es6 rename to assets/javascripts/discourse/controllers/admin-wizards-columns.js.es6 index 4af487ee..4754c577 100644 --- a/assets/javascripts/discourse/controllers/admin-wizards-submissions-columns.js.es6 +++ b/assets/javascripts/discourse/controllers/admin-wizards-columns.js.es6 @@ -6,10 +6,9 @@ export default Controller.extend(ModalFunctionality, { save() { this.send("closeModal"); }, + resetToDefault() { - this.get("model.fields").forEach((field) => { - field.set("enabled", true); - }); + this.get("model.reset")(); }, }, }); diff --git a/assets/javascripts/discourse/controllers/admin-wizards-logs-show.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards-logs-show.js.es6 new file mode 100644 index 00000000..7e3fdff1 --- /dev/null +++ b/assets/javascripts/discourse/controllers/admin-wizards-logs-show.js.es6 @@ -0,0 +1,52 @@ +import discourseComputed from "discourse-common/utils/decorators"; +import { notEmpty } from "@ember/object/computed"; +import CustomWizardLogs from "../models/custom-wizard-logs"; +import Controller from "@ember/controller"; + +export default Controller.extend({ + refreshing: false, + hasLogs: notEmpty("logs"), + page: 0, + canLoadMore: true, + logs: [], + messageKey: "viewing", + + loadLogs() { + if (!this.canLoadMore) { + return; + } + const page = this.get("page"); + const wizardId = this.get("wizard.id"); + + this.set("refreshing", true); + + CustomWizardLogs.list(wizardId, page) + .then((result) => { + this.set("logs", this.logs.concat(result.logs)); + }) + .finally(() => this.set("refreshing", false)); + }, + + @discourseComputed("hasLogs", "refreshing") + noResults(hasLogs, refreshing) { + return !hasLogs && !refreshing; + }, + + actions: { + loadMore() { + if (!this.loadingMore && this.logs.length < this.total) { + this.set("page", (this.page += 1)); + this.loadLogs(); + } + }, + + refresh() { + this.setProperties({ + canLoadMore: true, + page: 0, + logs: [], + }); + this.loadLogs(); + }, + }, +}); diff --git a/assets/javascripts/discourse/controllers/admin-wizards-logs.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards-logs.js.es6 index f45013d7..7388a8d6 100644 --- a/assets/javascripts/discourse/controllers/admin-wizards-logs.js.es6 +++ b/assets/javascripts/discourse/controllers/admin-wizards-logs.js.es6 @@ -1,52 +1,34 @@ -import discourseComputed from "discourse-common/utils/decorators"; -import { notEmpty } from "@ember/object/computed"; -import CustomWizardLogs from "../models/custom-wizard-logs"; import Controller from "@ember/controller"; +import { default as discourseComputed } from "discourse-common/utils/decorators"; export default Controller.extend({ - refreshing: false, - hasLogs: notEmpty("logs"), - page: 0, - canLoadMore: true, - logs: [], documentationUrl: "https://thepavilion.io/t/2818", - messageKey: "viewing", - loadLogs() { - if (!this.canLoadMore) { - return; + @discourseComputed("wizardId") + wizardName(wizardId) { + let currentWizard = this.wizardList.find( + (wizard) => wizard.id === wizardId + ); + if (currentWizard) { + return currentWizard.name; + } + }, + + @discourseComputed("wizardName") + messageOpts(wizardName) { + return { + wizardName, + }; + }, + + @discourseComputed("wizardId") + messageKey(wizardId) { + let key = "select"; + + if (wizardId) { + key = "viewing"; } - this.set("refreshing", true); - - CustomWizardLogs.list() - .then((result) => { - if (!result || result.length === 0) { - this.set("canLoadMore", false); - } - this.set("logs", this.logs.concat(result)); - }) - .finally(() => this.set("refreshing", false)); - }, - - @discourseComputed("hasLogs", "refreshing") - noResults(hasLogs, refreshing) { - return !hasLogs && !refreshing; - }, - - actions: { - loadMore() { - this.set("page", (this.page += 1)); - this.loadLogs(); - }, - - refresh() { - this.setProperties({ - canLoadMore: true, - page: 0, - logs: [], - }); - this.loadLogs(); - }, + return key; }, }); diff --git a/assets/javascripts/discourse/controllers/admin-wizards-submissions-show.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards-submissions-show.js.es6 index 7ba0050f..10621cd3 100644 --- a/assets/javascripts/discourse/controllers/admin-wizards-submissions-show.js.es6 +++ b/assets/javascripts/discourse/controllers/admin-wizards-submissions-show.js.es6 @@ -54,10 +54,14 @@ export default Controller.extend({ }, showEditColumnsModal() { - return showModal("admin-wizards-submissions-columns", { + return showModal("admin-wizards-columns", { model: { - fields: this.get("fields"), - submissions: this.get("submissions"), + columns: this.get("fields"), + reset: () => { + this.get("fields").forEach((field) => { + field.set("enabled", true); + }); + }, }, }); }, diff --git a/assets/javascripts/discourse/custom-wizard-admin-route-map.js.es6 b/assets/javascripts/discourse/custom-wizard-admin-route-map.js.es6 index 5468e863..1ca6d41b 100644 --- a/assets/javascripts/discourse/custom-wizard-admin-route-map.js.es6 +++ b/assets/javascripts/discourse/custom-wizard-admin-route-map.js.es6 @@ -43,10 +43,16 @@ export default { } ); - this.route("adminWizardsLogs", { - path: "/logs", - resetNamespace: true, - }); + this.route( + "adminWizardsLogs", + { path: "/logs", resetNamespace: true }, + function () { + this.route("adminWizardsLogsShow", { + path: "/:wizardId/", + resetNamespace: true, + }); + } + ); this.route("adminWizardsManager", { path: "/manager", diff --git a/assets/javascripts/discourse/models/custom-wizard-logs.js.es6 b/assets/javascripts/discourse/models/custom-wizard-logs.js.es6 index e2de8a07..1bd19dfe 100644 --- a/assets/javascripts/discourse/models/custom-wizard-logs.js.es6 +++ b/assets/javascripts/discourse/models/custom-wizard-logs.js.es6 @@ -3,14 +3,48 @@ import { popupAjaxError } from "discourse/lib/ajax-error"; import EmberObject from "@ember/object"; const CustomWizardLogs = EmberObject.extend(); +const logItemTypes = { + date: "date_time", + action: "text", + message: "long_text", + user: "user", + username: "text", +}; + +function logItem(item, attr) { + return { + value: item[attr], + type: logItemTypes[attr], + }; +} CustomWizardLogs.reopenClass({ - list(page = 0) { - return ajax("/admin/wizards/logs", { - data: { - page, - }, - }).catch(popupAjaxError); + list(wizardId, page = 0) { + let data = { + page, + }; + + return ajax(`/admin/wizards/logs/${wizardId}`, { data }) + .catch(popupAjaxError) + .then((result) => { + if (result.logs) { + result.logs = result.logs.map((item) => { + let map = {}; + + if (item.date) {map.date = logItem(item, "date");} + if (item.action) {map.action = logItem(item, "action");} + if (item.user) { + map.user = item.user; + } else { + map.user = logItem(item, "username"); + } + if (item.message) {map.message = logItem(item, "message");} + + return map; + }); + } + return result; + }); }, }); diff --git a/assets/javascripts/discourse/routes/admin-wizards-logs-show.js.es6 b/assets/javascripts/discourse/routes/admin-wizards-logs-show.js.es6 new file mode 100644 index 00000000..474360ec --- /dev/null +++ b/assets/javascripts/discourse/routes/admin-wizards-logs-show.js.es6 @@ -0,0 +1,17 @@ +import CustomWizardLogs from "../models/custom-wizard-logs"; +import DiscourseRoute from "discourse/routes/discourse"; +import { A } from "@ember/array"; + +export default DiscourseRoute.extend({ + model(params) { + return CustomWizardLogs.list(params.wizardId); + }, + + setupController(controller, model) { + controller.setProperties({ + wizard: model.wizard, + logs: A(model.logs), + total: model.total, + }); + }, +}); diff --git a/assets/javascripts/discourse/routes/admin-wizards-logs.js.es6 b/assets/javascripts/discourse/routes/admin-wizards-logs.js.es6 index 56b91350..a1575050 100644 --- a/assets/javascripts/discourse/routes/admin-wizards-logs.js.es6 +++ b/assets/javascripts/discourse/routes/admin-wizards-logs.js.es6 @@ -1,12 +1,24 @@ -import CustomWizardLogs from "../models/custom-wizard-logs"; import DiscourseRoute from "discourse/routes/discourse"; +import { ajax } from "discourse/lib/ajax"; export default DiscourseRoute.extend({ model() { - return CustomWizardLogs.list(); + return ajax(`/admin/wizards/wizard`); }, setupController(controller, model) { - controller.set("logs", model); + const showParams = this.paramsFor("adminWizardsLogsShow"); + + controller.setProperties({ + wizardId: showParams.wizardId, + wizardList: model.wizard_list, + }); + }, + + actions: { + changeWizard(wizardId) { + this.controllerFor("adminWizardsLogs").set("wizardId", wizardId); + this.transitionTo("adminWizardsLogsShow", wizardId); + }, }, }); diff --git a/assets/javascripts/discourse/templates/admin-wizards-logs-show.hbs b/assets/javascripts/discourse/templates/admin-wizards-logs-show.hbs new file mode 100644 index 00000000..898198d1 --- /dev/null +++ b/assets/javascripts/discourse/templates/admin-wizards-logs-show.hbs @@ -0,0 +1,45 @@ +{{#if logs}} +
+ + +
+ {{d-button + label="refresh" + icon="sync" + action="refresh" + class="refresh"}} +
+
+ +
+ {{#load-more selector=".wizard-table tr" action=(action "loadMore")}} + {{#if noResults}} +

{{i18n "search.no_results"}}

+ {{else}} + + + + + + + + + + + {{#each logs as |log|}} + + {{#each-in log as |field value|}} + + {{/each-in}} + + {{/each}} + +
{{i18n "admin.wizard.log.date"}}{{i18n "admin.wizard.log.action"}}{{i18n "admin.wizard.log.user"}}{{i18n "admin.wizard.log.message"}}
{{wizard-table-field field=field value=value}}
+ {{/if}} + + {{conditional-loading-spinner condition=refreshing}} + {{/load-more}} +
+{{/if}} diff --git a/assets/javascripts/discourse/templates/admin-wizards-logs.hbs b/assets/javascripts/discourse/templates/admin-wizards-logs.hbs index ea0afb7c..45738a9f 100644 --- a/assets/javascripts/discourse/templates/admin-wizards-logs.hbs +++ b/assets/javascripts/discourse/templates/admin-wizards-logs.hbs @@ -1,11 +1,11 @@ -
-

{{i18n "admin.wizard.log.nav_label"}}

- - {{d-button - label="refresh" - icon="sync" - action="refresh" - class="refresh"}} +
+ {{combo-box + value=wizardId + content=wizardList + onChange=(route-action "changeWizard") + options=(hash + none="admin.wizard.select" + )}}
{{wizard-message @@ -14,27 +14,6 @@ url=documentationUrl component="logs"}} -{{#load-more selector=".log-list tr" action=(action "loadMore") class="wizard-logs"}} - {{#if noResults}} -

{{i18n "search.no_results"}}

- {{else}} - - - - - - - - - {{#each logs as |log|}} - - - - - {{/each}} - -
MessageDate
{{log.message}}{{bound-date log.date}}
- {{/if}} - - {{conditional-loading-spinner condition=refreshing}} -{{/load-more}} +
+ {{outlet}} +
diff --git a/assets/javascripts/discourse/templates/admin-wizards-submissions-show.hbs b/assets/javascripts/discourse/templates/admin-wizards-submissions-show.hbs index df97513b..72ec7c38 100644 --- a/assets/javascripts/discourse/templates/admin-wizards-submissions-show.hbs +++ b/assets/javascripts/discourse/templates/admin-wizards-submissions-show.hbs @@ -7,7 +7,7 @@
{{d-button icon="sliders-h" - label="admin.wizard.submissions.edit_columns" + label="admin.wizard.edit_columns" action=(action "showEditColumnsModal") class="btn-default open-edit-columns-btn download-link" }} @@ -26,12 +26,10 @@
-
- {{#load-more selector=".wizard-submissions tr" action=(action "loadMore")}} +
+ {{#load-more selector=".wizard-table tr" action=(action "loadMore")}} {{#if noResults}} -

- {{i18n "search.no_results"}} -

+

{{i18n "search.no_results"}}

{{else}} @@ -49,9 +47,7 @@ {{#each displaySubmissions as |submission|}} {{#each-in submission as |field value|}} - + {{/each-in}} {{/each}} diff --git a/assets/javascripts/discourse/templates/components/submission-field.hbs b/assets/javascripts/discourse/templates/components/submission-field.hbs deleted file mode 100644 index c635ff47..00000000 --- a/assets/javascripts/discourse/templates/components/submission-field.hbs +++ /dev/null @@ -1,163 +0,0 @@ -{{#if isText}} - {{value.value}} -{{/if}} - -{{#if isTextArea}} -
-

- {{value.value}} -

- - {{toggleText}} - -
-{{/if}} - -{{#if isComposer}} -
-

- {{value.value}} -

- - {{toggleText}} - -
-{{/if}} - -{{#if isComposerPreview}} - {{d-icon "comment-alt"}} - - {{i18n "admin.wizard.submissions.composer_preview"}}: {{value.value}} - -{{/if}} - -{{#if isTextOnly}} - {{value.value}} -{{/if}} - -{{#if isDate}} - - {{d-icon "calendar"}}{{value.value}} - -{{/if}} - -{{#if isTime}} - - {{d-icon "clock"}}{{value.value}} - -{{/if}} - -{{#if isDateTime}} - - {{d-icon "calendar"}}{{format-date value.value format="medium"}} - -{{/if}} - -{{#if isNumber}} - {{value.value}} -{{/if}} - -{{#if isCheckbox}} - {{#if checkboxValue}} - - {{d-icon "check"}}{{value.value}} - - {{else}} - - {{d-icon "times"}}{{value.value}} - - {{/if}} -{{/if}} - -{{#if isUrl}} - - {{d-icon "link"}} - - {{value.value}} - - -{{/if}} - -{{#if isUpload}} - - {{file.original_filename}} - -{{/if}} - -{{#if isDropdown}} - - {{d-icon "check-square"}} - {{value.value}} - -{{/if}} - -{{#if isTag}} - {{#each value.value as |tag|}} - {{discourse-tag tag}} - {{/each}} -{{/if}} - -{{#if isCategory}} - - {{i18n "admin.wizard.submissions.category_id"}}: - - - {{value.value}} - -{{/if}} - -{{#if isGroup}} - - {{i18n "admin.wizard.submissions.group_id"}}: - - {{value.value}} -{{/if}} - -{{#if isUserSelector}} - {{#each submittedUsers as |user|}} - {{d-icon "user"}} - - {{user.username}} - - {{/each}} -{{/if}} - -{{#if isUser}} - {{#link-to "user" value}} - {{avatar value imageSize="tiny"}} - {{/link-to}} - - {{value.username}} - -{{/if}} - -{{#if isSubmittedAt}} - - {{d-icon "clock"}}{{format-date value format="tiny"}} - -{{/if}} diff --git a/assets/javascripts/discourse/templates/components/wizard-table-field.hbs b/assets/javascripts/discourse/templates/components/wizard-table-field.hbs new file mode 100644 index 00000000..bd7da5c4 --- /dev/null +++ b/assets/javascripts/discourse/templates/components/wizard-table-field.hbs @@ -0,0 +1,161 @@ +{{#if hasValue}} + {{#if isText}} + {{value.value}} + {{/if}} + + {{#if isLongtext}} +
+

+ {{value.value}} +

+ + {{toggleText}} + +
+ {{/if}} + + {{#if isComposer}} +
+

+ {{value.value}} +

+ + {{toggleText}} + +
+ {{/if}} + + {{#if isComposerPreview}} + {{d-icon "comment-alt"}} + + {{i18n "admin.wizard.submissions.composer_preview"}}: {{value.value}} + + {{/if}} + + {{#if isTextOnly}} + {{value.value}} + {{/if}} + + {{#if isDate}} + + {{d-icon "calendar"}}{{value.value}} + + {{/if}} + + {{#if isTime}} + + {{d-icon "clock"}}{{value.value}} + + {{/if}} + + {{#if isDateTime}} + + {{d-icon "calendar"}}{{format-date value.value format="medium"}} + + {{/if}} + + {{#if isNumber}} + {{value.value}} + {{/if}} + + {{#if isCheckbox}} + {{#if checkboxValue}} + + {{d-icon "check"}}{{value.value}} + + {{else}} + + {{d-icon "times"}}{{value.value}} + + {{/if}} + {{/if}} + + {{#if isUrl}} + + {{d-icon "link"}} + + {{value.value}} + + + {{/if}} + + {{#if isUpload}} + + {{file.original_filename}} + + {{/if}} + + {{#if isDropdown}} + + {{d-icon "check-square"}} + {{value.value}} + + {{/if}} + + {{#if isTag}} + {{#each value.value as |tag|}} + {{discourse-tag tag}} + {{/each}} + {{/if}} + + {{#if isCategory}} + + {{i18n "admin.wizard.submissions.category_id"}}: + + + {{value.value}} + + {{/if}} + + {{#if isGroup}} + + {{i18n "admin.wizard.submissions.group_id"}}: + + {{value.value}} + {{/if}} + + {{#if isUserSelector}} + {{#each submittedUsers as |user|}} + {{d-icon "user"}} + + {{user.username}} + + {{/each}} + {{/if}} + + {{#if isUser}} + {{#link-to "user" value}} + {{avatar value imageSize="tiny"}} + {{/link-to}} + {{/if}} + + {{#if showUsername}} + + {{username}} + + {{/if}} + + {{#if isSubmittedAt}} + + {{d-icon "clock"}}{{format-date value format="tiny"}} + + {{/if}} +{{else}} + — +{{/if}} diff --git a/assets/javascripts/discourse/templates/modal/admin-wizards-submissions-columns.hbs b/assets/javascripts/discourse/templates/modal/admin-wizards-columns.hbs similarity index 72% rename from assets/javascripts/discourse/templates/modal/admin-wizards-submissions-columns.hbs rename to assets/javascripts/discourse/templates/modal/admin-wizards-columns.hbs index a167a954..eb5218b1 100644 --- a/assets/javascripts/discourse/templates/modal/admin-wizards-submissions-columns.hbs +++ b/assets/javascripts/discourse/templates/modal/admin-wizards-columns.hbs @@ -1,14 +1,14 @@ -{{#d-modal-body title="directory.edit_columns.title"}} +{{#d-modal-body title="admin.wizard.edit_columns"}} {{#if loading}} {{loading-spinner size="large"}} {{else}}
- {{#each model.fields as |field|}} + {{#each model.columns as |column|}}
diff --git a/assets/stylesheets/common/wizard-admin.scss b/assets/stylesheets/common/wizard-admin.scss index 7027950a..7c6cd95d 100644 --- a/assets/stylesheets/common/wizard-admin.scss +++ b/assets/stylesheets/common/wizard-admin.scss @@ -66,10 +66,10 @@ } } -.wizard-submissions { +.wizard-table { overflow: scroll; - table td { + table td:not(.small) { min-width: 150px; } @@ -77,25 +77,26 @@ text-transform: capitalize; } - .submission-icon-item { + .wizard-table-icon-item { display: flex; align-items: center; + svg { margin-right: 5px; } } - .submission-checkbox-true { + .wizard-table-checkbox-true { text-transform: capitalize; color: var(--success); } - .submission-checkbox-false { + .wizard-table-checkbox-false { text-transform: capitalize; color: var(--danger); } - .submission-long-text { + .wizard-table-long-text { &-content { white-space: nowrap; word-wrap: break-word; @@ -114,25 +115,11 @@ } } - .submission-composer-text { + .wizard-table-composer-text { font-family: monospace; } } -.admin-wizards-logs { - .admin-wizard-controls { - h3 { - margin: 0 7px; - } - } - - .wizard-logs { - .date { - width: 100px; - } - } -} - .wizard-settings-parent { padding: 20px; border: 1px solid var(--primary-low); @@ -215,6 +202,10 @@ margin-bottom: 0; } + button { + font-size: 1rem; + } + .download-link { font-size: 1rem; line-height: 20px; @@ -230,10 +221,6 @@ font-size: 1rem; background-color: var(--primary-low); } - - button { - font-size: 1rem; - } } } diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index ae2594d2..cb30cf76 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -58,6 +58,9 @@ en: select_type: "Select a type" condition: "Condition" index: "Index" + edit_columns: "Edit Columns" + expand_text: "Read More" + collapse_text: "Show Less" pro_support_button: title: "Request Pro Support" @@ -108,6 +111,7 @@ en: viewing: "You're viewing the submissions of the %{wizardName}" documentation: "Check out the submissions documentation" logs: + select: "Select a wizard to see its logs" viewing: "View recent logs for wizards on the forum" documentation: "Check out the logs documentation" @@ -378,9 +382,6 @@ en: nav_label: "Submissions" title: "{{name}} Submissions" download: "Download" - edit_columns: "Edit Columns" - expand_text: "Read More" - collapse_text: "Show Less" group_id: "Group ID" category_id: "Category ID" composer_preview: "Composer Preview" @@ -437,9 +438,14 @@ en: log: label: "Logs" - + log: nav_label: "Logs" + title: "{{name}} Logs" + date: Date + action: Action + user: User + message: Message manager: nav_label: Manager diff --git a/config/routes.rb b/config/routes.rb index abe36479..3a37a137 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -38,6 +38,7 @@ Discourse::Application.routes.append do get 'admin/wizards/api/:name/authorize' => 'admin_api#authorize' get 'admin/wizards/logs' => 'admin_logs#index' + get 'admin/wizards/logs/:wizard_id' => 'admin_logs#show' get 'admin/wizards/manager' => 'admin_manager#index' get 'admin/wizards/manager/export' => 'admin_manager#export' diff --git a/controllers/custom_wizard/admin/logs.rb b/controllers/custom_wizard/admin/logs.rb index 976814f8..7ca37bb2 100644 --- a/controllers/custom_wizard/admin/logs.rb +++ b/controllers/custom_wizard/admin/logs.rb @@ -1,9 +1,44 @@ # frozen_string_literal: true class CustomWizard::AdminLogsController < CustomWizard::AdminController + before_action :find_wizard, except: [:index] + def index - render_serialized( - CustomWizard::Log.list(params[:page].to_i, params[:limit].to_i), - CustomWizard::LogSerializer + render json: ActiveModel::ArraySerializer.new( + CustomWizard::Wizard.list(current_user), + each_serializer: CustomWizard::BasicWizardSerializer ) end + + def show + render_json_dump( + wizard: CustomWizard::BasicWizardSerializer.new(@wizard, root: false), + logs: ActiveModel::ArraySerializer.new( + log_list.logs, + each_serializer: CustomWizard::LogSerializer + ), + total: log_list.total + ) + end + + protected + + def log_list + @log_list ||= begin + list = CustomWizard::Log.list(params[:page].to_i, params[:limit].to_i, params[:wizard_id]) + + if list.logs.any? && (usernames = list.logs.map(&:username)).present? + user_map = User.where(username: usernames) + .reduce({}) do |result, user| + result[user.username] = user + result + end + + list.logs.each do |log_item| + log_item.user = user_map[log_item.username] + end + end + + list + end + end end diff --git a/coverage/.last_run.json b/coverage/.last_run.json index cff5740b..23e2ecb9 100644 --- a/coverage/.last_run.json +++ b/coverage/.last_run.json @@ -1,5 +1,5 @@ { "result": { - "line": 91.96 + "line": 92.14 } } diff --git a/db/migrate/20210806135416_split_custom_wizard_log_fields.rb b/db/migrate/20210806135416_split_custom_wizard_log_fields.rb index 984a7a23..8107bc07 100644 --- a/db/migrate/20210806135416_split_custom_wizard_log_fields.rb +++ b/db/migrate/20210806135416_split_custom_wizard_log_fields.rb @@ -1,51 +1,56 @@ # frozen_string_literal: true class SplitCustomWizardLogFields < ActiveRecord::Migration[6.1] + KEY_MAP = { + wizard: "wizard_id", + action: "action", + user: "username", + date: "date", + message: "message" + } + def change reversible do |dir| dir.up do # separate wizard/action/user into their own keys - wizard_logs = PluginStoreRow.where(" - plugin_name = 'custom_wizard_log' - ") + wizard_logs = PluginStoreRow.where("plugin_name = 'custom_wizard_log'") - if wizard_logs.exists? - wizard_logs.each do |row| - begin - log_json = JSON.parse(row.value) - rescue TypeError, JSON::ParserError - next - end + if wizard_logs.exists? + wizard_logs.each do |row| + begin + log_json = JSON.parse(row.value) + rescue TypeError, JSON::ParserError + next + end - if log_json.key?('message') && log_json['message'].is_a?(String) + if log_json.key?('message') && log_json['message'].is_a?(String) - attr_strs = [] + attr_strs = [] - # assumes no whitespace in the values - attr_strs << log_json['message'].slice!(/(wizard: \S*; )/, 1) - attr_strs << log_json['message'].slice!(/(action: \S*; )/, 1) - attr_strs << log_json['message'].slice!(/(user: \S*; )/, 1) + # assumes no whitespace in the values + attr_strs << log_json['message'].slice!(/(wizard: \S*; )/, 1) + attr_strs << log_json['message'].slice!(/(action: \S*; )/, 1) + attr_strs << log_json['message'].slice!(/(user: \S*; )/, 1) - attr_strs.each do |attr_str| - if attr_str.is_a? String - attr_str.gsub!(/[;]/ , "") - key, value = attr_str.split(': ') - value.strip! if value - log_json[key] = value ? value : '' - end + attr_strs.each do |attr_str| + if attr_str.is_a? String + attr_str.gsub!(/[;]/ , "") + key, value = attr_str.split(': ') + value.strip! if value + key = KEY_MAP[key.to_sym] ? KEY_MAP[key.to_sym] : key + log_json[key] = value ? value : '' end - - row.value = log_json.to_json - row.save - end + + row.value = log_json.to_json + row.save end end + end end + dir.down do - wizard_logs = PluginStoreRow.where(" - plugin_name = 'custom_wizard_log' - ") + wizard_logs = PluginStoreRow.where("plugin_name = 'custom_wizard_log'") if wizard_logs.exists? wizard_logs.each do |row| @@ -56,19 +61,26 @@ class SplitCustomWizardLogFields < ActiveRecord::Migration[6.1] end # concatenate wizard/action/user to start of message - prefixes = log_json.extract!('wizard', 'action', 'user') + prefixes = log_json.extract!('wizard_id', 'action', 'username') + message_prefix = "" - message_prefix = prefixes.map { |k, v| "#{k}: #{v}" }.join('; ') + if prefixes.present? + message_prefix = prefixes.map do |k, v| + key = KEY_MAP.key(k) ? KEY_MAP.key(k) : k + "#{key.to_s}: #{v};" + end.join(' ') + end if log_json.key?('message') - log_json['message'] = "#{message_prefix}; #{log_json['message']}" + message = log_json['message'] + message = "#{message_prefix} #{message}" if message_prefix.present? + log_json['message'] = message else log_json['message'] = message_prefix end row.value = log_json.to_json row.save - end end end diff --git a/lib/custom_wizard/log.rb b/lib/custom_wizard/log.rb index c50a5712..cb5b78c7 100644 --- a/lib/custom_wizard/log.rb +++ b/lib/custom_wizard/log.rb @@ -2,46 +2,51 @@ class CustomWizard::Log include ActiveModel::Serialization - attr_accessor :date, :wizard, :action, :user, :message + attr_reader :date, :wizard_id, :action, :username, :message + attr_accessor :user PAGE_LIMIT = 100 def initialize(attrs) @date = attrs['date'] - @wizard = attrs['wizard'] @action = attrs['action'] - @user = attrs['user'] @message = attrs['message'] + @wizard_id = attrs['wizard_id'] + @username = attrs['username'] end - def self.create(wizard, action, user, message) + def self.create(wizard_id, action, username, message) log_id = SecureRandom.hex(12) PluginStore.set('custom_wizard_log', log_id.to_s, { date: Time.now, - wizard: wizard, + wizard_id: wizard_id, action: action, - user: user, + username: username, message: message } ) end - def self.list_query - PluginStoreRow.where(" - plugin_name = 'custom_wizard_log' AND - (value::json->'date') IS NOT NULL - ").order("value::json->>'date' DESC") + def self.list_query(wizard_id = nil) + query = PluginStoreRow.where("plugin_name = 'custom_wizard_log' AND (value::json->'date') IS NOT NULL") + query = query.where("(value::json->>'wizard_id') = ?", wizard_id) if wizard_id + query.order("value::json->>'date' DESC") end - def self.list(page = 0, limit = nil) + def self.list(page = 0, limit = nil, wizard_id = nil) limit = limit.to_i > 0 ? limit.to_i : PAGE_LIMIT page = page.to_i + logs = self.list_query(wizard_id) - self.list_query.limit(limit) + result = OpenStruct.new(logs: [], total: nil) + result.total = logs.size + result.logs = logs.limit(limit) .offset(page * limit) .map { |r| self.new(JSON.parse(r.value)) } + + result end end diff --git a/serializers/custom_wizard/log_serializer.rb b/serializers/custom_wizard/log_serializer.rb index c4683ba8..56c5fd8f 100644 --- a/serializers/custom_wizard/log_serializer.rb +++ b/serializers/custom_wizard/log_serializer.rb @@ -1,4 +1,10 @@ # frozen_string_literal: true + class CustomWizard::LogSerializer < ApplicationSerializer - attributes :date, :wizard, :action, :user, :message + attributes :date, + :action, + :username, + :message + + has_one :user, serializer: ::BasicUserSerializer, embed: :objects end diff --git a/spec/components/custom_wizard/log_spec.rb b/spec/components/custom_wizard/log_spec.rb index 62f2e6df..0b4832c0 100644 --- a/spec/components/custom_wizard/log_spec.rb +++ b/spec/components/custom_wizard/log_spec.rb @@ -10,19 +10,25 @@ describe CustomWizard::Log do it "creates logs" do expect( - CustomWizard::Log.list.length + CustomWizard::Log.list.logs.length ).to eq(3) end it "lists logs by time created" do expect( - CustomWizard::Log.list.first.message + CustomWizard::Log.list.logs.first.message ).to eq("Third log message") end it "paginates logs" do expect( - CustomWizard::Log.list(0, 2).length + CustomWizard::Log.list(0, 2).logs.length ).to eq(2) end + + it "lists logs by wizard" do + expect( + CustomWizard::Log.list(0, 2, 'third-test-wizard').logs.length + ).to eq(1) + end end diff --git a/spec/requests/custom_wizard/admin/logs_controller_spec.rb b/spec/requests/custom_wizard/admin/logs_controller_spec.rb index 5aaf9578..1559fa6f 100644 --- a/spec/requests/custom_wizard/admin/logs_controller_spec.rb +++ b/spec/requests/custom_wizard/admin/logs_controller_spec.rb @@ -3,21 +3,40 @@ require_relative '../../../plugin_helper' describe CustomWizard::AdminLogsController do fab!(:admin_user) { Fabricate(:user, admin: true) } + let(:template) { get_wizard_fixture("wizard") } before do - CustomWizard::Log.create('first-test-wizard', 'perform_first_action', 'first_test_user', 'First log message') - CustomWizard::Log.create('second-test-wizard', 'perform_second_action', 'second_test_user', 'Second log message') - CustomWizard::Log.create('third-test-wizard', 'perform_third_action', 'third_test_user', 'Third log message') + ["first", "second", "third"].each_with_index do |key, index| + temp = template.dup + temp["id"] = "#{key}_test_wizard" + CustomWizard::Template.save(temp, skip_jobs: true) + CustomWizard::Log.create("#{key}_test_wizard", "perform_#{key}_action", "#{key}_test_user", "#{key} log message") + end sign_in(admin_user) end - it "returns a list of logs" do + it "returns a list of wizards" do get "/admin/wizards/logs.json" expect(response.parsed_body.length).to eq(3) end + it "returns a list of logs for a wizard" do + get "/admin/wizards/logs/first_test_wizard.json" + expect(response.parsed_body['logs'].length).to eq(1) + end + it "paginates" do - get "/admin/wizards/logs.json", params: { page: 1, limit: 2 } - expect(response.parsed_body.length).to eq(1) + get "/admin/wizards/logs/first_test_wizard.json", params: { page: 1 } + expect(response.parsed_body['logs'].length).to eq(0) + end + + it "returns total logs for a wizard" do + get "/admin/wizards/logs/first_test_wizard.json" + expect(response.parsed_body['total']).to eq(1) + end + + it "returns basic wizard" do + get "/admin/wizards/logs/first_test_wizard.json" + expect(response.parsed_body['wizard']['id']).to eq("first_test_wizard") end end diff --git a/spec/serializers/custom_wizard/log_serializer_spec.rb b/spec/serializers/custom_wizard/log_serializer_spec.rb index b452b9c5..d520fefa 100644 --- a/spec/serializers/custom_wizard/log_serializer_spec.rb +++ b/spec/serializers/custom_wizard/log_serializer_spec.rb @@ -10,13 +10,12 @@ describe CustomWizard::LogSerializer do CustomWizard::Log.create('second-test-wizard', 'perform_second_action', 'second_test_user', 'Second log message') json_array = ActiveModel::ArraySerializer.new( - CustomWizard::Log.list(0), + CustomWizard::Log.list(0).logs, each_serializer: CustomWizard::LogSerializer ).as_json expect(json_array.length).to eq(2) - expect(json_array[0][:wizard]).to eq("second-test-wizard") expect(json_array[0][:action]).to eq("perform_second_action") - expect(json_array[0][:user]).to eq("second_test_user") + expect(json_array[0][:username]).to eq('second_test_user') expect(json_array[0][:message]).to eq("Second log message") end end From 270d3bccf5468ac5f1cfc0759f577feafe6c86fb Mon Sep 17 00:00:00 2001 From: angusmcleod Date: Tue, 14 Sep 2021 11:33:16 +0800 Subject: [PATCH 045/160] IMPROVE: translation feature --- .../components/wizard-table-field.js.es6 | 12 +++- .../discourse/lib/wizard-schema.js.es6 | 2 - .../models/custom-wizard-logs.js.es6 | 12 +++- .../templates/admin-wizards-wizard-show.hbs | 36 +++++----- .../components/wizard-custom-field.hbs | 13 ---- .../components/wizard-custom-step.hbs | 13 ---- .../components/wizard-composer-editor.js.es6 | 2 +- .../initializers/custom-wizard-field.js.es6 | 32 +++++++-- .../initializers/custom-wizard-step.js.es6 | 32 +++++++-- .../javascripts/wizard/lib/wizard-i18n.js.es6 | 14 +++- .../javascripts/wizard/models/custom.js.es6 | 7 +- .../components/wizard-field-text.hbs | 2 +- .../components/wizard-field-textarea.hbs | 2 +- .../templates/components/wizard-field.hbs | 2 +- config/locales/client.en.yml | 6 +- lib/custom_wizard/field.rb | 2 - lib/custom_wizard/step.rb | 1 - lib/custom_wizard/validators/template.rb | 23 ++++-- .../custom_wizard/wizard_field_serializer.rb | 37 +++++----- .../custom_wizard/wizard_serializer.rb | 7 +- .../custom_wizard/wizard_step_serializer.rb | 20 +++--- spec/components/custom_wizard/action_spec.rb | 2 +- spec/components/custom_wizard/builder_spec.rb | 8 ++- .../custom_wizard/custom_field_spec.rb | 2 +- spec/components/custom_wizard/field_spec.rb | 1 - spec/components/custom_wizard/mapper_spec.rb | 2 +- .../custom_wizard/template_validator_spec.rb | 70 ++++++++++++------- .../custom_field_extensions_spec.rb | 2 +- spec/plugin_helper.rb | 2 +- .../custom_field_extensions_spec.rb | 2 +- .../custom_wizard/steps_controller_spec.rb | 2 +- .../wizard_step_serializer_spec.rb | 3 +- 32 files changed, 230 insertions(+), 143 deletions(-) diff --git a/assets/javascripts/discourse/components/wizard-table-field.js.es6 b/assets/javascripts/discourse/components/wizard-table-field.js.es6 index ce6b1584..049b4d40 100644 --- a/assets/javascripts/discourse/components/wizard-table-field.js.es6 +++ b/assets/javascripts/discourse/components/wizard-table-field.js.es6 @@ -102,8 +102,12 @@ export default Component.extend({ @discourseComputed("isUser", "field", "value") username(isUser, field, value) { - if (isUser) {return value.username;} - if (field === "username") {return value.value;} + if (isUser) { + return value.username; + } + if (field === "username") { + return value.value; + } return null; }, @@ -111,7 +115,9 @@ export default Component.extend({ @discourseComputed("username") userProfileUrl(username) { - if (username) {return `/u/${username}`;} + if (username) { + return `/u/${username}`; + } return "/"; }, diff --git a/assets/javascripts/discourse/lib/wizard-schema.js.es6 b/assets/javascripts/discourse/lib/wizard-schema.js.es6 index a25ab6d0..a20b61a7 100644 --- a/assets/javascripts/discourse/lib/wizard-schema.js.es6 +++ b/assets/javascripts/discourse/lib/wizard-schema.js.es6 @@ -39,7 +39,6 @@ const step = { id: null, index: null, title: null, - key: null, banner: null, raw_description: null, required_data: null, @@ -68,7 +67,6 @@ const field = { description: null, property: null, required: null, - key: null, type: null, condition: null, }, diff --git a/assets/javascripts/discourse/models/custom-wizard-logs.js.es6 b/assets/javascripts/discourse/models/custom-wizard-logs.js.es6 index 1bd19dfe..23565e2c 100644 --- a/assets/javascripts/discourse/models/custom-wizard-logs.js.es6 +++ b/assets/javascripts/discourse/models/custom-wizard-logs.js.es6 @@ -31,14 +31,20 @@ CustomWizardLogs.reopenClass({ result.logs = result.logs.map((item) => { let map = {}; - if (item.date) {map.date = logItem(item, "date");} - if (item.action) {map.action = logItem(item, "action");} + if (item.date) { + map.date = logItem(item, "date"); + } + if (item.action) { + map.action = logItem(item, "action"); + } if (item.user) { map.user = item.user; } else { map.user = logItem(item, "username"); } - if (item.message) {map.message = logItem(item, "message");} + if (item.message) { + map.message = logItem(item, "message"); + } return map; }); diff --git a/assets/javascripts/discourse/templates/admin-wizards-wizard-show.hbs b/assets/javascripts/discourse/templates/admin-wizards-wizard-show.hbs index 7645c20e..472cbfcb 100644 --- a/assets/javascripts/discourse/templates/admin-wizards-wizard-show.hbs +++ b/assets/javascripts/discourse/templates/admin-wizards-wizard-show.hbs @@ -126,25 +126,29 @@
-
-
- + {{#if proSubscribed}} +
+
+ + {{i18n "admin.wizard.pro.label"}} +
+
+ {{input type="checkbox" checked=wizard.save_submissions}} + {{i18n "admin.wizard.save_submissions_label"}} +
-
- {{input type="checkbox" checked=wizard.save_submissions}} - {{i18n "admin.wizard.save_submissions_label"}} -
-
-
-
- +
+
+ + {{i18n "admin.wizard.pro.label"}} +
+
+ {{input type="checkbox" checked=wizard.restart_on_revisit}} + {{i18n "admin.wizard.restart_on_revisit_label"}} +
-
- {{input type="checkbox" checked=wizard.restart_on_revisit}} - {{i18n "admin.wizard.restart_on_revisit_label"}} -
-
+ {{/if}}
{{wizard-links diff --git a/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs b/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs index 8c8bb6d4..fe3089ac 100644 --- a/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs +++ b/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs @@ -253,19 +253,6 @@
{{/if}} -
-
- - {{i18n "admin.wizard.pro.label"}} -
-
- {{input - name="key" - value=field.key - placeholderKey="admin.wizard.translation_placeholder"}} -
-
- {{#if validations}} {{wizard-realtime-validations field=field validations=validations}} {{/if}} diff --git a/assets/javascripts/discourse/templates/components/wizard-custom-step.hbs b/assets/javascripts/discourse/templates/components/wizard-custom-step.hbs index 91476ae3..3feb6731 100644 --- a/assets/javascripts/discourse/templates/components/wizard-custom-step.hbs +++ b/assets/javascripts/discourse/templates/components/wizard-custom-step.hbs @@ -100,19 +100,6 @@ )}} - -
-
- - {{i18n "admin.wizard.pro.label"}} -
-
- {{input - name="key" - value=step.key - placeholderKey="admin.wizard.translation_placeholder"}} -
-
{{/if}} {{wizard-links diff --git a/assets/javascripts/wizard/components/wizard-composer-editor.js.es6 b/assets/javascripts/wizard/components/wizard-composer-editor.js.es6 index 2a92f12a..c4cc950c 100644 --- a/assets/javascripts/wizard/components/wizard-composer-editor.js.es6 +++ b/assets/javascripts/wizard/components/wizard-composer-editor.js.es6 @@ -33,7 +33,7 @@ export default ComposerEditor.extend({ lastValidatedAt: "lastValidatedAt", popupMenuOptions: [], draftStatus: "null", - replyPlaceholder: alias("field.placeholder"), + replyPlaceholder: alias("field.translatedPlaceholder"), @on("didInsertElement") _composerEditorInit() { diff --git a/assets/javascripts/wizard/initializers/custom-wizard-field.js.es6 b/assets/javascripts/wizard/initializers/custom-wizard-field.js.es6 index f5deb927..6f2a80b7 100644 --- a/assets/javascripts/wizard/initializers/custom-wizard-field.js.es6 +++ b/assets/javascripts/wizard/initializers/custom-wizard-field.js.es6 @@ -16,19 +16,23 @@ export default { const DEditor = requirejs("discourse/components/d-editor").default; const { clipboardHelpers } = requirejs("discourse/lib/utilities"); const toMarkdown = requirejs("discourse/lib/to-markdown").default; + const { translatedText } = requirejs( + "discourse/plugins/discourse-custom-wizard/wizard/lib/wizard-i18n" + ); FieldComponent.reopen({ classNameBindings: ["field.id"], + @discourseComputed("field.translatedDescription") + cookedDescription(description) { + return cook(description); + }, + @discourseComputed("field.type") textType(fieldType) { return ["text", "textarea"].includes(fieldType); }, - cookedDescription: function () { - return cook(this.get("field.description")); - }.property("field.description"), - inputComponentName: function () { const type = this.get("field.type"); const id = this.get("field.id"); @@ -57,6 +61,26 @@ export default { ]; FieldModel.reopen({ + @discourseComputed("wizardId", "stepId", "id") + i18nKey(wizardId, stepId, id) { + return `${wizardId}.${stepId}.${id}`; + }, + + @discourseComputed("i18nKey", "label") + translatedLabel(i18nKey, label) { + return translatedText(`${i18nKey}.label`, label); + }, + + @discourseComputed("i18nKey", "placeholder") + translatedPlaceholder(i18nKey, placeholder) { + return translatedText(`${i18nKey}.placeholder`, placeholder); + }, + + @discourseComputed("i18nKey", "description") + translatedDescription(i18nKey, description) { + return translatedText(`${i18nKey}.description`, description); + }, + check() { if (this.customCheck) { return this.customCheck(); diff --git a/assets/javascripts/wizard/initializers/custom-wizard-step.js.es6 b/assets/javascripts/wizard/initializers/custom-wizard-step.js.es6 index fbbe7d8b..6a679796 100644 --- a/assets/javascripts/wizard/initializers/custom-wizard-step.js.es6 +++ b/assets/javascripts/wizard/initializers/custom-wizard-step.js.es6 @@ -22,8 +22,26 @@ export default { ).cook; const { schedule } = requirejs("@ember/runloop"); const { alias, not } = requirejs("@ember/object/computed"); + const { translatedText } = requirejs( + "discourse/plugins/discourse-custom-wizard/wizard/lib/wizard-i18n" + ); StepModel.reopen({ + @discourseComputed("wizardId", "id") + i18nKey(wizardId, stepId) { + return `${wizardId}.${stepId}`; + }, + + @discourseComputed("i18nKey", "title") + translatedTitle(i18nKey, title) { + return translatedText(`${i18nKey}.title`, title); + }, + + @discourseComputed("i18nKey", "description") + translatedDescription(i18nKey, description) { + return translatedText(`${i18nKey}.description`, description); + }, + save() { const wizardId = this.get("wizardId"); const fields = {}; @@ -128,13 +146,15 @@ export default { return index === 0 && !required; }.property("step.index", "wizard.required"), - cookedTitle: function () { - return cook(this.get("step.title")); - }.property("step.title"), + @discourseComputed("step.translatedTitle") + cookedTitle(title) { + return cook(title); + }, - cookedDescription: function () { - return cook(this.get("step.description")); - }.property("step.description"), + @discourseComputed("step.translatedDescription") + cookedDescription(description) { + return cook(description); + }, bannerImage: function () { const src = this.get("step.banner"); diff --git a/assets/javascripts/wizard/lib/wizard-i18n.js.es6 b/assets/javascripts/wizard/lib/wizard-i18n.js.es6 index fdefab77..ae69f54c 100644 --- a/assets/javascripts/wizard/lib/wizard-i18n.js.es6 +++ b/assets/javascripts/wizard/lib/wizard-i18n.js.es6 @@ -20,13 +20,25 @@ const translationExists = (key) => { ); }; +const getThemeKey = (key) => { + const themeId = getThemeId(); + return `theme_translations.${themeId}.${key}`; +}; + +const translatedText = (key, value) => { + const themeKey = getThemeKey(key); + return translationExists(themeKey) ? I18n.t(themeKey) : value; +}; + +export { translatedText }; + const WizardI18n = (key, params = {}) => { const themeId = getThemeId(); if (!themeId) { return I18n.t(key, params); } - const themeKey = `theme_translations.${themeId}.${key}`; + let themeKey = getThemeKey(key); if (translationExists(themeKey)) { return I18n.t(themeKey, params); diff --git a/assets/javascripts/wizard/models/custom.js.es6 b/assets/javascripts/wizard/models/custom.js.es6 index 31a403da..39ce098c 100644 --- a/assets/javascripts/wizard/models/custom.js.es6 +++ b/assets/javascripts/wizard/models/custom.js.es6 @@ -41,6 +41,7 @@ CustomWizard.reopenClass({ wizardJson.steps = wizardJson.steps .map((step) => { const stepObj = Step.create(step); + stepObj.wizardId = wizardJson.id; stepObj.fields.sort((a, b) => { return parseFloat(a.number) - parseFloat(b.number); @@ -57,7 +58,11 @@ CustomWizard.reopenClass({ } }); - stepObj.fields = stepObj.fields.map((f) => WizardField.create(f)); + stepObj.fields = stepObj.fields.map((f) => { + f.wizardId = wizardJson.id; + f.stepId = stepObj.id; + return WizardField.create(f); + }); return stepObj; }) diff --git a/assets/javascripts/wizard/templates/components/wizard-field-text.hbs b/assets/javascripts/wizard/templates/components/wizard-field-text.hbs index be44e8e7..08733d3f 100644 --- a/assets/javascripts/wizard/templates/components/wizard-field-text.hbs +++ b/assets/javascripts/wizard/templates/components/wizard-field-text.hbs @@ -1 +1 @@ -{{input id=field.id value=field.value class=fieldClass placeholder=field.placeholder tabindex=field.tabindex autocomplete=autocomplete}} +{{input id=field.id value=field.value class=fieldClass placeholder=field.translatedPlaceholder tabindex=field.tabindex autocomplete=autocomplete}} diff --git a/assets/javascripts/wizard/templates/components/wizard-field-textarea.hbs b/assets/javascripts/wizard/templates/components/wizard-field-textarea.hbs index 6efb7560..dda299bc 100644 --- a/assets/javascripts/wizard/templates/components/wizard-field-textarea.hbs +++ b/assets/javascripts/wizard/templates/components/wizard-field-textarea.hbs @@ -1 +1 @@ -{{textarea id=field.id value=field.value class=fieldClass placeholder=field.placeholder tabindex=field.tabindex}} +{{textarea id=field.id value=field.value class=fieldClass placeholder=field.translatedPlaceholder tabindex=field.tabindex}} diff --git a/assets/javascripts/wizard/templates/components/wizard-field.hbs b/assets/javascripts/wizard/templates/components/wizard-field.hbs index e0dd1551..efda8629 100644 --- a/assets/javascripts/wizard/templates/components/wizard-field.hbs +++ b/assets/javascripts/wizard/templates/components/wizard-field.hbs @@ -1,5 +1,5 @@ {{#if field.image}} diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index cb30cf76..04b9ceab 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -44,8 +44,6 @@ en: key: "Key" value: "Value" profile: "profile" - translation: "Translation" - translation_placeholder: "key" type: "Type" none: "Make a selection" submission_key: 'submission key' @@ -174,10 +172,10 @@ en: banner: "Banner" description: "Description" required_data: - label: "Required" + label: "Required Data" not_permitted_message: "Message shown when required data not present" permitted_params: - label: "Params" + label: "Permitted Params" force_final: label: "Conditional Final Step" description: "Display this step as the final step if conditions on later steps have not passed when the user reaches this step." diff --git a/lib/custom_wizard/field.rb b/lib/custom_wizard/field.rb index eb4af65d..888afa6b 100644 --- a/lib/custom_wizard/field.rb +++ b/lib/custom_wizard/field.rb @@ -11,7 +11,6 @@ class CustomWizard::Field :label, :description, :image, - :key, :validations, :min_length, :max_length, @@ -36,7 +35,6 @@ class CustomWizard::Field @value = attrs[:value] || default_value @description = attrs[:description] @image = attrs[:image] - @key = attrs[:key] @validations = attrs[:validations] @min_length = attrs[:min_length] @max_length = attrs[:max_length] diff --git a/lib/custom_wizard/step.rb b/lib/custom_wizard/step.rb index 5ffd8024..17827ea1 100644 --- a/lib/custom_wizard/step.rb +++ b/lib/custom_wizard/step.rb @@ -9,7 +9,6 @@ class CustomWizard::Step attr_accessor :index, :title, :description, - :key, :permitted, :permitted_message, :fields, diff --git a/lib/custom_wizard/validators/template.rb b/lib/custom_wizard/validators/template.rb index 839a070f..4311bbbc 100644 --- a/lib/custom_wizard/validators/template.rb +++ b/lib/custom_wizard/validators/template.rb @@ -54,10 +54,15 @@ class CustomWizard::TemplateValidator def self.pro { - wizard: {}, + wizard: { + save_submissions: 'false', + restart_on_revisit: 'true', + }, step: { condition: 'present', - index: 'conditional' + index: 'conditional', + required_data: 'present', + permitted_params: 'present' }, field: { condition: 'present', @@ -87,10 +92,12 @@ class CustomWizard::TemplateValidator def validate_pro(object, type) self.class.pro[type].each do |property, pro_type| - is_pro = object[property.to_s].present? && ( - pro_type === 'present' || - (pro_type === 'conditional' && object[property.to_s].is_a?(Hash)) || - (pro_type.is_a?(Array) && pro_type.include?(object[property.to_s])) + val = object[property.to_s] + is_pro = (val != nil) && ( + pro_type === 'present' && val.present? || + (['true', 'false'].include?(pro_type) && cast_bool(val) == cast_bool(pro_type)) || + (pro_type === 'conditional' && val.is_a?(Hash)) || + (pro_type.is_a?(Array) && pro_type.include?(val)) ) if is_pro && !@pro.subscribed? @@ -122,4 +129,8 @@ class CustomWizard::TemplateValidator errors.add :base, I18n.t("wizard.validation.after_time") end end + + def cast_bool(val) + ActiveRecord::Type::Boolean.new.cast(val) + end end diff --git a/serializers/custom_wizard/wizard_field_serializer.rb b/serializers/custom_wizard/wizard_field_serializer.rb index 37f7a80f..fc2575ef 100644 --- a/serializers/custom_wizard/wizard_field_serializer.rb +++ b/serializers/custom_wizard/wizard_field_serializer.rb @@ -41,13 +41,8 @@ class CustomWizard::FieldSerializer < ::ApplicationSerializer object.value end - def i18n_key - @i18n_key ||= "wizard.step.#{object.step.id}.fields.#{object.id}".underscore - end - def label - return object.label if object.label.present? - I18n.t("#{object.key || i18n_key}.label", default: '') + I18n.t("#{i18n_key}.label", default: object.label, base_url: Discourse.base_url) end def include_label? @@ -55,14 +50,21 @@ class CustomWizard::FieldSerializer < ::ApplicationSerializer end def description - return object.description if object.description.present? - I18n.t("#{object.key || i18n_key}.description", default: '', base_url: Discourse.base_url) + I18n.t("#{i18n_key}.description", default: object.description, base_url: Discourse.base_url) end def include_description? description.present? end + def placeholder + I18n.t("#{i18n_key}.placeholder", default: object.placeholder) + end + + def include_placeholder? + placeholder.present? + end + def image object.image end @@ -71,15 +73,6 @@ class CustomWizard::FieldSerializer < ::ApplicationSerializer object.image.present? end - def placeholder - return object.placeholder if object.placeholder.present? - I18n.t("#{object.key || i18n_key}.placeholder", default: '') - end - - def include_placeholder? - placeholder.present? - end - def file_types object.file_types end @@ -122,4 +115,14 @@ class CustomWizard::FieldSerializer < ::ApplicationSerializer def preview_template object.preview_template end + + protected + + def i18n_key + @i18n_key ||= "#{object.step.wizard.id}.#{object.step.id}.#{object.id}".underscore + end + + def subscribed? + @subscribed ||= CustomWizard::Pro.subscribed? + end end diff --git a/serializers/custom_wizard/wizard_serializer.rb b/serializers/custom_wizard/wizard_serializer.rb index 7a162ba5..4a29d3ea 100644 --- a/serializers/custom_wizard/wizard_serializer.rb +++ b/serializers/custom_wizard/wizard_serializer.rb @@ -9,7 +9,8 @@ class CustomWizard::WizardSerializer < CustomWizard::BasicWizardSerializer :required, :permitted, :uncategorized_category_id, - :categories + :categories, + :pro_subscribed has_many :steps, serializer: ::CustomWizard::StepSerializer, embed: :objects has_one :user, serializer: ::BasicUserSerializer, embed: :objects @@ -60,4 +61,8 @@ class CustomWizard::WizardSerializer < CustomWizard::BasicWizardSerializer def categories object.categories.map { |c| c.to_h } end + + def pro_subscribed + CustomWizard::Pro.subscribed? + end end diff --git a/serializers/custom_wizard/wizard_step_serializer.rb b/serializers/custom_wizard/wizard_step_serializer.rb index 85f527bb..463fa3d6 100644 --- a/serializers/custom_wizard/wizard_step_serializer.rb +++ b/serializers/custom_wizard/wizard_step_serializer.rb @@ -39,13 +39,8 @@ class CustomWizard::StepSerializer < ::ApplicationSerializer object.previous.present? end - def i18n_key - @i18n_key ||= "wizard.step.#{object.id}".underscore - end - def title - return PrettyText.cook(object.title) if object.title - PrettyText.cook(I18n.t("#{object.key || i18n_key}.title", default: '')) + I18n.t("#{i18n_key}.title", default: object.title, base_url: Discourse.base_url) end def include_title? @@ -53,8 +48,7 @@ class CustomWizard::StepSerializer < ::ApplicationSerializer end def description - return object.description if object.description - PrettyText.cook(I18n.t("#{object.key || i18n_key}.description", default: '', base_url: Discourse.base_url)) + I18n.t("#{i18n_key}.description", default: object.description, base_url: Discourse.base_url) end def include_description? @@ -80,4 +74,14 @@ class CustomWizard::StepSerializer < ::ApplicationSerializer def final object.final? end + + protected + + def i18n_key + @i18n_key ||= "#{object.wizard.id}.#{object.id}".underscore + end + + def subscribed? + @subscribed ||= CustomWizard::Pro.subscribed? + end end diff --git a/spec/components/custom_wizard/action_spec.rb b/spec/components/custom_wizard/action_spec.rb index 34a08461..c92a0d61 100644 --- a/spec/components/custom_wizard/action_spec.rb +++ b/spec/components/custom_wizard/action_spec.rb @@ -176,7 +176,7 @@ describe CustomWizard::Action do context "pro actions" do before do - enable_pro + enable_subscription end it '#send_message' do diff --git a/spec/components/custom_wizard/builder_spec.rb b/spec/components/custom_wizard/builder_spec.rb index 9b9c6000..28880409 100644 --- a/spec/components/custom_wizard/builder_spec.rb +++ b/spec/components/custom_wizard/builder_spec.rb @@ -176,6 +176,7 @@ describe CustomWizard::Builder do context "restart is enabled" do before do + enable_subscription @template[:restart_on_revisit] = true CustomWizard::Template.save(@template.as_json) end @@ -204,6 +205,7 @@ describe CustomWizard::Builder do context 'with required data' do before do + enable_subscription @template[:steps][0][:required_data] = required_data_json['required_data'] @template[:steps][0][:required_data_message] = required_data_json['required_data_message'] CustomWizard::Template.save(@template.as_json) @@ -239,6 +241,7 @@ describe CustomWizard::Builder do context "with permitted params" do before do + enable_subscription @template[:steps][0][:permitted_params] = permitted_param_json['permitted_params'] CustomWizard::Template.save(@template.as_json) end @@ -253,7 +256,7 @@ describe CustomWizard::Builder do context "with condition" do before do - enable_pro + enable_subscription @template[:steps][0][:condition] = user_condition_json['condition'] CustomWizard::Template.save(@template.as_json) end @@ -292,7 +295,7 @@ describe CustomWizard::Builder do context "with condition" do before do - enable_pro + enable_subscription @template[:steps][0][:fields][0][:condition] = user_condition_json['condition'] CustomWizard::Template.save(@template.as_json) end @@ -324,6 +327,7 @@ describe CustomWizard::Builder do context 'save submissions disabled' do before do + enable_subscription @template[:save_submissions] = false CustomWizard::Template.save(@template.as_json) @wizard = CustomWizard::Builder.new(@template[:id], user).build diff --git a/spec/components/custom_wizard/custom_field_spec.rb b/spec/components/custom_wizard/custom_field_spec.rb index 2204264f..3bcfab46 100644 --- a/spec/components/custom_wizard/custom_field_spec.rb +++ b/spec/components/custom_wizard/custom_field_spec.rb @@ -217,7 +217,7 @@ describe CustomWizard::CustomField do context "with a pro subscription" do before do - enable_pro + enable_subscription end it "saves pro field types" do diff --git a/spec/components/custom_wizard/field_spec.rb b/spec/components/custom_wizard/field_spec.rb index 0fcf9fc2..c6daa516 100644 --- a/spec/components/custom_wizard/field_spec.rb +++ b/spec/components/custom_wizard/field_spec.rb @@ -23,7 +23,6 @@ describe CustomWizard::Field do expect(field.image).to eq("field_image_url.png") expect(field.description).to eq("Field description") expect(field.required).to eq(true) - expect(field.key).to eq("field.locale.key") expect(field.type).to eq("field_type") expect(field.content).to eq([]) end diff --git a/spec/components/custom_wizard/mapper_spec.rb b/spec/components/custom_wizard/mapper_spec.rb index b210c588..7ef2a5a8 100644 --- a/spec/components/custom_wizard/mapper_spec.rb +++ b/spec/components/custom_wizard/mapper_spec.rb @@ -359,7 +359,7 @@ describe CustomWizard::Mapper do context "with a pro subscription" do before do - enable_pro + enable_subscription end it "treats replaced values as string literals" do diff --git a/spec/components/custom_wizard/template_validator_spec.rb b/spec/components/custom_wizard/template_validator_spec.rb index 7a84660c..e8b8af28 100644 --- a/spec/components/custom_wizard/template_validator_spec.rb +++ b/spec/components/custom_wizard/template_validator_spec.rb @@ -43,47 +43,63 @@ describe CustomWizard::TemplateValidator do ).to eq(false) end - it "invalidates pro step attributes without a pro subscription" do - template[:steps][0][:condition] = user_condition['condition'] - expect( - CustomWizard::TemplateValidator.new(template).perform - ).to eq(false) - end - - it "invalidates pro field attributes without a pro subscription" do - template[:steps][0][:fields][0][:condition] = user_condition['condition'] - expect( - CustomWizard::TemplateValidator.new(template).perform - ).to eq(false) - end - - it "invalidates pro actions without a pro subscription" do - template[:actions] << create_category - expect( - CustomWizard::TemplateValidator.new(template).perform - ).to eq(false) - end - - context "with pro subscription" do - before do - enable_pro + context "without subscription" do + it "invalidates subscription wizard attributes" do + template[:save_submissions] = false + expect( + CustomWizard::TemplateValidator.new(template).perform + ).to eq(false) end - it "validates pro step attributes" do + it "invalidates subscription step attributes" do + template[:steps][0][:condition] = user_condition['condition'] + expect( + CustomWizard::TemplateValidator.new(template).perform + ).to eq(false) + end + + it "invalidates subscription field attributes" do + template[:steps][0][:fields][0][:condition] = user_condition['condition'] + expect( + CustomWizard::TemplateValidator.new(template).perform + ).to eq(false) + end + + it "invalidates subscription actions" do + template[:actions] << create_category + expect( + CustomWizard::TemplateValidator.new(template).perform + ).to eq(false) + end + end + + context "with subscription" do + before do + enable_subscription + end + + it "validates wizard attributes" do + template[:save_submissions] = false + expect( + CustomWizard::TemplateValidator.new(template).perform + ).to eq(true) + end + + it "validates step attributes" do template[:steps][0][:condition] = user_condition['condition'] expect( CustomWizard::TemplateValidator.new(template).perform ).to eq(true) end - it "validates pro field attributes" do + it "validates field attributes" do template[:steps][0][:fields][0][:condition] = user_condition['condition'] expect( CustomWizard::TemplateValidator.new(template).perform ).to eq(true) end - it "validates pro actions" do + it "validates actions" do template[:actions] << create_category expect( CustomWizard::TemplateValidator.new(template).perform diff --git a/spec/extensions/custom_field_extensions_spec.rb b/spec/extensions/custom_field_extensions_spec.rb index bf1b3ff2..7ada17a4 100644 --- a/spec/extensions/custom_field_extensions_spec.rb +++ b/spec/extensions/custom_field_extensions_spec.rb @@ -74,7 +74,7 @@ describe "custom field extensions" do context "pro custom fields" do before do - enable_pro + enable_subscription pro_custom_field_json['custom_fields'].each do |field_json| custom_field = CustomWizard::CustomField.new(nil, field_json) diff --git a/spec/plugin_helper.rb b/spec/plugin_helper.rb index 4aa34029..348e7791 100644 --- a/spec/plugin_helper.rb +++ b/spec/plugin_helper.rb @@ -28,7 +28,7 @@ def authenticate_pro CustomWizard::ProAuthentication.any_instance.stubs(:active?).returns(true) end -def enable_pro +def enable_subscription CustomWizard::Pro.any_instance.stubs(:subscribed?).returns(true) end diff --git a/spec/requests/custom_wizard/custom_field_extensions_spec.rb b/spec/requests/custom_wizard/custom_field_extensions_spec.rb index 64d7c755..9ec3f5b1 100644 --- a/spec/requests/custom_wizard/custom_field_extensions_spec.rb +++ b/spec/requests/custom_wizard/custom_field_extensions_spec.rb @@ -40,7 +40,7 @@ describe "custom field extensions" do context "with a pro subscription" do before do - enable_pro + enable_subscription pro_custom_field_json['custom_fields'].each do |field_json| custom_field = CustomWizard::CustomField.new(nil, field_json) diff --git a/spec/requests/custom_wizard/steps_controller_spec.rb b/spec/requests/custom_wizard/steps_controller_spec.rb index 85353e4c..d4ad4042 100644 --- a/spec/requests/custom_wizard/steps_controller_spec.rb +++ b/spec/requests/custom_wizard/steps_controller_spec.rb @@ -121,7 +121,7 @@ describe CustomWizard::StepsController do context "pro" do before do - enable_pro + enable_subscription end it "raises an error when user cant see the step due to conditions" do diff --git a/spec/serializers/custom_wizard/wizard_step_serializer_spec.rb b/spec/serializers/custom_wizard/wizard_step_serializer_spec.rb index 53afa8e5..0a9f7e89 100644 --- a/spec/serializers/custom_wizard/wizard_step_serializer_spec.rb +++ b/spec/serializers/custom_wizard/wizard_step_serializer_spec.rb @@ -18,7 +18,7 @@ describe CustomWizard::StepSerializer do each_serializer: described_class, scope: Guardian.new(user) ).as_json - expect(json_array[0][:title]).to eq("

Text

") + expect(json_array[0][:title]).to eq("Text") expect(json_array[0][:description]).to eq("

Text inputs!

") expect(json_array[1][:index]).to eq(1) end @@ -34,6 +34,7 @@ describe CustomWizard::StepSerializer do context 'with required data' do before do + enable_subscription wizard_template['steps'][0]['required_data'] = required_data_json['required_data'] wizard_template['steps'][0]['required_data_message'] = required_data_json['required_data_message'] CustomWizard::Template.save(wizard_template) From 084c6f4a7afc773e75faa2770e6e155e49771658 Mon Sep 17 00:00:00 2001 From: angusmcleod Date: Fri, 24 Sep 2021 17:58:42 +0800 Subject: [PATCH 046/160] wip --- .../components/custom-field-input.js.es6 | 22 +- .../components/subscription-container.js.es6 | 21 ++ .../components/wizard-custom-action.js.es6 | 10 +- .../discourse/components/wizard-notice.js.es6 | 34 +++ .../wizard-realtime-validations.js.es6 | 2 +- ...s6 => wizard-subscription-selector.js.es6} | 6 +- ...izard-subscription-selector-header.js.es6} | 2 +- .../wizard-subscription-selector-row.js.es6} | 0 ...tion.js.es6 => wizard-subscription.js.es6} | 12 +- .../custom-wizard-issue-notice.hbs | 3 + .../custom-wizard-issue-notice.js.es6 | 12 + .../admin-menu/wizards-nav-button.hbs | 4 + ....es6 => admin-wizards-subscription.js.es6} | 2 +- .../controllers/admin-wizards.js.es6 | 20 ++ .../custom-wizard-admin-route-map.js.es6 | 4 +- .../initializers/custom-wizard-edits.js.es6 | 28 +++ .../discourse/lib/wizard-schema.js.es6 | 8 + .../models/custom-wizard-notice.js.es6 | 23 ++ ....es6 => custom-wizard-subscription.js.es6} | 12 +- .../routes/admin-wizards-custom-fields.js.es6 | 4 +- ....es6 => admin-wizards-subscription.js.es6} | 6 +- .../routes/admin-wizards-wizard-show.js.es6 | 2 +- .../discourse/routes/admin-wizards.js.es6 | 14 +- .../templates/admin-wizards-custom-fields.hbs | 2 +- .../discourse/templates/admin-wizards-pro.hbs | 34 --- .../templates/admin-wizards-subscription.hbs | 35 +++ .../templates/admin-wizards-wizard-show.hbs | 14 +- .../discourse/templates/admin-wizards.hbs | 9 +- .../components/custom-field-input.hbs | 4 +- .../components/subscription-container.hbs | 12 + .../components/wizard-custom-action.hbs | 2 +- .../components/wizard-custom-field.hbs | 12 +- .../components/wizard-custom-step.hbs | 12 +- .../templates/components/wizard-notice.hbs | 37 +++ .../wizard-realtime-validations.hbs | 1 - .../wizard-subscription-selector-header.hbs} | 4 +- .../wizard-subscription-selector-row.hbs} | 4 +- ...bscription.hbs => wizard-subscription.hbs} | 6 +- .../wizard-admin.scss => admin/admin.scss} | 138 +++++++++-- .../wizard-api.scss => admin/wizard/api.scss} | 0 .../wizard/manager.scss} | 0 .../wizard/mapper.scss} | 0 .../stylesheets/admin/wizard/variables.scss | 9 + .../stylesheets/common/wizard-variables.scss | 7 - config/locales/client.en.yml | 40 ++- config/locales/server.en.yml | 13 +- config/routes.rb | 13 +- controllers/custom_wizard/admin/admin.rb | 6 + .../custom_wizard/admin/custom_fields.rb | 2 +- controllers/custom_wizard/admin/notice.rb | 22 ++ controllers/custom_wizard/admin/pro.rb | 48 ---- .../custom_wizard/admin/subscription.rb | 48 ++++ controllers/custom_wizard/admin/wizard.rb | 2 +- controllers/custom_wizard/wizard.rb | 6 +- coverage/.last_run.json | 2 +- .../scheduled/custom_wizard/update_notices.rb | 9 + .../custom_wizard/update_subscription.rb | 9 + jobs/scheduled/update_pro_subscription.rb | 9 - lib/custom_wizard/action.rb | 4 - lib/custom_wizard/custom_field.rb | 14 +- lib/custom_wizard/mapper.rb | 4 +- lib/custom_wizard/notice.rb | 234 ++++++++++++++++++ lib/custom_wizard/{pro.rb => subscription.rb} | 26 +- .../{pro => subscription}/authentication.rb | 12 +- .../{pro => subscription}/subscription.rb | 2 +- lib/custom_wizard/validators/template.rb | 30 +-- plugin.rb | 31 ++- .../custom_wizard/notice_serializer.rb | 20 ++ serializers/custom_wizard/pro_serializer.rb | 6 - .../authentication_serializer.rb | 2 +- .../subscription_serializer.rb | 2 +- .../custom_wizard/subscription_serializer.rb | 6 + .../custom_wizard/wizard_field_serializer.rb | 2 +- .../custom_wizard/wizard_serializer.rb | 6 +- .../custom_wizard/wizard_step_serializer.rb | 4 - spec/components/custom_wizard/action_spec.rb | 2 +- .../custom_wizard/custom_field_spec.rb | 32 +-- spec/components/custom_wizard/mapper_spec.rb | 4 +- spec/components/custom_wizard/notice_spec.rb | 80 ++++++ .../{pro_spec.rb => subscription_spec.rb} | 70 +++--- .../custom_field_extensions_spec.rb | 6 +- ...s.json => subscription_custom_fields.json} | 0 spec/jobs/update_notices_spec.rb | 29 +++ ...on_spec.rb => update_subscription_spec.rb} | 6 +- spec/plugin_helper.rb | 16 +- .../admin/notice_controller_spec.rb | 31 +++ ...pec.rb => subscription_controller_spec.rb} | 36 +-- .../custom_field_extensions_spec.rb | 6 +- .../custom_wizard/steps_controller_spec.rb | 34 ++- .../custom_wizard/notice_serializer_spec.rb | 22 ++ .../custom_wizard/pro_serializer_spec.rb | 14 -- .../authentication_serializer_spec.rb | 6 +- .../subscription_serializer_spec.rb | 6 +- .../subscription_serializer_spec.rb | 14 ++ 94 files changed, 1228 insertions(+), 413 deletions(-) create mode 100644 assets/javascripts/discourse/components/subscription-container.js.es6 create mode 100644 assets/javascripts/discourse/components/wizard-notice.js.es6 rename assets/javascripts/discourse/components/{wizard-pro-selector.js.es6 => wizard-subscription-selector.js.es6} (58%) rename assets/javascripts/discourse/components/{wizard-pro-selector/wizard-pro-selector-header.js.es6 => wizard-subscription-selector/wizard-subscription-selector-header.js.es6} (88%) rename assets/javascripts/discourse/components/{wizard-pro-selector/wizard-pro-selector-row.js.es6 => wizard-subscription-selector/wizard-subscription-selector-row.js.es6} (100%) rename assets/javascripts/discourse/components/{wizard-pro-subscription.js.es6 => wizard-subscription.js.es6} (75%) create mode 100644 assets/javascripts/discourse/connectors/admin-dashboard-top/custom-wizard-issue-notice.hbs create mode 100644 assets/javascripts/discourse/connectors/admin-dashboard-top/custom-wizard-issue-notice.js.es6 rename assets/javascripts/discourse/controllers/{admin-wizards-pro.js.es6 => admin-wizards-subscription.js.es6} (95%) create mode 100644 assets/javascripts/discourse/controllers/admin-wizards.js.es6 create mode 100644 assets/javascripts/discourse/models/custom-wizard-notice.js.es6 rename assets/javascripts/discourse/models/{custom-wizard-pro.js.es6 => custom-wizard-subscription.js.es6} (68%) rename assets/javascripts/discourse/routes/{admin-wizards-pro.js.es6 => admin-wizards-subscription.js.es6} (61%) delete mode 100644 assets/javascripts/discourse/templates/admin-wizards-pro.hbs create mode 100644 assets/javascripts/discourse/templates/admin-wizards-subscription.hbs create mode 100644 assets/javascripts/discourse/templates/components/subscription-container.hbs create mode 100644 assets/javascripts/discourse/templates/components/wizard-notice.hbs rename assets/javascripts/discourse/templates/components/{wizard-pro-selector/wizard-pro-selector-header.hbs => wizard-subscription-selector/wizard-subscription-selector-header.hbs} (70%) rename assets/javascripts/discourse/templates/components/{wizard-pro-selector/wizard-pro-selector-row.hbs => wizard-subscription-selector/wizard-subscription-selector-row.hbs} (71%) rename assets/javascripts/discourse/templates/components/{wizard-pro-subscription.hbs => wizard-subscription.hbs} (72%) rename assets/stylesheets/{common/wizard-admin.scss => admin/admin.scss} (86%) rename assets/stylesheets/{common/wizard-api.scss => admin/wizard/api.scss} (100%) rename assets/stylesheets/{common/wizard-manager.scss => admin/wizard/manager.scss} (100%) rename assets/stylesheets/{common/wizard-mapper.scss => admin/wizard/mapper.scss} (100%) create mode 100644 assets/stylesheets/admin/wizard/variables.scss delete mode 100644 assets/stylesheets/common/wizard-variables.scss create mode 100644 controllers/custom_wizard/admin/notice.rb delete mode 100644 controllers/custom_wizard/admin/pro.rb create mode 100644 controllers/custom_wizard/admin/subscription.rb create mode 100644 jobs/scheduled/custom_wizard/update_notices.rb create mode 100644 jobs/scheduled/custom_wizard/update_subscription.rb delete mode 100644 jobs/scheduled/update_pro_subscription.rb create mode 100644 lib/custom_wizard/notice.rb rename lib/custom_wizard/{pro.rb => subscription.rb} (81%) rename lib/custom_wizard/{pro => subscription}/authentication.rb (77%) rename lib/custom_wizard/{pro => subscription}/subscription.rb (89%) create mode 100644 serializers/custom_wizard/notice_serializer.rb delete mode 100644 serializers/custom_wizard/pro_serializer.rb rename serializers/custom_wizard/{pro => subscription}/authentication_serializer.rb (66%) rename serializers/custom_wizard/{pro => subscription}/subscription_serializer.rb (63%) create mode 100644 serializers/custom_wizard/subscription_serializer.rb create mode 100644 spec/components/custom_wizard/notice_spec.rb rename spec/components/custom_wizard/{pro_spec.rb => subscription_spec.rb} (54%) rename spec/fixtures/custom_field/{pro_custom_fields.json => subscription_custom_fields.json} (100%) create mode 100644 spec/jobs/update_notices_spec.rb rename spec/jobs/{update_pro_subscription_spec.rb => update_subscription_spec.rb} (52%) create mode 100644 spec/requests/custom_wizard/admin/notice_controller_spec.rb rename spec/requests/custom_wizard/admin/{pro_controller_spec.rb => subscription_controller_spec.rb} (53%) create mode 100644 spec/serializers/custom_wizard/notice_serializer_spec.rb delete mode 100644 spec/serializers/custom_wizard/pro_serializer_spec.rb rename spec/serializers/custom_wizard/{pro => subscription}/authentication_serializer_spec.rb (60%) rename spec/serializers/custom_wizard/{pro => subscription}/subscription_serializer_spec.rb (57%) create mode 100644 spec/serializers/custom_wizard/subscription_serializer_spec.rb diff --git a/assets/javascripts/discourse/components/custom-field-input.js.es6 b/assets/javascripts/discourse/components/custom-field-input.js.es6 index 877b83fb..a673112b 100644 --- a/assets/javascripts/discourse/components/custom-field-input.js.es6 +++ b/assets/javascripts/discourse/components/custom-field-input.js.es6 @@ -6,20 +6,20 @@ import I18n from "I18n"; const klasses = ["topic", "post", "group", "category"]; const types = ["string", "boolean", "integer", "json"]; -const proTypes = { +const subscriptionTypes = { klass: ["group", "category"], type: ["json"], }; -const generateContent = function (array, type, proSubscribed = false) { +const generateContent = function (array, type, subscribed = false) { return array.reduce((result, key) => { - let proArr = proTypes[type]; - let pro = proArr && proArr.includes(key); - if (!pro || proSubscribed) { + let subArr = subscriptionTypes[type]; + let subscription = subArr && subArr.includes(key); + if (!subscription || subscribed) { result.push({ id: key, name: I18n.t(`admin.wizard.custom_field.${type}.${key}`), - pro, + subscription, }); } return result; @@ -32,11 +32,11 @@ export default Component.extend({ postSerializers: ["post"], groupSerializers: ["basic_group"], categorySerializers: ["basic_category"], - klassContent: computed("proSubscribed", function () { - return generateContent(klasses, "klass", this.proSubscribed); + klassContent: computed("subscribed", function () { + return generateContent(klasses, "klass", this.subscribed); }), - typeContent: computed("proSubscribed", function () { - return generateContent(types, "type", this.proSubscribed); + typeContent: computed("subscribed", function () { + return generateContent(types, "type", this.subscribed); }), showInputs: or("field.new", "field.edit"), classNames: ["custom-field-input"], @@ -54,7 +54,7 @@ export default Component.extend({ const serializers = this.get(`${klass}Serializers`); if (serializers) { - return generateContent(serializers, "serializers", this.proSubscribed); + return generateContent(serializers, "serializers", this.subscribed); } else { return []; } diff --git a/assets/javascripts/discourse/components/subscription-container.js.es6 b/assets/javascripts/discourse/components/subscription-container.js.es6 new file mode 100644 index 00000000..08498f6f --- /dev/null +++ b/assets/javascripts/discourse/components/subscription-container.js.es6 @@ -0,0 +1,21 @@ +import Component from "@ember/component"; +import discourseComputed from "discourse-common/utils/decorators"; + +export default Component.extend({ + classNameBindings: [":subscription-container", "subscribed"], + + @discourseComputed('subscribed') + subscribedIcon(subscribed) { + return subscribed ? 'check' : 'dash'; + }, + + @discourseComputed('subscribed') + subscribedLabel(subscribed) { + return `admin.wizard.subscription_container.${subscribed ? 'subscribed' : 'not_subscribed'}.label`; + }, + + @discourseComputed('subscribed') + subscribedTitle(subscribed) { + return `admin.wizard.subscription_container.${subscribed ? 'subscribed' : 'not_subscribed'}.title`; + } +}) \ No newline at end of file diff --git a/assets/javascripts/discourse/components/wizard-custom-action.js.es6 b/assets/javascripts/discourse/components/wizard-custom-action.js.es6 index c50be2ba..ffe5b514 100644 --- a/assets/javascripts/discourse/components/wizard-custom-action.js.es6 +++ b/assets/javascripts/discourse/components/wizard-custom-action.js.es6 @@ -94,15 +94,15 @@ export default Component.extend(UndoChanges, { return apis.find((a) => a.name === api).endpoints; }, - @discourseComputed("proSubscribed") - actionTypes(proSubscribed) { + @discourseComputed("subscribed") + actionTypes(subscribed) { return Object.keys(wizardSchema.action.types).reduce((result, type) => { - let pro = wizardSchema.action.proTypes.includes(type); - if (proSubscribed || !pro) { + let subscription = wizardSchema.action.subscriptionTypes.includes(type); + if (subscribed || !subscription) { result.push({ id: type, name: I18n.t(`admin.wizard.action.${type}.label`), - pro, + subscription, }); } return result; diff --git a/assets/javascripts/discourse/components/wizard-notice.js.es6 b/assets/javascripts/discourse/components/wizard-notice.js.es6 new file mode 100644 index 00000000..86a82c94 --- /dev/null +++ b/assets/javascripts/discourse/components/wizard-notice.js.es6 @@ -0,0 +1,34 @@ +import Component from "@ember/component"; +import discourseComputed from "discourse-common/utils/decorators"; +import { notEmpty, not } from "@ember/object/computed"; +import I18n from "I18n"; + +export default Component.extend({ + classNameBindings: [':wizard-notice', 'notice.type', 'dismissed', 'expired'], + showFull: false, + resolved: notEmpty('notice.expired_at'), + dismissed: notEmpty('notice.dismissed_at'), + canDismiss: not('dismissed'), + + @discourseComputed('notice.type') + title(type) { + return I18n.t(`admin.wizard.notice.title.${type}`); + }, + + @discourseComputed('notice.type') + icon(type) { + return { + warning: 'exclamation-circle', + info: 'info-circle' + }[type]; + }, + + actions: { + dismiss() { + this.set('dismissing', true) + this.notice.dismiss().then(() => { + this.set('dismissing', false); + }); + } + } +}); \ No newline at end of file diff --git a/assets/javascripts/discourse/components/wizard-realtime-validations.js.es6 b/assets/javascripts/discourse/components/wizard-realtime-validations.js.es6 index 04123e3d..b1d8a0f5 100644 --- a/assets/javascripts/discourse/components/wizard-realtime-validations.js.es6 +++ b/assets/javascripts/discourse/components/wizard-realtime-validations.js.es6 @@ -6,7 +6,7 @@ import discourseComputed from "discourse-common/utils/decorators"; import I18n from "I18n"; export default Component.extend({ - classNames: ["realtime-validations", "setting", "full", "pro"], + classNames: ["realtime-validations", "setting", "full", "subscription"], @discourseComputed timeUnits() { diff --git a/assets/javascripts/discourse/components/wizard-pro-selector.js.es6 b/assets/javascripts/discourse/components/wizard-subscription-selector.js.es6 similarity index 58% rename from assets/javascripts/discourse/components/wizard-pro-selector.js.es6 rename to assets/javascripts/discourse/components/wizard-subscription-selector.js.es6 index 8e472782..ec94bce5 100644 --- a/assets/javascripts/discourse/components/wizard-pro-selector.js.es6 +++ b/assets/javascripts/discourse/components/wizard-subscription-selector.js.es6 @@ -1,18 +1,18 @@ import SingleSelectComponent from "select-kit/components/single-select"; export default SingleSelectComponent.extend({ - classNames: ["combo-box", "wizard-pro-selector"], + classNames: ["combo-box", "wizard-subscription-selector"], selectKitOptions: { autoFilterable: false, filterable: false, showFullTitle: true, - headerComponent: "wizard-pro-selector/wizard-pro-selector-header", + headerComponent: "wizard-subscription-selector/wizard-subscription-selector-header", caretUpIcon: "caret-up", caretDownIcon: "caret-down", }, modifyComponentForRow() { - return "wizard-pro-selector/wizard-pro-selector-row"; + return "wizard-subscription-selector/wizard-subscription-selector-row"; }, }); diff --git a/assets/javascripts/discourse/components/wizard-pro-selector/wizard-pro-selector-header.js.es6 b/assets/javascripts/discourse/components/wizard-subscription-selector/wizard-subscription-selector-header.js.es6 similarity index 88% rename from assets/javascripts/discourse/components/wizard-pro-selector/wizard-pro-selector-header.js.es6 rename to assets/javascripts/discourse/components/wizard-subscription-selector/wizard-subscription-selector-header.js.es6 index c1f7251c..74f29f08 100644 --- a/assets/javascripts/discourse/components/wizard-pro-selector/wizard-pro-selector-header.js.es6 +++ b/assets/javascripts/discourse/components/wizard-subscription-selector/wizard-subscription-selector-header.js.es6 @@ -3,7 +3,7 @@ import { computed } from "@ember/object"; import { reads } from "@ember/object/computed"; export default SingleSelectHeaderComponent.extend({ - classNames: ["combo-box-header", "wizard-pro-selector-header"], + classNames: ["combo-box-header", "wizard-subscription-selector-header"], caretUpIcon: reads("selectKit.options.caretUpIcon"), caretDownIcon: reads("selectKit.options.caretDownIcon"), caretIcon: computed( diff --git a/assets/javascripts/discourse/components/wizard-pro-selector/wizard-pro-selector-row.js.es6 b/assets/javascripts/discourse/components/wizard-subscription-selector/wizard-subscription-selector-row.js.es6 similarity index 100% rename from assets/javascripts/discourse/components/wizard-pro-selector/wizard-pro-selector-row.js.es6 rename to assets/javascripts/discourse/components/wizard-subscription-selector/wizard-subscription-selector-row.js.es6 diff --git a/assets/javascripts/discourse/components/wizard-pro-subscription.js.es6 b/assets/javascripts/discourse/components/wizard-subscription.js.es6 similarity index 75% rename from assets/javascripts/discourse/components/wizard-pro-subscription.js.es6 rename to assets/javascripts/discourse/components/wizard-subscription.js.es6 index 7824cb83..0d839a5f 100644 --- a/assets/javascripts/discourse/components/wizard-pro-subscription.js.es6 +++ b/assets/javascripts/discourse/components/wizard-subscription.js.es6 @@ -1,12 +1,12 @@ import Component from "@ember/component"; -import CustomWizardPro from "../models/custom-wizard-pro"; +import CustomWizardSubscription from "../models/custom-wizard-subscription"; import { notEmpty } from "@ember/object/computed"; import discourseComputed from "discourse-common/utils/decorators"; import I18n from "I18n"; export default Component.extend({ classNameBindings: [ - ":custom-wizard-pro-subscription", + ":custom-wizard-subscription", "subscription.active:active:inactive", ], subscribed: notEmpty("subscription"), @@ -14,8 +14,8 @@ export default Component.extend({ @discourseComputed("subscription.type") title(type) { return type - ? I18n.t(`admin.wizard.pro.subscription.title.${type}`) - : I18n.t("admin.wizard.pro.not_subscribed"); + ? I18n.t(`admin.wizard.subscription.subscription.title.${type}`) + : I18n.t("admin.wizard.subscription.not_subscribed"); }, @discourseComputed("subscription.active") @@ -25,13 +25,13 @@ export default Component.extend({ @discourseComputed("stateClass") stateLabel(stateClass) { - return I18n.t(`admin.wizard.pro.subscription.status.${stateClass}`); + return I18n.t(`admin.wizard.subscription.subscription.status.${stateClass}`); }, actions: { update() { this.set("updating", true); - CustomWizardPro.update_subscription() + CustomWizardSubscription.update() .then((result) => { if (result.success) { this.setProperties({ diff --git a/assets/javascripts/discourse/connectors/admin-dashboard-top/custom-wizard-issue-notice.hbs b/assets/javascripts/discourse/connectors/admin-dashboard-top/custom-wizard-issue-notice.hbs new file mode 100644 index 00000000..a8aad815 --- /dev/null +++ b/assets/javascripts/discourse/connectors/admin-dashboard-top/custom-wizard-issue-notice.hbs @@ -0,0 +1,3 @@ +{{#if wizardWarningNotice}} + {{wizard-notice notice=wizardWarningNotice}} +{{/if}} \ No newline at end of file diff --git a/assets/javascripts/discourse/connectors/admin-dashboard-top/custom-wizard-issue-notice.js.es6 b/assets/javascripts/discourse/connectors/admin-dashboard-top/custom-wizard-issue-notice.js.es6 new file mode 100644 index 00000000..b92e7897 --- /dev/null +++ b/assets/javascripts/discourse/connectors/admin-dashboard-top/custom-wizard-issue-notice.js.es6 @@ -0,0 +1,12 @@ +import { getOwner } from "discourse-common/lib/get-owner"; + +export default { + setupComponent() { + const controller = getOwner(this).lookup('controller:admin-dashboard') + const wizardWarningNotice = controller.get('wizardWarningNotice'); + + if (wizardWarningNotice) { + this.set('wizardWarningNotice', wizardWarningNotice); + } + } +} \ No newline at end of file diff --git a/assets/javascripts/discourse/connectors/admin-menu/wizards-nav-button.hbs b/assets/javascripts/discourse/connectors/admin-menu/wizards-nav-button.hbs index f76722fc..f893d4ac 100644 --- a/assets/javascripts/discourse/connectors/admin-menu/wizards-nav-button.hbs +++ b/assets/javascripts/discourse/connectors/admin-menu/wizards-nav-button.hbs @@ -1,3 +1,7 @@ {{#if currentUser.admin}} {{nav-item route="adminWizards" label="admin.wizard.nav_label"}} + + {{#if wizardErrorNotice}} + {{d-icon "exclaimation-circle"}} + {{/if}} {{/if}} diff --git a/assets/javascripts/discourse/controllers/admin-wizards-pro.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards-subscription.js.es6 similarity index 95% rename from assets/javascripts/discourse/controllers/admin-wizards-pro.js.es6 rename to assets/javascripts/discourse/controllers/admin-wizards-subscription.js.es6 index 61012d8f..844d5a25 100644 --- a/assets/javascripts/discourse/controllers/admin-wizards-pro.js.es6 +++ b/assets/javascripts/discourse/controllers/admin-wizards-subscription.js.es6 @@ -1,6 +1,6 @@ import Controller from "@ember/controller"; import discourseComputed from "discourse-common/utils/decorators"; -import CustomWizardPro from "../models/custom-wizard-pro"; +import CustomWizardSubscription from "../models/custom-wizard-subscription"; import { alias } from "@ember/object/computed"; export default Controller.extend({ diff --git a/assets/javascripts/discourse/controllers/admin-wizards.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards.js.es6 new file mode 100644 index 00000000..a94222c1 --- /dev/null +++ b/assets/javascripts/discourse/controllers/admin-wizards.js.es6 @@ -0,0 +1,20 @@ +import Controller from "@ember/controller"; +import { popupAjaxError } from "discourse/lib/ajax-error"; +import { ajax } from "discourse/lib/ajax"; + +export default Controller.extend({ + actions: { + dismissNotice(noticeId) { + ajax(`/admin/wizards/notice/${this.id}`, { + type: "DELETE", + }) + .then(result => { + if (result.success) { + const notices = this.notices; + notices.removeObject(notices.findBy('id', noticeId)); + } + }) + .catch(popupAjaxError); + } + } +}); \ No newline at end of file diff --git a/assets/javascripts/discourse/custom-wizard-admin-route-map.js.es6 b/assets/javascripts/discourse/custom-wizard-admin-route-map.js.es6 index 1ca6d41b..67b91f87 100644 --- a/assets/javascripts/discourse/custom-wizard-admin-route-map.js.es6 +++ b/assets/javascripts/discourse/custom-wizard-admin-route-map.js.es6 @@ -59,8 +59,8 @@ export default { resetNamespace: true, }); - this.route("adminWizardsPro", { - path: "/pro", + this.route("adminWizardsSubscription", { + path: "/subscription", resetNamespace: true, }); } diff --git a/assets/javascripts/discourse/initializers/custom-wizard-edits.js.es6 b/assets/javascripts/discourse/initializers/custom-wizard-edits.js.es6 index 63ddd5e8..4bf50fa9 100644 --- a/assets/javascripts/discourse/initializers/custom-wizard-edits.js.es6 +++ b/assets/javascripts/discourse/initializers/custom-wizard-edits.js.es6 @@ -1,4 +1,8 @@ import DiscourseURL from "discourse/lib/url"; +import { withPluginApi } from "discourse/lib/plugin-api"; +import { ajax } from "discourse/lib/ajax"; +import CustomWizardNotice from "../models/custom-wizard-notice"; +import { A } from "@ember/array"; export default { name: "custom-wizard-edits", @@ -16,5 +20,29 @@ export default { } return existing.apply(this, [path, opts]); }; + + withPluginApi("0.8.36", (api) => { + api.modifyClass('route:admin-dashboard', { + afterModel() { + return CustomWizardNotice.list().then(result => { + if (result && result.length) { + this.set('notices', A(result.map(n => CustomWizardNotice.create(n)))); + } + }); + }, + + setupController(controller, model) { + if (this.notices) { + let warningNotices = this.notices.filter(n => n.type === 'warning'); + + if (warningNotices.length) { + controller.set('wizardWarningNotice', warningNotices[0]); + } + } + + this._super(...arguments); + } + }); + }); }, }; diff --git a/assets/javascripts/discourse/lib/wizard-schema.js.es6 b/assets/javascripts/discourse/lib/wizard-schema.js.es6 index a20b61a7..5d876ed0 100644 --- a/assets/javascripts/discourse/lib/wizard-schema.js.es6 +++ b/assets/javascripts/discourse/lib/wizard-schema.js.es6 @@ -193,6 +193,14 @@ const action = { "members_visibility_level", ], required: ["id", "type"], + subscriptionTypes: [ + "send_message", + "add_to_group", + "create_category", + "create_group", + "send_to_api", + ], + required: ["id", "type"], proTypes: [ "send_message", "add_to_group", diff --git a/assets/javascripts/discourse/models/custom-wizard-notice.js.es6 b/assets/javascripts/discourse/models/custom-wizard-notice.js.es6 new file mode 100644 index 00000000..bae81822 --- /dev/null +++ b/assets/javascripts/discourse/models/custom-wizard-notice.js.es6 @@ -0,0 +1,23 @@ +import EmberObject from "@ember/object"; +import { ajax } from "discourse/lib/ajax"; +import { popupAjaxError } from "discourse/lib/ajax-error"; + +const CustomWizardNotice = EmberObject.extend(); + +CustomWizardNotice.reopen({ + dismiss() { + return ajax(`/admin/wizards/notice/${this.id}`, { type: 'PUT' }).then(result => { + if (result.success) { + this.set('dismissed_at', result.dismissed_at); + } + }).catch(popupAjaxError) + } +}); + +CustomWizardNotice.reopenClass({ + list() { + return ajax('/admin/wizards/notice').catch(popupAjaxError) + } +}); + +export default CustomWizardNotice; \ No newline at end of file diff --git a/assets/javascripts/discourse/models/custom-wizard-pro.js.es6 b/assets/javascripts/discourse/models/custom-wizard-subscription.js.es6 similarity index 68% rename from assets/javascripts/discourse/models/custom-wizard-pro.js.es6 rename to assets/javascripts/discourse/models/custom-wizard-subscription.js.es6 index 76429726..469460d5 100644 --- a/assets/javascripts/discourse/models/custom-wizard-pro.js.es6 +++ b/assets/javascripts/discourse/models/custom-wizard-subscription.js.es6 @@ -2,11 +2,11 @@ import { ajax } from "discourse/lib/ajax"; import { popupAjaxError } from "discourse/lib/ajax-error"; import EmberObject from "@ember/object"; -const CustomWizardPro = EmberObject.extend(); +const CustomWizardSubscription = EmberObject.extend(); -const basePath = "/admin/wizards/pro"; +const basePath = "/admin/wizards/subscription"; -CustomWizardPro.reopenClass({ +CustomWizardSubscription.reopenClass({ status() { return ajax(basePath, { type: "GET", @@ -23,11 +23,11 @@ CustomWizardPro.reopenClass({ }).catch(popupAjaxError); }, - update_subscription() { - return ajax(`${basePath}/subscription`, { + update() { + return ajax(basePath, { type: "POST", }).catch(popupAjaxError); }, }); -export default CustomWizardPro; +export default CustomWizardSubscription; diff --git a/assets/javascripts/discourse/routes/admin-wizards-custom-fields.js.es6 b/assets/javascripts/discourse/routes/admin-wizards-custom-fields.js.es6 index 45ca9ae7..ca9c4c40 100644 --- a/assets/javascripts/discourse/routes/admin-wizards-custom-fields.js.es6 +++ b/assets/javascripts/discourse/routes/admin-wizards-custom-fields.js.es6 @@ -9,11 +9,11 @@ export default DiscourseRoute.extend({ setupController(controller, model) { const customFields = A(model.custom_fields || []); - const proSubscribed = model.pro_subscribed; + const subscribed = model.subscribed; controller.setProperties({ customFields, - proSubscribed, + subscribed, }); }, }); diff --git a/assets/javascripts/discourse/routes/admin-wizards-pro.js.es6 b/assets/javascripts/discourse/routes/admin-wizards-subscription.js.es6 similarity index 61% rename from assets/javascripts/discourse/routes/admin-wizards-pro.js.es6 rename to assets/javascripts/discourse/routes/admin-wizards-subscription.js.es6 index d5c7fbd7..4824425a 100644 --- a/assets/javascripts/discourse/routes/admin-wizards-pro.js.es6 +++ b/assets/javascripts/discourse/routes/admin-wizards-subscription.js.es6 @@ -1,9 +1,9 @@ -import CustomWizardPro from "../models/custom-wizard-pro"; +import CustomWizardSubscription from "../models/custom-wizard-subscription"; import DiscourseRoute from "discourse/routes/discourse"; export default DiscourseRoute.extend({ model() { - return CustomWizardPro.status(); + return CustomWizardSubscription.status(); }, setupController(controller, model) { @@ -13,7 +13,7 @@ export default DiscourseRoute.extend({ actions: { authorize() { - CustomWizardPro.authorize(); + CustomWizardSubscription.authorize(); }, }, }); diff --git a/assets/javascripts/discourse/routes/admin-wizards-wizard-show.js.es6 b/assets/javascripts/discourse/routes/admin-wizards-wizard-show.js.es6 index 7032b974..046fc6d4 100644 --- a/assets/javascripts/discourse/routes/admin-wizards-wizard-show.js.es6 +++ b/assets/javascripts/discourse/routes/admin-wizards-wizard-show.js.es6 @@ -39,7 +39,7 @@ export default DiscourseRoute.extend({ currentStep: wizard.steps[0], currentAction: wizard.actions[0], creating: model.create, - proSubscribed: parentModel.pro_subscribed, + subscribed: parentModel.subscribed, }; controller.setProperties(props); diff --git a/assets/javascripts/discourse/routes/admin-wizards.js.es6 b/assets/javascripts/discourse/routes/admin-wizards.js.es6 index 5de271a8..da184d93 100644 --- a/assets/javascripts/discourse/routes/admin-wizards.js.es6 +++ b/assets/javascripts/discourse/routes/admin-wizards.js.es6 @@ -1,9 +1,19 @@ import DiscourseRoute from "discourse/routes/discourse"; +import { ajax } from "discourse/lib/ajax"; +import { A } from "@ember/array"; export default DiscourseRoute.extend({ - beforeModel(transition) { + model() { + return ajax('/admin/wizards'); + }, + + setupController(controller, model) { + controller.set('notices', A(model.notices)); + }, + + afterModel(model, transition) { if (transition.targetName === "adminWizards.index") { this.transitionTo("adminWizardsWizard"); } - }, + } }); diff --git a/assets/javascripts/discourse/templates/admin-wizards-custom-fields.hbs b/assets/javascripts/discourse/templates/admin-wizards-custom-fields.hbs index 09a0d569..361d2d44 100644 --- a/assets/javascripts/discourse/templates/admin-wizards-custom-fields.hbs +++ b/assets/javascripts/discourse/templates/admin-wizards-custom-fields.hbs @@ -33,7 +33,7 @@ field=field removeField=(action "removeField") saveField=(action "saveField") - proSubscribed=proSubscribed}} + subscribed=subscribed}} {{/each}}
- {{submission-field fieldName=field value=value}} - {{wizard-table-field field=field value=value}}
diff --git a/assets/javascripts/discourse/templates/admin-wizards-pro.hbs b/assets/javascripts/discourse/templates/admin-wizards-pro.hbs deleted file mode 100644 index b4f0691b..00000000 --- a/assets/javascripts/discourse/templates/admin-wizards-pro.hbs +++ /dev/null @@ -1,34 +0,0 @@ -
-

{{i18n "admin.wizard.pro.title"}}

- -
- {{#if model.authentication.active}} - {{conditional-loading-spinner size="small" condition=unauthorizing}} - - {{i18n "admin.wizard.pro.unauthorize"}} - - - {{else}} - {{d-button - icon="id-card" - label="admin.wizard.pro.authorize" - title="admin.wizard.pro.authorize" - action=(route-action "authorize")}} - {{/if}} -
-
- -{{wizard-message - key=messageKey - url=messageUrl - type=messageType - opts=messageOpts - component="pro"}} - -
- {{#if showSubscription}} - {{wizard-pro-subscription subscription=model.subscription}} - {{/if}} -
diff --git a/assets/javascripts/discourse/templates/admin-wizards-subscription.hbs b/assets/javascripts/discourse/templates/admin-wizards-subscription.hbs new file mode 100644 index 00000000..c75f3e1b --- /dev/null +++ b/assets/javascripts/discourse/templates/admin-wizards-subscription.hbs @@ -0,0 +1,35 @@ +
+

{{i18n "admin.wizard.subscription.title"}}

+ +
+ {{#if model.authentication.active}} + {{conditional-loading-spinner size="small" condition=unauthorizing}} + + {{i18n "admin.wizard.subscription.unauthorize"}} + + + {{else}} + {{d-button + icon="id-card" + class="btn-primary" + label="admin.wizard.subscription.authorize" + title="admin.wizard.subscription.authorize" + action=(route-action "authorize")}} + {{/if}} +
+
+ +{{wizard-message + key=messageKey + url=messageUrl + type=messageType + opts=messageOpts + component="subscription"}} + +
+ {{#if showSubscription}} + {{wizard-subscription subscription=model.subscription}} + {{/if}} +
diff --git a/assets/javascripts/discourse/templates/admin-wizards-wizard-show.hbs b/assets/javascripts/discourse/templates/admin-wizards-wizard-show.hbs index 472cbfcb..3966a50c 100644 --- a/assets/javascripts/discourse/templates/admin-wizards-wizard-show.hbs +++ b/assets/javascripts/discourse/templates/admin-wizards-wizard-show.hbs @@ -126,11 +126,10 @@
- {{#if proSubscribed}} -
+ {{#subscription-container subscribed=subscribed}} +
- {{i18n "admin.wizard.pro.label"}}
{{input type="checkbox" checked=wizard.save_submissions}} @@ -138,17 +137,16 @@
-
+
- {{i18n "admin.wizard.pro.label"}}
{{input type="checkbox" checked=wizard.restart_on_revisit}} {{i18n "admin.wizard.restart_on_revisit_label"}}
- {{/if}} + {{/subscription-container}}
{{wizard-links @@ -163,7 +161,7 @@ currentField=currentField wizardFields=wizardFields fieldTypes=fieldTypes - proSubscribed=proSubscribed}} + subscribed=subscribed}} {{/if}} {{wizard-links @@ -180,7 +178,7 @@ apis=apis removeAction="removeAction" wizardFields=wizardFields - proSubscribed=proSubscribed}} + subscribed=subscribed}} {{/each}}
diff --git a/assets/javascripts/discourse/templates/admin-wizards.hbs b/assets/javascripts/discourse/templates/admin-wizards.hbs index 0578e01f..5ab666c7 100644 --- a/assets/javascripts/discourse/templates/admin-wizards.hbs +++ b/assets/javascripts/discourse/templates/admin-wizards.hbs @@ -7,13 +7,18 @@ {{/if}} {{nav-item route="adminWizardsLogs" label="admin.wizard.log.nav_label"}} {{nav-item route="adminWizardsManager" label="admin.wizard.manager.nav_label"}} - {{nav-item route="adminWizardsPro" label="admin.wizard.pro.nav_label"}} + {{nav-item route="adminWizardsSubscription" label="admin.wizard.subscription.nav_label"}} {{/admin-nav}}
+ {{#each notices as |notice|}} + {{wizard-notice notice=notice}} + {{/each}} {{outlet}}
diff --git a/assets/javascripts/discourse/templates/components/custom-field-input.hbs b/assets/javascripts/discourse/templates/components/custom-field-input.hbs index 2bb9acce..ac7e8689 100644 --- a/assets/javascripts/discourse/templates/components/custom-field-input.hbs +++ b/assets/javascripts/discourse/templates/components/custom-field-input.hbs @@ -1,13 +1,13 @@ {{#if showInputs}} - {{wizard-pro-selector + {{wizard-subscription-selector value=field.klass content=klassContent none="admin.wizard.custom_field.klass.select" onChange=(action (mut field.klass))}} - {{wizard-pro-selector + {{wizard-subscription-selector value=field.type content=typeContent none="admin.wizard.custom_field.type.select" diff --git a/assets/javascripts/discourse/templates/components/subscription-container.hbs b/assets/javascripts/discourse/templates/components/subscription-container.hbs new file mode 100644 index 00000000..7a02f555 --- /dev/null +++ b/assets/javascripts/discourse/templates/components/subscription-container.hbs @@ -0,0 +1,12 @@ +
+

{{i18n 'admin.wizard.subscription_container.title'}}

+ + + {{d-icon subscribedIcon}} + {{i18n subscribedLabel}} + +
+ +
+ {{yield}} +
\ No newline at end of file diff --git a/assets/javascripts/discourse/templates/components/wizard-custom-action.hbs b/assets/javascripts/discourse/templates/components/wizard-custom-action.hbs index f5d53200..cb4cf28d 100644 --- a/assets/javascripts/discourse/templates/components/wizard-custom-action.hbs +++ b/assets/javascripts/discourse/templates/components/wizard-custom-action.hbs @@ -12,7 +12,7 @@
- {{wizard-pro-selector + {{wizard-subscription-selector value=action.type content=actionTypes onChange=(action "changeType") diff --git a/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs b/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs index fe3089ac..e271d0af 100644 --- a/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs +++ b/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs @@ -207,11 +207,10 @@
{{/if}} -{{#if proSubscribed}} -
+{{#subscription-container subscribed=subscribed}} +
- {{i18n "admin.wizard.pro.label"}}
@@ -221,10 +220,9 @@
-
+
- - {{i18n "admin.wizard.pro.label"}} + >
@@ -256,4 +254,4 @@ {{#if validations}} {{wizard-realtime-validations field=field validations=validations}} {{/if}} -{{/if}} +{{/subscription-container}} diff --git a/assets/javascripts/discourse/templates/components/wizard-custom-step.hbs b/assets/javascripts/discourse/templates/components/wizard-custom-step.hbs index 3feb6731..320b5e47 100644 --- a/assets/javascripts/discourse/templates/components/wizard-custom-step.hbs +++ b/assets/javascripts/discourse/templates/components/wizard-custom-step.hbs @@ -33,11 +33,10 @@
-{{#if proSubscribed}} -
+{{#subscription-container subscribed=subscribed}} +
- {{i18n "admin.wizard.pro.label"}}
@@ -83,10 +82,9 @@
-
+
- {{i18n "admin.wizard.pro.label"}}
{{wizard-mapper @@ -100,7 +98,7 @@ )}}
-{{/if}} +{{/subscription-container}} {{wizard-links itemType="field" @@ -116,5 +114,5 @@ fieldTypes=fieldTypes removeField="removeField" wizardFields=wizardFields - proSubscribed=proSubscribed}} + subscribed=subscribed}} {{/each}} diff --git a/assets/javascripts/discourse/templates/components/wizard-notice.hbs b/assets/javascripts/discourse/templates/components/wizard-notice.hbs new file mode 100644 index 00000000..f6f923a3 --- /dev/null +++ b/assets/javascripts/discourse/templates/components/wizard-notice.hbs @@ -0,0 +1,37 @@ +
+ {{#if resolved}} +
+ {{d-icon "check"}} + {{i18n "admin.wizard.notice.resolved"}} + {{format-date notice.expired_at leaveAgo="true"}} +
+ {{/if}} + +
+ {{d-icon icon}} + {{title}} +
+ +
+ {{d-icon "calendar-alt"}} + {{i18n "admin.wizard.notice.issued"}} + {{format-date notice.created_at leaveAgo="true"}} +
+ +
+ {{d-icon "plug"}} + {{i18n "admin.wizard.notice.plugin"}} +
+
+ +
+ {{{notice.message}}} +
+ +{{#if canDismiss}} + {{#if dismissing}} + {{loading-spinner size="small"}} + {{else}} + {{d-icon "times"}} + {{/if}} +{{/if}} \ No newline at end of file diff --git a/assets/javascripts/discourse/templates/components/wizard-realtime-validations.hbs b/assets/javascripts/discourse/templates/components/wizard-realtime-validations.hbs index 1aa0893b..8269d6ca 100644 --- a/assets/javascripts/discourse/templates/components/wizard-realtime-validations.hbs +++ b/assets/javascripts/discourse/templates/components/wizard-realtime-validations.hbs @@ -1,6 +1,5 @@
- {{i18n "admin.wizard.pro.label"}}
    diff --git a/assets/javascripts/discourse/templates/components/wizard-pro-selector/wizard-pro-selector-header.hbs b/assets/javascripts/discourse/templates/components/wizard-subscription-selector/wizard-subscription-selector-header.hbs similarity index 70% rename from assets/javascripts/discourse/templates/components/wizard-pro-selector/wizard-pro-selector-header.hbs rename to assets/javascripts/discourse/templates/components/wizard-subscription-selector/wizard-subscription-selector-header.hbs index db467b02..a7e3d2e6 100644 --- a/assets/javascripts/discourse/templates/components/wizard-pro-selector/wizard-pro-selector-header.hbs +++ b/assets/javascripts/discourse/templates/components/wizard-subscription-selector/wizard-subscription-selector-header.hbs @@ -7,8 +7,8 @@ shouldDisplayClearableButton=shouldDisplayClearableButton }} - {{#if selectedContent.pro}} - {{i18n "admin.wizard.pro.label"}} + {{#if selectedContent.subscription}} + {{i18n "admin.wizard.subscription.label"}} {{/if}} {{d-icon caretIcon class="caret-icon"}} diff --git a/assets/javascripts/discourse/templates/components/wizard-pro-selector/wizard-pro-selector-row.hbs b/assets/javascripts/discourse/templates/components/wizard-subscription-selector/wizard-subscription-selector-row.hbs similarity index 71% rename from assets/javascripts/discourse/templates/components/wizard-pro-selector/wizard-pro-selector-row.hbs rename to assets/javascripts/discourse/templates/components/wizard-subscription-selector/wizard-subscription-selector-row.hbs index 077265ef..e2650408 100644 --- a/assets/javascripts/discourse/templates/components/wizard-pro-selector/wizard-pro-selector-row.hbs +++ b/assets/javascripts/discourse/templates/components/wizard-subscription-selector/wizard-subscription-selector-row.hbs @@ -9,7 +9,7 @@
    {{html-safe label}} - {{#if item.pro}} - {{i18n "admin.wizard.pro.label"}} + {{#if item.subscription}} + {{i18n "admin.wizard.subscription.label"}} {{/if}}
    diff --git a/assets/javascripts/discourse/templates/components/wizard-pro-subscription.hbs b/assets/javascripts/discourse/templates/components/wizard-subscription.hbs similarity index 72% rename from assets/javascripts/discourse/templates/components/wizard-pro-subscription.hbs rename to assets/javascripts/discourse/templates/components/wizard-subscription.hbs index 8eca5996..418225a3 100644 --- a/assets/javascripts/discourse/templates/components/wizard-pro-subscription.hbs +++ b/assets/javascripts/discourse/templates/components/wizard-subscription.hbs @@ -13,8 +13,8 @@ icon="sync" action=(action "update") disabled=updating - title="admin.wizard.pro.subscription.update" - label="admin.wizard.pro.subscription.update"}} + title="admin.wizard.subscription.subscription.update" + label="admin.wizard.subscription.subscription.update"}}
@@ -24,7 +24,7 @@ {{#if subscription.updated_at}}
- {{i18n "admin.wizard.pro.subscription.last_updated"}} {{format-date subscription.updated_at leaveAgo="true"}} + {{i18n "admin.wizard.subscription.subscription.last_updated"}} {{format-date subscription.updated_at leaveAgo="true"}}
{{/if}}
diff --git a/assets/stylesheets/common/wizard-admin.scss b/assets/stylesheets/admin/admin.scss similarity index 86% rename from assets/stylesheets/common/wizard-admin.scss rename to assets/stylesheets/admin/admin.scss index 7c6cd95d..2eb32bf3 100644 --- a/assets/stylesheets/common/wizard-admin.scss +++ b/assets/stylesheets/admin/admin.scss @@ -1,8 +1,8 @@ -@import "wizard-mapper"; -@import "wizard-manager"; -@import "wizard-api"; +@import "wizard/mapper"; +@import "wizard/manager"; +@import "wizard/api"; @import "common/components/buttons"; -@import "wizard-variables"; +@import "wizard/variables"; .admin-wizard-controls { display: flex; @@ -121,7 +121,7 @@ } .wizard-settings-parent { - padding: 20px; + padding: 1em; border: 1px solid var(--primary-low); } @@ -142,7 +142,7 @@ .wizard-basic-details, .wizard-custom-field, -.advanced-settings { +.subscription-settings { @extend .wizard-settings-group; } @@ -411,7 +411,7 @@ margin-top: 5px; } - &.pro { + &.subscription { .setting-label { display: flex; flex-direction: column; @@ -423,15 +423,6 @@ } } - .advanced-settings { - width: 100%; - margin-top: 30px; - - [class~="setting"]:first-of-type { - border-top: none; - } - } - .wizard-custom-action > [class~="setting"]:first-of-type { margin-bottom: 0; } @@ -735,7 +726,7 @@ flex-wrap: wrap; > li { - background-color: var(--primary-low); + border: 1px solid var(--primary); padding: 1em; margin: 0 0 1em 0; @@ -797,12 +788,12 @@ vertical-align: middle; } -.pro-label { +.subscription-label { color: var(--tertiary); font-size: 0.75em; } -.admin-wizards-pro { +.admin-wizards-subscription { .admin-wizard-controls { h3, label { @@ -826,7 +817,7 @@ } } - .custom-wizard-pro-subscription { + .custom-wizard-subscription { .title-container { display: flex; justify-content: space-between; @@ -861,19 +852,19 @@ } } -.wizard-pro-selector.select-kit.single-select { +.wizard-subscription-selector.select-kit.single-select { .select-kit-row .texts { display: flex; align-items: center; } - .pro-label { + .subscription-label { margin-left: 0.75em; padding-top: 0.25em; } } -.btn.btn-pavilion-pro { +.btn.btn-pavilion-support { background: var(--pavilion-primary); color: var(--pavilion-secondary); @@ -883,11 +874,108 @@ &:hover, &:focus { - background: darken($pavilionPrimary, 5%); + background: darken($pavilion_primary, 5%); &[href], svg.d-icon { - color: darken($pavilionSecondary, 10%); + color: darken($pavilion_secondary, 10%); } } } + +.subscription-container { + width: 100%; + padding: 1em; + background-color: rgba($pavilion_primary, 0.1); + + .subscription-header { + display: flex; + justify-content: space-between; + margin-bottom: 1em; + + h3 { + margin: 0; + } + + a { + color: var(--pavilion-primary); + } + } + + &:not(.subscribed) .subscription-settings { + filter: blur(1px); + } +} + +.wizard-notice { + padding: 1em; + margin-bottom: 1em; + border: 1px solid var(--primary); + border-radius: 4px; + position: relative; + + &.dismissed { + display: none; + } + + .d-icon { + margin-right: .4em; + } + + .notice-header { + display: flex; + } + + .notice-badge { + border: 1px solid var(--primary); + display: inline-flex; + align-items: center; + padding: 0 .5em; + border-radius: 4px; + margin-right: 1em; + font-size: .9em; + line-height: 25px; + min-height: 25px; + box-sizing: border-box; + + &:last-of-type { + margin-right: 0; + } + } + + &.warning { + .notice-expired-at { + border: 1px solid var(--success); + background-color: rgba($success, 0.1); + color: var(--success); + } + + .notice-title { + border: 1px solid var(--pavilion-warning); + background-color: rgba($pavilion_warning, 0.1); + color: var(--pavilion-warning); + } + } + + .notice-issued { + margin-right: .3em; + } + + .notice-message { + p { + margin: .5em 0; + } + + p:last-of-type { + margin-bottom: 0; + } + } + + .dismiss-notice, + .spinner { + position: absolute; + top: 1em; + right: 1em; + color: var(--primary); + } +} diff --git a/assets/stylesheets/common/wizard-api.scss b/assets/stylesheets/admin/wizard/api.scss similarity index 100% rename from assets/stylesheets/common/wizard-api.scss rename to assets/stylesheets/admin/wizard/api.scss diff --git a/assets/stylesheets/common/wizard-manager.scss b/assets/stylesheets/admin/wizard/manager.scss similarity index 100% rename from assets/stylesheets/common/wizard-manager.scss rename to assets/stylesheets/admin/wizard/manager.scss diff --git a/assets/stylesheets/common/wizard-mapper.scss b/assets/stylesheets/admin/wizard/mapper.scss similarity index 100% rename from assets/stylesheets/common/wizard-mapper.scss rename to assets/stylesheets/admin/wizard/mapper.scss diff --git a/assets/stylesheets/admin/wizard/variables.scss b/assets/stylesheets/admin/wizard/variables.scss new file mode 100644 index 00000000..5912f961 --- /dev/null +++ b/assets/stylesheets/admin/wizard/variables.scss @@ -0,0 +1,9 @@ +$pavilion_primary: #3c1c8c; +$pavilion_secondary: #ffffff; +$pavilion_warning: rgb(243, 163, 61); + +:root { + --pavilion-primary: #{$pavilion_primary}; + --pavilion-secondary: #{$pavilion_secondary}; + --pavilion-warning: #{$pavilion_warning}; +} diff --git a/assets/stylesheets/common/wizard-variables.scss b/assets/stylesheets/common/wizard-variables.scss deleted file mode 100644 index 68f02b6b..00000000 --- a/assets/stylesheets/common/wizard-variables.scss +++ /dev/null @@ -1,7 +0,0 @@ -$pavilionPrimary: #3c1c8c; -$pavilionSecondary: #ffffff; - -:root { - --pavilion-primary: #3c1c8c; - --pavilion-secondary: #ffffff; -} diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 04b9ceab..a63677c6 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -60,9 +60,9 @@ en: expand_text: "Read More" collapse_text: "Show Less" - pro_support_button: - title: "Request Pro Support" - label: "Pro Support" + support_button: + title: "Request Support" + label: "Support" message: wizard: @@ -95,10 +95,10 @@ en: destroying: Destroying wizards... import_complete: Import complete destroy_complete: Destruction complete - pro: - documentation: Check out the PRO documentation - authorize: "Authorize this forum to use your PRO subscription plan on %{server}." - not_subscribed: "You've authorized, but are not currently subscribed to a PRO plan on %{server}." + subscription: + documentation: Check out the subscription documentation + authorize: "Authorize this forum to use your Custom Wizard subscription plan on %{server}." + not_subscribed: "You've authorized, but are not currently subscribed to a Custom Wizard plan on %{server}." subscription_expiring: "Your subscription is active, but will expire in the next 48 hours." subscription_active: "Your subscription is active." subscription_inactive: "Your subscription is inactive on this forum. Read more in the documentation." @@ -455,10 +455,19 @@ en: destroy: Destroy destroyed: destroyed - pro: - nav_label: PRO - label: PRO - title: Custom Wizard PRO + subscription_container: + title: Subscriber Features + subscribed: + label: Subscribed + title: You're subscribed and can use these features + not_subscribed: + label: Not Subscribed + title: Subscribe to use these features + + subscription: + nav_label: Subscription + label: Subscription + title: Custom Wizard Subscription authorize: Authorize authorized: Authorized unauthorize: cancel @@ -471,7 +480,14 @@ en: active: Active inactive: Inactive update: Update - last_updated: Last updated + last_updated: Last updated + + notice: + plugin: Custom Wizard Plugin + issued: Issued + resolved: Resolved + title: + warning: Warning Notice wizard_js: group: diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index fffa01cc..63996b02 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -17,7 +17,7 @@ en: name_too_short: "'%{name}' is too short for a custom field name (min length is #{min_length})" name_already_taken: "'%{name}' is already taken as a custom field name" save_default: "Failed to save custom field '%{name}'" - pro_type: "%{type} custom fields require PRO Subscription" + subscription_type: "%{type} custom fields require a subscription" field: too_short: "%{label} must be at least %{min} characters" @@ -50,7 +50,16 @@ en: required: "%{property} is required" conflict: "Wizard with id '%{wizard_id}' already exists" after_time: "After time setting is invalid" - pro: "%{type} %{property} is PRO only" + subscription: "%{type} %{property} is subscription only" + + notice: + connection_error: "Failed to connect to [%{server}](http://%{server})" + compatibility_issue: > + The Custom Wizard Plugin may have a compatibility issue with the latest version of Discourse. + Please check the Custom Wizard Plugin status on [%{server}](http://%{server}) before updating Discourse. + plugin_status_connection_error_limit: > + We're unable to connect to the plugin status server to determine whether there are any compatibility issues with the latest version of Discourse. + Please contact support@thepavilion.io for further assistance. site_settings: custom_wizard_enabled: "Enable custom wizards." diff --git a/config/routes.rb b/config/routes.rb index 3a37a137..0d59b200 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -45,10 +45,13 @@ Discourse::Application.routes.append do post 'admin/wizards/manager/import' => 'admin_manager#import' delete 'admin/wizards/manager/destroy' => 'admin_manager#destroy' - get 'admin/wizards/pro' => 'admin_pro#index' - get 'admin/wizards/pro/authorize' => 'admin_pro#authorize' - get 'admin/wizards/pro/authorize/callback' => 'admin_pro#authorize_callback' - delete 'admin/wizards/pro/authorize' => 'admin_pro#destroy_authentication' - post 'admin/wizards/pro/subscription' => 'admin_pro#update_subscription' + get 'admin/wizards/subscription' => 'admin_subscription#index' + post 'admin/wizards/subscription' => 'admin_subscription#update_subscription' + get 'admin/wizards/subscription/authorize' => 'admin_subscription#authorize' + get 'admin/wizards/subscription/authorize/callback' => 'admin_subscription#authorize_callback' + delete 'admin/wizards/subscription/authorize' => 'admin_subscription#destroy_authentication' + + get 'admin/wizards/notice' => 'admin_notice#index' + put 'admin/wizards/notice/:notice_id' => 'admin_notice#dismiss' end end diff --git a/controllers/custom_wizard/admin/admin.rb b/controllers/custom_wizard/admin/admin.rb index c99954d6..ff01ddac 100644 --- a/controllers/custom_wizard/admin/admin.rb +++ b/controllers/custom_wizard/admin/admin.rb @@ -3,6 +3,12 @@ class CustomWizard::AdminController < ::Admin::AdminController before_action :ensure_admin def index + render_json_dump( + notices: ActiveModel::ArraySerializer.new( + CustomWizard::Notice.list, + each_serializer: CustomWizard::NoticeSerializer + ) + ) end private diff --git a/controllers/custom_wizard/admin/custom_fields.rb b/controllers/custom_wizard/admin/custom_fields.rb index 40ff64be..d10f82ed 100644 --- a/controllers/custom_wizard/admin/custom_fields.rb +++ b/controllers/custom_wizard/admin/custom_fields.rb @@ -3,7 +3,7 @@ class CustomWizard::AdminCustomFieldsController < CustomWizard::AdminController def index render_json_dump( custom_fields: custom_field_list, - pro_subscribed: CustomWizard::Pro.subscribed? + subscribed: CustomWizard::Subscription.subscribed? ) end diff --git a/controllers/custom_wizard/admin/notice.rb b/controllers/custom_wizard/admin/notice.rb new file mode 100644 index 00000000..bb332810 --- /dev/null +++ b/controllers/custom_wizard/admin/notice.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +class CustomWizard::AdminNoticeController < CustomWizard::AdminController + before_action :find_notice, only: [:dismiss] + + def index + render_serialized(CustomWizard::Notice.list, CustomWizard::NoticeSerializer) + end + + def dismiss + if @notice.dismissable? && @notice.dismiss + render json: success_json.merge(dismissed_at: @notice.dismissed_at) + else + render json: failed_json + end + end + + def find_notice + @notice = CustomWizard::Notice.find(params[:notice_id]) + raise Discourse::InvalidParameters.new(:notice_id) unless @notice + end +end \ No newline at end of file diff --git a/controllers/custom_wizard/admin/pro.rb b/controllers/custom_wizard/admin/pro.rb deleted file mode 100644 index 650743e6..00000000 --- a/controllers/custom_wizard/admin/pro.rb +++ /dev/null @@ -1,48 +0,0 @@ -# frozen_string_literal: true - -class CustomWizard::AdminProController < CustomWizard::AdminController - skip_before_action :check_xhr, :preload_json, :verify_authenticity_token, only: [:authorize, :authorize_callback] - - def index - render_serialized(pro, CustomWizard::ProSerializer, root: false) - end - - def authorize - request_id = SecureRandom.hex(32) - cookies[:user_api_request_id] = request_id - redirect_to pro.authentication_url(current_user.id, request_id).to_s - end - - def authorize_callback - payload = params[:payload] - request_id = cookies[:user_api_request_id] - - pro.authentication_response(request_id, payload) - pro.update_subscription - - redirect_to '/admin/wizards/pro' - end - - def destroy_authentication - if pro.destroy_authentication - render json: success_json - else - render json: failed_json - end - end - - def update_subscription - if pro.update_subscription - subscription = CustomWizard::ProSubscriptionSerializer.new(pro.subscription, root: false) - render json: success_json.merge(subscription: subscription) - else - render json: failed_json - end - end - - protected - - def pro - @pro ||= CustomWizard::Pro.new - end -end diff --git a/controllers/custom_wizard/admin/subscription.rb b/controllers/custom_wizard/admin/subscription.rb new file mode 100644 index 00000000..15ff6396 --- /dev/null +++ b/controllers/custom_wizard/admin/subscription.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +class CustomWizard::AdminSubscriptionController < CustomWizard::AdminController + skip_before_action :check_xhr, :preload_json, :verify_authenticity_token, only: [:authorize, :authorize_callback] + + def index + render_serialized(subscription, CustomWizard::SubscriptionSerializer, root: false) + end + + def authorize + request_id = SecureRandom.hex(32) + cookies[:user_api_request_id] = request_id + redirect_to subscription.authentication_url(current_user.id, request_id).to_s + end + + def authorize_callback + payload = params[:payload] + request_id = cookies[:user_api_request_id] + + subscription.authentication_response(request_id, payload) + subscription.update + + redirect_to '/admin/wizards/subscription' + end + + def destroy_authentication + if subscription.destroy_authentication + render json: success_json + else + render json: failed_json + end + end + + def update_subscription + if subscription.update + serialized_subscription = CustomWizard::Subscription::SubscriptionSerializer.new(subscription.subscription, root: false) + render json: success_json.merge(subscription: serialized_subscription) + else + render json: failed_json + end + end + + protected + + def subscription + @subscription ||= CustomWizard::Subscription.new + end +end diff --git a/controllers/custom_wizard/admin/wizard.rb b/controllers/custom_wizard/admin/wizard.rb index e824398c..fdf338bf 100644 --- a/controllers/custom_wizard/admin/wizard.rb +++ b/controllers/custom_wizard/admin/wizard.rb @@ -11,7 +11,7 @@ class CustomWizard::AdminWizardController < CustomWizard::AdminController field_types: CustomWizard::Field.types, realtime_validations: CustomWizard::RealtimeValidation.types, custom_fields: custom_field_list, - pro_subscribed: CustomWizard::Pro.subscribed? + subscribed: CustomWizard::Subscription.subscribed? ) end diff --git a/controllers/custom_wizard/wizard.rb b/controllers/custom_wizard/wizard.rb index 732c5d5e..40db52e6 100644 --- a/controllers/custom_wizard/wizard.rb +++ b/controllers/custom_wizard/wizard.rb @@ -5,7 +5,7 @@ class CustomWizard::WizardController < ::ApplicationController layout 'wizard' before_action :ensure_plugin_enabled - before_action :update_pro_subscription, only: [:index] + before_action :update_subscription, only: [:index] helper_method :wizard_page_title helper_method :wizard_theme_id helper_method :wizard_theme_lookup @@ -84,7 +84,7 @@ class CustomWizard::WizardController < ::ApplicationController end end - def update_pro_subscription - CustomWizard::Pro.update_subscription + def update_subscription + CustomWizard::Subscription.update end end diff --git a/coverage/.last_run.json b/coverage/.last_run.json index 23e2ecb9..1c721888 100644 --- a/coverage/.last_run.json +++ b/coverage/.last_run.json @@ -1,5 +1,5 @@ { "result": { - "line": 92.14 + "line": 92.3 } } diff --git a/jobs/scheduled/custom_wizard/update_notices.rb b/jobs/scheduled/custom_wizard/update_notices.rb new file mode 100644 index 00000000..5194e2b8 --- /dev/null +++ b/jobs/scheduled/custom_wizard/update_notices.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class Jobs::CustomWizardUpdateNotices < ::Jobs::Scheduled + every 5.minutes + + def execute(args = {}) + CustomWizard::Notice.update + end +end \ No newline at end of file diff --git a/jobs/scheduled/custom_wizard/update_subscription.rb b/jobs/scheduled/custom_wizard/update_subscription.rb new file mode 100644 index 00000000..72e34435 --- /dev/null +++ b/jobs/scheduled/custom_wizard/update_subscription.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class Jobs::CustomWizardUpdateSubscription < ::Jobs::Scheduled + every 1.hour + + def execute(args = {}) + CustomWizard::Subscription.update + end +end diff --git a/jobs/scheduled/update_pro_subscription.rb b/jobs/scheduled/update_pro_subscription.rb deleted file mode 100644 index c790d529..00000000 --- a/jobs/scheduled/update_pro_subscription.rb +++ /dev/null @@ -1,9 +0,0 @@ -# frozen_string_literal: true - -class CustomWizard::UpdateProSubscription < ::Jobs::Scheduled - every 1.hour - - def execute(args = {}) - CustomWizard::Pro.update_subscription - end -end diff --git a/lib/custom_wizard/action.rb b/lib/custom_wizard/action.rb index 8ee11e54..113a0393 100644 --- a/lib/custom_wizard/action.rb +++ b/lib/custom_wizard/action.rb @@ -749,8 +749,4 @@ class CustomWizard::Action @log.join('; ') ) end - - def pro_actions - %w[send_message watch_categories send_to_api create_group create_category] - end end diff --git a/lib/custom_wizard/custom_field.rb b/lib/custom_wizard/custom_field.rb index bc1a146d..eb93e292 100644 --- a/lib/custom_wizard/custom_field.rb +++ b/lib/custom_wizard/custom_field.rb @@ -17,9 +17,9 @@ class ::CustomWizard::CustomField category: ["basic_category"], post: ["post"] } - PRO_CLASSES ||= ['category', 'group'] + SUBSCRIPTION_CLASSES ||= ['category', 'group'] TYPES ||= ["string", "boolean", "integer", "json"] - PRO_TYPES ||= ["json"] + SUBSCRIPTION_TYPES ||= ["json"] LIST_CACHE_KEY ||= 'custom_field_list' def self.serializers @@ -40,7 +40,7 @@ class ::CustomWizard::CustomField end end - @pro = CustomWizard::Pro.new + @subscription = CustomWizard::Subscription.new end def save @@ -85,8 +85,8 @@ class ::CustomWizard::CustomField next end - if attr == 'klass' && PRO_CLASSES.include?(value) && !@pro.subscribed? - add_error(I18n.t("wizard.custom_field.error.pro_type", type: value)) + if attr == 'klass' && SUBSCRIPTION_CLASSES.include?(value) && !@subscription.subscribed? + add_error(I18n.t("wizard.custom_field.error.subscription_type", type: value)) end if attr == 'serializers' && (unsupported = value - CLASSES[klass.to_sym]).length > 0 @@ -100,8 +100,8 @@ class ::CustomWizard::CustomField add_error(I18n.t("#{i18n_key}.unsupported_type", type: value)) end - if attr == 'type' && PRO_TYPES.include?(value) && !@pro.subscribed? - add_error(I18n.t("wizard.custom_field.error.pro_type", type: value)) + if attr == 'type' && SUBSCRIPTION_TYPES.include?(value) && !@subscription.subscribed? + add_error(I18n.t("wizard.custom_field.error.subscription_type", type: value)) end if attr == 'name' diff --git a/lib/custom_wizard/mapper.rb b/lib/custom_wizard/mapper.rb index b66c1716..05d19a1d 100644 --- a/lib/custom_wizard/mapper.rb +++ b/lib/custom_wizard/mapper.rb @@ -47,7 +47,7 @@ class CustomWizard::Mapper @data = params[:data] || {} @user = params[:user] @opts = params[:opts] || {} - @pro = CustomWizard::Pro.new + @subscription = CustomWizard::Subscription.new end def perform @@ -252,7 +252,7 @@ class CustomWizard::Mapper end end - if opts[:template] && @pro.subscribed? + if opts[:template] && @subscription.subscribed? template = Liquid::Template.parse(string) string = template.render(data) end diff --git a/lib/custom_wizard/notice.rb b/lib/custom_wizard/notice.rb new file mode 100644 index 00000000..096cc579 --- /dev/null +++ b/lib/custom_wizard/notice.rb @@ -0,0 +1,234 @@ +# frozen_string_literal: true + +class CustomWizard::Notice + include ActiveModel::Serialization + + PLUGIN_STATUSES_TO_WARN = %w(incompatible tests_failing) + + attr_reader :id, + :message, + :type, + :created_at + + attr_accessor :retrieved_at, + :dismissed_at, + :expired_at + + def initialize(attrs) + @id = Digest::SHA1.hexdigest(attrs[:message]) + @message = attrs[:message] + @type = attrs[:type].to_i + @created_at = attrs[:created_at] + @retrieved_at = attrs[:retrieved_at] + @dismissed_at = attrs[:dismissed_at] + @expired_at = attrs[:expired_at] + end + + def dismiss + if dismissable? + self.dismissed_at = Time.now + self.save + end + end + + def expire + self.expired_at = Time.now + self.save + end + + def expired? + expired_at.present? + end + + def dismissed? + dismissed_at.present? + end + + def dismissable? + true + end + + def save + attrs = { + expired_at: expired_at, + created_at: created_at, + expired_at: expired_at, + message: message, + type: type + } + + if current = self.class.find(self.id) + attrs[:dismissed_at] = current.dismissed_at || self.dismissed_at + end + + self.class.store(id, attrs) + end + + def self.types + @types ||= Enum.new( + info: 0, + warning: 1 + ) + end + + def self.connection_types + @connection_types ||= Enum.new( + plugin_status: 0, + subscription: 1 + ) + end + + def self.update(skip_subscription: false, skip_plugin: false) + notices = [] + + if !skip_subscription + subscription_messages = request(subscription_messages_url) + if subscription_messages.present? + subscription_notices = convert_subscription_messages_to_notices(subscription_messages[:messages]) + notices.push(*subscription_notices) + end + end + + if !skip_plugin && (Discourse.git_branch === 'tests-passed' || (Rails.env.test? || Rails.env.development?)) + plugin_status = request(plugin_status_url) + + if plugin_status.present? && plugin_status[:status].present? && plugin_status[:status].is_a?(Hash) + plugin_notice = convert_plugin_status_to_notice(plugin_status[:status]) + notices.push(plugin_notice) if plugin_notice + + expire_connection_errors(connection_types[:plugin_status]) + else + create_connection_error(connection_types[:plugin_status]) + end + end + + notices.each do |notice_data| + notice = new(notice_data) + notice.retrieved_at = Time.now + notice.save + end + + if reached_connection_error_limit(connection_types[:plugin_status]) + new( + message: I18n.t("wizard.notice.plugin_status_connection_error_limit"), + type: types[:warning], + created_at: Time.now + ) + end + end + + def self.convert_subscription_messages_to_notices(messages) + messages.map do |message| + { + message: message[:message], + type: types[message[:type].to_sym], + created_at: message[:created_at], + expired_at: message[:expired_at] + } + end + end + + def self.convert_plugin_status_to_notice(plugin_status) + notice = nil + + if PLUGIN_STATUSES_TO_WARN.include?(plugin_status[:status]) + notice = { + message: PrettyText.cook(I18n.t('wizard.notice.compatibility_issue', server: plugin_status_domain)), + type: types[:warning], + created_at: plugin_status[:status_changed_at] + } + else + list(types[:warning]).each(&:expire) + end + + notice + end + + def self.subscription_messages_domain + "localhost:3000" + end + + def self.subscription_messages_url + "http://#{subscription_messages_domain}/subscription-server/messages.json" + end + + def self.plugin_status_domain + "localhost:4200" + end + + def self.plugin_status_url + "http://#{plugin_status_domain}/plugin-manager/status/discourse-custom-wizard" + end + + def self.request(url) + response = Excon.get(url) + + if response.status == 200 + begin + data = JSON.parse(response.body).deep_symbolize_keys + rescue JSON::ParserError + return nil + end + + data + else + nil + end + end + + def self.namespace + "#{CustomWizard::PLUGIN_NAME}_notice" + end + + def self.namespace_connection + "#{CustomWizard::PLUGIN_NAME}_notice_connection" + end + + def self.find(id) + raw = PluginStore.get(namespace, id) + new(raw.symbolize_keys) if raw.present? + end + + def self.store(id, raw_notice) + PluginStore.set(namespace, id, raw_notice) + end + + def self.plugin_status_connection_error_limit + 5 + end + + def self.list_connection_query(type) + query = PluginStoreRow.where(plugin_name: namespace_connection) + query.where("(value::json->>'type')::integer = ?", type) + end + + def self.expire_connection_errors(type) + list_connection_query(type).update_all("value = jsonb_set(value::jsonb, '{ expired_at }', (to_char(current_timestamp, 'HH12:MI:SS'))::jsonb)") + end + + def self.create_connection_error(type) + id = SecureRandom.hex(16) + attrs = { + message: I18n.t("wizard.notice.connection_error", domain: self.send("#{type}_domain")), + type: type, + created_at: Time.now + } + PluginStore.set(namespace_connection, id, attrs) + end + + def self.reached_connection_error_limit(type) + list_connection_query(type).size >= self.send("#{connection_types.key(type)}_connection_error_limit") + end + + def self.list_query(type = nil) + query = PluginStoreRow.where(plugin_name: namespace) + query = query.where("(value::json->>'expired_at') IS NULL OR (value::json->>'expired_at')::date > now()::date - 1") + query = query.where("(value::json->>'type')::integer = ?", type) if type + query.order("value::json->>'created_at' DESC") + end + + def self.list(type = nil) + list_query(type) + .map { |r| self.new(JSON.parse(r.value).symbolize_keys) } + end +end diff --git a/lib/custom_wizard/pro.rb b/lib/custom_wizard/subscription.rb similarity index 81% rename from lib/custom_wizard/pro.rb rename to lib/custom_wizard/subscription.rb index 61097069..11e6d4d3 100644 --- a/lib/custom_wizard/pro.rb +++ b/lib/custom_wizard/subscription.rb @@ -1,14 +1,14 @@ # frozen_string_literal: true -class CustomWizard::Pro +class CustomWizard::Subscription include ActiveModel::Serialization attr_accessor :authentication, :subscription def initialize - @authentication = CustomWizard::ProAuthentication.new(get_authentication) - @subscription = CustomWizard::ProSubscription.new(get_subscription) + @authentication = CustomWizard::Subscription::Authentication.new(get_authentication) + @subscription = CustomWizard::Subscription::Subscription.new(get_subscription) end def authorized? @@ -35,7 +35,7 @@ class CustomWizard::Pro "discourse-subscription-server:user_subscription" end - def update_subscription + def update if @authentication.active? response = Excon.get( "https://#{server}/subscription-server/user-subscriptions/#{subscription_type}/#{client_name}", @@ -67,7 +67,7 @@ class CustomWizard::Pro def destroy_subscription if remove_subscription - @subscription = CustomWizard::ProSubscription.new(get_subscription) + @subscription = CustomWizard::Subscription::Subscription.new(get_subscription) !@subscription.active? else false @@ -80,7 +80,7 @@ class CustomWizard::Pro public_key: keys.public_key, nonce: keys.nonce, client_id: @authentication.client_id, - auth_redirect: "#{Discourse.base_url}/admin/wizards/pro/authorize/callback", + auth_redirect: "#{Discourse.base_url}/admin/wizards/subscription/authorize/callback", application_name: SiteSetting.title, scopes: scope } @@ -108,7 +108,7 @@ class CustomWizard::Pro def destroy_authentication if remove_authentication - @authentication = CustomWizard::ProAuthentication.new(get_authentication) + @authentication = CustomWizard::Subscription::Authentication.new(get_authentication) !@authentication.active? else false @@ -123,12 +123,12 @@ class CustomWizard::Pro self.new.authorized? end - def self.update_subscription - self.new.update_subscription + def self.update + self.new.update end def self.namespace - "custom_wizard_pro" + "custom_wizard_subscription" end private @@ -157,8 +157,8 @@ class CustomWizard::Pro end def set_subscription(type) - PluginStore.set(CustomWizard::Pro.namespace, subscription_db_key, type: type, updated_at: Time.now) - CustomWizard::ProSubscription.new(get_subscription) + PluginStore.set(CustomWizard::Subscription.namespace, subscription_db_key, type: type, updated_at: Time.now) + CustomWizard::Subscription::Subscription.new(get_subscription) end def get_authentication @@ -176,7 +176,7 @@ class CustomWizard::Pro auth_by: user_id, auth_at: Time.now ) - CustomWizard::ProAuthentication.new(get_authentication) + CustomWizard::Subscription::Authentication.new(get_authentication) end def remove_authentication diff --git a/lib/custom_wizard/pro/authentication.rb b/lib/custom_wizard/subscription/authentication.rb similarity index 77% rename from lib/custom_wizard/pro/authentication.rb rename to lib/custom_wizard/subscription/authentication.rb index 23603898..7ad02d2c 100644 --- a/lib/custom_wizard/pro/authentication.rb +++ b/lib/custom_wizard/subscription/authentication.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true -class CustomWizard::ProAuthentication +class CustomWizard::Subscription::Authentication include ActiveModel::Serialization attr_reader :client_id, @@ -53,7 +53,7 @@ class CustomWizard::ProAuthentication end def get_keys(request_id) - raw = PluginStore.get(CustomWizard::Pro.namespace, "#{keys_db_key}_#{request_id}") + raw = PluginStore.get(CustomWizard::Subscription.namespace, "#{keys_db_key}_#{request_id}") OpenStruct.new( user_id: raw && raw['user_id'], pem: raw && raw['pem'], @@ -72,7 +72,7 @@ class CustomWizard::ProAuthentication end def set_keys(request_id, user_id, rsa, nonce) - PluginStore.set(CustomWizard::Pro.namespace, "#{keys_db_key}_#{request_id}", + PluginStore.set(CustomWizard::Subscription.namespace, "#{keys_db_key}_#{request_id}", user_id: user_id, pem: rsa.export, nonce: nonce @@ -80,16 +80,16 @@ class CustomWizard::ProAuthentication end def delete_keys(request_id) - PluginStore.remove(CustomWizard::Pro.namespace, "#{keys_db_key}_#{request_id}") + PluginStore.remove(CustomWizard::Subscription.namespace, "#{keys_db_key}_#{request_id}") end def get_client_id - PluginStore.get(CustomWizard::Pro.namespace, client_id_db_key) + PluginStore.get(CustomWizard::Subscription.namespace, client_id_db_key) end def set_client_id client_id = SecureRandom.hex(32) - PluginStore.set(CustomWizard::Pro.namespace, client_id_db_key, client_id) + PluginStore.set(CustomWizard::Subscription.namespace, client_id_db_key, client_id) client_id end end diff --git a/lib/custom_wizard/pro/subscription.rb b/lib/custom_wizard/subscription/subscription.rb similarity index 89% rename from lib/custom_wizard/pro/subscription.rb rename to lib/custom_wizard/subscription/subscription.rb index 7f5cf911..129f993a 100644 --- a/lib/custom_wizard/pro/subscription.rb +++ b/lib/custom_wizard/subscription/subscription.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true -class CustomWizard::ProSubscription +class CustomWizard::Subscription::Subscription include ActiveModel::Serialization attr_reader :type, diff --git a/lib/custom_wizard/validators/template.rb b/lib/custom_wizard/validators/template.rb index 4311bbbc..4928c4e5 100644 --- a/lib/custom_wizard/validators/template.rb +++ b/lib/custom_wizard/validators/template.rb @@ -6,7 +6,7 @@ class CustomWizard::TemplateValidator def initialize(data, opts = {}) @data = data @opts = opts - @pro = CustomWizard::Pro.new + @subscription = CustomWizard::Subscription.new end def perform @@ -15,15 +15,15 @@ class CustomWizard::TemplateValidator check_id(data, :wizard) check_required(data, :wizard) validate_after_time - validate_pro(data, :wizard) + validate_subscription(data, :wizard) data[:steps].each do |step| check_required(step, :step) - validate_pro(step, :step) + validate_subscription(step, :step) if step[:fields].present? step[:fields].each do |field| - validate_pro(field, :field) + validate_subscription(field, :field) check_required(field, :field) end end @@ -31,7 +31,7 @@ class CustomWizard::TemplateValidator if data[:actions].present? data[:actions].each do |action| - validate_pro(action, :action) + validate_subscription(action, :action) check_required(action, :action) end end @@ -52,7 +52,7 @@ class CustomWizard::TemplateValidator } end - def self.pro + def self.subscription { wizard: { save_submissions: 'false', @@ -90,18 +90,18 @@ class CustomWizard::TemplateValidator end end - def validate_pro(object, type) - self.class.pro[type].each do |property, pro_type| + def validate_subscription(object, type) + self.class.subscription[type].each do |property, subscription_type| val = object[property.to_s] - is_pro = (val != nil) && ( - pro_type === 'present' && val.present? || - (['true', 'false'].include?(pro_type) && cast_bool(val) == cast_bool(pro_type)) || - (pro_type === 'conditional' && val.is_a?(Hash)) || - (pro_type.is_a?(Array) && pro_type.include?(val)) + is_subscription = (val != nil) && ( + subscription_type === 'present' && val.present? || + (['true', 'false'].include?(subscription_type) && cast_bool(val) == cast_bool(subscription_type)) || + (subscription_type === 'conditional' && val.is_a?(Hash)) || + (subscription_type.is_a?(Array) && subscription_type.include?(val)) ) - if is_pro && !@pro.subscribed? - errors.add :base, I18n.t("wizard.validation.pro", type: type.to_s, property: property) + if is_subscription && !@subscription.subscribed? + errors.add :base, I18n.t("wizard.validation.subscription", type: type.to_s, property: property) end end end diff --git a/plugin.rb b/plugin.rb index 808975d6..c3063d29 100644 --- a/plugin.rb +++ b/plugin.rb @@ -7,9 +7,7 @@ # contact emails: angus@thepavilion.io gem 'liquid', '5.0.1', require: true -register_asset 'stylesheets/common/wizard-admin.scss' -register_asset 'stylesheets/common/wizard-mapper.scss' - +register_asset 'stylesheets/admin/admin.scss', :desktop enabled_site_setting :custom_wizard_enabled config = Rails.application.config @@ -42,6 +40,7 @@ if respond_to?(:register_svg_icon) register_svg_icon "comment-alt" register_svg_icon "far-life-ring" register_svg_icon "arrow-right" + register_svg_icon "shield-virus" end class ::Sprockets::DirectiveProcessor @@ -71,13 +70,15 @@ after_initialize do ../controllers/custom_wizard/admin/logs.rb ../controllers/custom_wizard/admin/manager.rb ../controllers/custom_wizard/admin/custom_fields.rb - ../controllers/custom_wizard/admin/pro.rb + ../controllers/custom_wizard/admin/subscription.rb + ../controllers/custom_wizard/admin/notice.rb ../controllers/custom_wizard/wizard.rb ../controllers/custom_wizard/steps.rb ../controllers/custom_wizard/realtime_validations.rb ../jobs/regular/refresh_api_access_token.rb ../jobs/regular/set_after_time_wizard.rb - ../jobs/scheduled/update_pro_subscription.rb + ../jobs/scheduled/custom_wizard/update_subscription.rb + ../jobs/scheduled/custom_wizard/update_notices.rb ../lib/custom_wizard/validators/template.rb ../lib/custom_wizard/validators/update.rb ../lib/custom_wizard/action_result.rb @@ -96,9 +97,10 @@ after_initialize do ../lib/custom_wizard/submission.rb ../lib/custom_wizard/template.rb ../lib/custom_wizard/wizard.rb - ../lib/custom_wizard/pro.rb - ../lib/custom_wizard/pro/subscription.rb - ../lib/custom_wizard/pro/authentication.rb + ../lib/custom_wizard/notice.rb + ../lib/custom_wizard/subscription.rb + ../lib/custom_wizard/subscription/subscription.rb + ../lib/custom_wizard/subscription/authentication.rb ../lib/custom_wizard/api/api.rb ../lib/custom_wizard/api/authorization.rb ../lib/custom_wizard/api/endpoint.rb @@ -119,9 +121,10 @@ after_initialize do ../serializers/custom_wizard/log_serializer.rb ../serializers/custom_wizard/submission_serializer.rb ../serializers/custom_wizard/realtime_validation/similar_topics_serializer.rb - ../serializers/custom_wizard/pro/authentication_serializer.rb - ../serializers/custom_wizard/pro/subscription_serializer.rb - ../serializers/custom_wizard/pro_serializer.rb + ../serializers/custom_wizard/subscription/authentication_serializer.rb + ../serializers/custom_wizard/subscription/subscription_serializer.rb + ../serializers/custom_wizard/subscription_serializer.rb + ../serializers/custom_wizard/notice_serializer.rb ../extensions/extra_locales_controller.rb ../extensions/invites_controller.rb ../extensions/users_controller.rb @@ -238,5 +241,11 @@ after_initialize do "#{serializer_klass}_serializer".classify.constantize.prepend CustomWizardCustomFieldSerializer end + AdminDashboardData.add_problem_check do + warning_notices = CustomWizard::Notice.list(CustomWizard::Notice.types[:warning]) + warning_notices.any? ? ActionView::Base.full_sanitizer.sanitize(warning_notices.first.message, tags: %w(a)) : nil + end + + Jobs.enqueue(:custom_wizard_update_notices) DiscourseEvent.trigger(:custom_wizard_ready) end diff --git a/serializers/custom_wizard/notice_serializer.rb b/serializers/custom_wizard/notice_serializer.rb new file mode 100644 index 00000000..310827f7 --- /dev/null +++ b/serializers/custom_wizard/notice_serializer.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +class CustomWizard::NoticeSerializer < ApplicationSerializer + attributes :id, + :message, + :type, + :created_at, + :expired_at, + :dismissed_at, + :retrieved_at, + :dismissable + + def dismissable + object.dismissable? + end + + def type + CustomWizard::Notice.types.key(object.type) + end +end diff --git a/serializers/custom_wizard/pro_serializer.rb b/serializers/custom_wizard/pro_serializer.rb deleted file mode 100644 index 2f141c6d..00000000 --- a/serializers/custom_wizard/pro_serializer.rb +++ /dev/null @@ -1,6 +0,0 @@ -# frozen_string_literal: true -class CustomWizard::ProSerializer < ApplicationSerializer - attributes :server - has_one :authentication, serializer: CustomWizard::ProAuthenticationSerializer, embed: :objects - has_one :subscription, serializer: CustomWizard::ProSubscriptionSerializer, embed: :objects -end diff --git a/serializers/custom_wizard/pro/authentication_serializer.rb b/serializers/custom_wizard/subscription/authentication_serializer.rb similarity index 66% rename from serializers/custom_wizard/pro/authentication_serializer.rb rename to serializers/custom_wizard/subscription/authentication_serializer.rb index 0a2915e4..3d54d0f2 100644 --- a/serializers/custom_wizard/pro/authentication_serializer.rb +++ b/serializers/custom_wizard/subscription/authentication_serializer.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true -class CustomWizard::ProAuthenticationSerializer < ApplicationSerializer +class CustomWizard::Subscription::AuthenticationSerializer < ApplicationSerializer attributes :active, :client_id, :auth_by, diff --git a/serializers/custom_wizard/pro/subscription_serializer.rb b/serializers/custom_wizard/subscription/subscription_serializer.rb similarity index 63% rename from serializers/custom_wizard/pro/subscription_serializer.rb rename to serializers/custom_wizard/subscription/subscription_serializer.rb index d3e04e4e..95a2b323 100644 --- a/serializers/custom_wizard/pro/subscription_serializer.rb +++ b/serializers/custom_wizard/subscription/subscription_serializer.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true -class CustomWizard::ProSubscriptionSerializer < ApplicationSerializer +class CustomWizard::Subscription::SubscriptionSerializer < ApplicationSerializer attributes :type, :active, :updated_at diff --git a/serializers/custom_wizard/subscription_serializer.rb b/serializers/custom_wizard/subscription_serializer.rb new file mode 100644 index 00000000..067cfbcd --- /dev/null +++ b/serializers/custom_wizard/subscription_serializer.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true +class CustomWizard::SubscriptionSerializer < ApplicationSerializer + attributes :server + has_one :authentication, serializer: CustomWizard::Subscription::AuthenticationSerializer, embed: :objects + has_one :subscription, serializer: CustomWizard::Subscription::SubscriptionSerializer, embed: :objects +end diff --git a/serializers/custom_wizard/wizard_field_serializer.rb b/serializers/custom_wizard/wizard_field_serializer.rb index fc2575ef..bba2b41a 100644 --- a/serializers/custom_wizard/wizard_field_serializer.rb +++ b/serializers/custom_wizard/wizard_field_serializer.rb @@ -123,6 +123,6 @@ class CustomWizard::FieldSerializer < ::ApplicationSerializer end def subscribed? - @subscribed ||= CustomWizard::Pro.subscribed? + @subscribed ||= CustomWizard::Subscription.subscribed? end end diff --git a/serializers/custom_wizard/wizard_serializer.rb b/serializers/custom_wizard/wizard_serializer.rb index 4a29d3ea..fe2ac355 100644 --- a/serializers/custom_wizard/wizard_serializer.rb +++ b/serializers/custom_wizard/wizard_serializer.rb @@ -10,7 +10,7 @@ class CustomWizard::WizardSerializer < CustomWizard::BasicWizardSerializer :permitted, :uncategorized_category_id, :categories, - :pro_subscribed + :subscribed has_many :steps, serializer: ::CustomWizard::StepSerializer, embed: :objects has_one :user, serializer: ::BasicUserSerializer, embed: :objects @@ -62,7 +62,7 @@ class CustomWizard::WizardSerializer < CustomWizard::BasicWizardSerializer object.categories.map { |c| c.to_h } end - def pro_subscribed - CustomWizard::Pro.subscribed? + def subscribed + CustomWizard::Subscription.subscribed? end end diff --git a/serializers/custom_wizard/wizard_step_serializer.rb b/serializers/custom_wizard/wizard_step_serializer.rb index 463fa3d6..a2a314a4 100644 --- a/serializers/custom_wizard/wizard_step_serializer.rb +++ b/serializers/custom_wizard/wizard_step_serializer.rb @@ -80,8 +80,4 @@ class CustomWizard::StepSerializer < ::ApplicationSerializer def i18n_key @i18n_key ||= "#{object.wizard.id}.#{object.id}".underscore end - - def subscribed? - @subscribed ||= CustomWizard::Pro.subscribed? - end end diff --git a/spec/components/custom_wizard/action_spec.rb b/spec/components/custom_wizard/action_spec.rb index c92a0d61..e34dd861 100644 --- a/spec/components/custom_wizard/action_spec.rb +++ b/spec/components/custom_wizard/action_spec.rb @@ -174,7 +174,7 @@ describe CustomWizard::Action do expect(updater.result[:redirect_on_next]).to eq("https://google.com") end - context "pro actions" do + context "subscription actions" do before do enable_subscription end diff --git a/spec/components/custom_wizard/custom_field_spec.rb b/spec/components/custom_wizard/custom_field_spec.rb index 3bcfab46..a30ec02b 100644 --- a/spec/components/custom_wizard/custom_field_spec.rb +++ b/spec/components/custom_wizard/custom_field_spec.rb @@ -4,7 +4,7 @@ require_relative '../../plugin_helper' describe CustomWizard::CustomField do let(:custom_field_json) { get_wizard_fixture("custom_field/custom_fields") } - let(:custom_field_pro_json) { get_wizard_fixture("custom_field/pro_custom_fields") } + let(:custom_field_subscription_json) { get_wizard_fixture("custom_field/subscription_custom_fields") } before do CustomWizard::CustomField.invalidate_cache @@ -193,44 +193,44 @@ describe CustomWizard::CustomField do ).to eq(false) end - it "does not save pro field types without a pro subscription" do - pro_field_json = custom_field_pro_json['custom_fields'].first - custom_field = CustomWizard::CustomField.new(nil, pro_field_json) + it "does not save subscription field types without a subscription" do + subscription_field_json = custom_field_subscription_json['custom_fields'].first + custom_field = CustomWizard::CustomField.new(nil, subscription_field_json) expect(custom_field.save).to eq(false) expect(custom_field.valid?).to eq(false) expect(custom_field.errors.full_messages.first).to eq( - I18n.t("wizard.custom_field.error.pro_type", type: "json") + I18n.t("wizard.custom_field.error.subscription_type", type: "json") ) end - it "does not save pro field classes without a pro subscription" do - pro_field_json = custom_field_pro_json['custom_fields'].second - custom_field = CustomWizard::CustomField.new(nil, pro_field_json) + it "does not save subscription field classes without a subscription" do + subscription_field_json = custom_field_subscription_json['custom_fields'].second + custom_field = CustomWizard::CustomField.new(nil, subscription_field_json) expect(custom_field.save).to eq(false) expect(custom_field.valid?).to eq(false) expect(custom_field.errors.full_messages.first).to eq( - I18n.t("wizard.custom_field.error.pro_type", type: "category") + I18n.t("wizard.custom_field.error.subscription_type", type: "category") ) end - context "with a pro subscription" do + context "with a subscription" do before do enable_subscription end - it "saves pro field types" do - pro_field_json = custom_field_pro_json['custom_fields'].first - custom_field = CustomWizard::CustomField.new(nil, pro_field_json) + it "saves subscription field types" do + subscription_field_json = custom_field_subscription_json['custom_fields'].first + custom_field = CustomWizard::CustomField.new(nil, subscription_field_json) expect(custom_field.save).to eq(true) expect(custom_field.valid?).to eq(true) end - it "saves pro field classes" do - pro_field_json = custom_field_pro_json['custom_fields'].second - custom_field = CustomWizard::CustomField.new(nil, pro_field_json) + it "saves subscription field classes" do + subscription_field_json = custom_field_subscription_json['custom_fields'].second + custom_field = CustomWizard::CustomField.new(nil, subscription_field_json) expect(custom_field.save).to eq(true) expect(custom_field.valid?).to eq(true) diff --git a/spec/components/custom_wizard/mapper_spec.rb b/spec/components/custom_wizard/mapper_spec.rb index 7ef2a5a8..1ac945d0 100644 --- a/spec/components/custom_wizard/mapper_spec.rb +++ b/spec/components/custom_wizard/mapper_spec.rb @@ -344,7 +344,7 @@ describe CustomWizard::Mapper do expect(result).to eq(template_params["step_1_field_1"]) end - it "requires a pro subscription" do + it "requires a subscription" do template = '{{ "w{step_1_field_1}" | size }}' mapper = create_template_mapper(template_params, user1) result = mapper.interpolate( @@ -357,7 +357,7 @@ describe CustomWizard::Mapper do expect(result).to eq("{{ \"#{template_params["step_1_field_1"]}\" | size }}") end - context "with a pro subscription" do + context "with a subscription" do before do enable_subscription end diff --git a/spec/components/custom_wizard/notice_spec.rb b/spec/components/custom_wizard/notice_spec.rb new file mode 100644 index 00000000..373d2e31 --- /dev/null +++ b/spec/components/custom_wizard/notice_spec.rb @@ -0,0 +1,80 @@ +# frozen_string_literal: true + +require_relative '../../plugin_helper' + +describe CustomWizard::Notice do + fab!(:user) { Fabricate(:user) } + let(:subscription_message) { + { + message: "Message about subscription", + type: "info", + created_at: Time.now - 3.day, + expired_at: nil + } + } + let(:plugin_status) { + { + name: 'discourse-custom-wizard', + status: 'incompatible', + status_changed_at: Time.now - 3.day + } + } + + context "subscription message" do + before do + freeze_time + stub_request(:get, described_class.subscription_messages_url).to_return(status: 200, body: { messages: [subscription_message] }.to_json) + described_class.update(skip_plugin: true) + end + + it "converts subscription messages into notices" do + notice = described_class.list.first + expect(notice.type).to eq(described_class.types[:info]) + expect(notice.message).to eq(subscription_message[:message]) + expect(notice.created_at.to_datetime).to be_within(1.second).of (subscription_message[:created_at].to_datetime) + end + + it "expires notice if subscription message is expired" do + subscription_message[:expired_at] = Time.now + stub_request(:get, described_class.subscription_messages_url).to_return(status: 200, body: { messages: [subscription_message] }.to_json) + described_class.update(skip_plugin: true) + + notice = described_class.list.first + expect(notice.expired?).to eq(true) + end + end + + context "plugin status" do + before do + freeze_time + stub_request(:get, described_class.plugin_status_url).to_return(status: 200, body: { status: plugin_status }.to_json) + described_class.update(skip_subscription: true) + end + + it "converts plugin statuses to warn into notices" do + notice = described_class.list.first + expect(notice.type).to eq(described_class.types[:warning]) + expect(notice.message).to eq(PrettyText.cook(I18n.t("wizard.notice.compatibility_issue", server: described_class.plugin_status_domain))) + expect(notice.created_at.to_datetime).to be_within(1.second).of (plugin_status[:status_changed_at].to_datetime) + end + + it "expires unexpired warning notices if status is recommended or compatible" do + plugin_status[:status] = 'compatible' + plugin_status[:status_changed_at] = Time.now + stub_request(:get, described_class.plugin_status_url).to_return(status: 200, body: { status: plugin_status }.to_json) + described_class.update(skip_subscription: true) + + notice = described_class.list(described_class.types[:warning]).first + expect(notice.expired?).to eq(true) + end + end + + it "lists notices not expired more than a day ago" do + subscription_message[:expired_at] = Time.now - 8.hours + stub_request(:get, described_class.subscription_messages_url).to_return(status: 200, body: { messages: [subscription_message] }.to_json) + stub_request(:get, described_class.plugin_status_url).to_return(status: 200, body: { status: plugin_status }.to_json) + + described_class.update + expect(described_class.list.length).to eq(2) + end +end \ No newline at end of file diff --git a/spec/components/custom_wizard/pro_spec.rb b/spec/components/custom_wizard/subscription_spec.rb similarity index 54% rename from spec/components/custom_wizard/pro_spec.rb rename to spec/components/custom_wizard/subscription_spec.rb index 6499b668..47fb2d3f 100644 --- a/spec/components/custom_wizard/pro_spec.rb +++ b/spec/components/custom_wizard/subscription_spec.rb @@ -2,69 +2,69 @@ require_relative '../../plugin_helper' -describe CustomWizard::Pro do +describe CustomWizard::Subscription do fab!(:user) { Fabricate(:user) } - it "initializes pro authentication and subscription" do - pro = described_class.new - expect(pro.authentication.class).to eq(CustomWizard::ProAuthentication) - expect(pro.subscription.class).to eq(CustomWizard::ProSubscription) + it "initializes subscription authentication and subscription" do + subscription = described_class.new + expect(subscription.authentication.class).to eq(CustomWizard::Subscription::Authentication) + expect(subscription.subscription.class).to eq(CustomWizard::Subscription::Subscription) end it "returns authorized and subscribed states" do - pro = described_class.new - expect(pro.authorized?).to eq(false) - expect(pro.subscribed?).to eq(false) + subscription = described_class.new + expect(subscription.authorized?).to eq(false) + expect(subscription.subscribed?).to eq(false) end context "subscription" do before do - @pro = described_class.new + @subscription = described_class.new end it "updates valid subscriptions" do stub_subscription_request(200, valid_subscription) - expect(@pro.update_subscription).to eq(true) - expect(@pro.subscribed?).to eq(true) + expect(@subscription.update).to eq(true) + expect(@subscription.subscribed?).to eq(true) end it "handles invalid subscriptions" do stub_subscription_request(200, invalid_subscription) - expect(@pro.update_subscription).to eq(false) - expect(@pro.subscribed?).to eq(false) + expect(@subscription.update).to eq(false) + expect(@subscription.subscribed?).to eq(false) end it "handles subscription http errors" do stub_subscription_request(404, {}) - expect(@pro.update_subscription).to eq(false) - expect(@pro.subscribed?).to eq(false) + expect(@subscription.update).to eq(false) + expect(@subscription.subscribed?).to eq(false) end it "destroys subscriptions" do stub_subscription_request(200, valid_subscription) - expect(@pro.update_subscription).to eq(true) - expect(@pro.destroy_subscription).to eq(true) - expect(@pro.subscribed?).to eq(false) + expect(@subscription.update).to eq(true) + expect(@subscription.destroy_subscription).to eq(true) + expect(@subscription.subscribed?).to eq(false) end it "has class aliases" do - authenticate_pro + authenticate_subscription stub_subscription_request(200, valid_subscription) - expect(described_class.update_subscription).to eq(true) + expect(described_class.update).to eq(true) expect(described_class.subscribed?).to eq(true) end end context "authentication" do before do - @pro = described_class.new + @subscription = described_class.new user.update!(admin: true) end it "generates a valid authentication request url" do request_id = SecureRandom.hex(32) - uri = URI(@pro.authentication_url(user.id, request_id)) - expect(uri.host).to eq(@pro.server) + uri = URI(@subscription.authentication_url(user.id, request_id)) + expect(uri.host).to eq(@subscription.server) parsed_query = Rack::Utils.parse_query uri.query expect(parsed_query['public_key'].present?).to eq(true) @@ -72,12 +72,12 @@ describe CustomWizard::Pro do expect(parsed_query['client_id'].present?).to eq(true) expect(parsed_query['auth_redirect'].present?).to eq(true) expect(parsed_query['application_name']).to eq(SiteSetting.title) - expect(parsed_query['scopes']).to eq(@pro.scope) + expect(parsed_query['scopes']).to eq(@subscription.scope) end def generate_payload(request_id, user_id) - uri = URI(@pro.authentication_url(user_id, request_id)) - keys = @pro.authentication.get_keys(request_id) + uri = URI(@subscription.authentication_url(user_id, request_id)) + keys = @subscription.authentication.get_keys(request_id) raw_payload = { key: "12345", nonce: keys.nonce, @@ -92,8 +92,8 @@ describe CustomWizard::Pro do request_id = SecureRandom.hex(32) payload = generate_payload(request_id, user.id) - expect(@pro.authentication_response(request_id, payload)).to eq(true) - expect(@pro.authorized?).to eq(true) + expect(@subscription.authentication_response(request_id, payload)).to eq(true) + expect(@subscription.authorized?).to eq(true) end it "discards authentication response if user who made request as not an admin" do @@ -102,24 +102,24 @@ describe CustomWizard::Pro do request_id = SecureRandom.hex(32) payload = generate_payload(request_id, user.id) - expect(@pro.authentication_response(request_id, payload)).to eq(false) - expect(@pro.authorized?).to eq(false) + expect(@subscription.authentication_response(request_id, payload)).to eq(false) + expect(@subscription.authorized?).to eq(false) end it "discards authentication response if request_id is invalid" do payload = generate_payload(SecureRandom.hex(32), user.id) - expect(@pro.authentication_response(SecureRandom.hex(32), payload)).to eq(false) - expect(@pro.authorized?).to eq(false) + expect(@subscription.authentication_response(SecureRandom.hex(32), payload)).to eq(false) + expect(@subscription.authorized?).to eq(false) end it "destroys authentication" do request_id = SecureRandom.hex(32) payload = generate_payload(request_id, user.id) - @pro.authentication_response(request_id, payload) + @subscription.authentication_response(request_id, payload) - expect(@pro.destroy_authentication).to eq(true) - expect(@pro.authorized?).to eq(false) + expect(@subscription.destroy_authentication).to eq(true) + expect(@subscription.authorized?).to eq(false) end end end diff --git a/spec/extensions/custom_field_extensions_spec.rb b/spec/extensions/custom_field_extensions_spec.rb index 7ada17a4..7bba27a0 100644 --- a/spec/extensions/custom_field_extensions_spec.rb +++ b/spec/extensions/custom_field_extensions_spec.rb @@ -10,7 +10,7 @@ describe "custom field extensions" do fab!(:user) { Fabricate(:user) } let(:custom_field_json) { get_wizard_fixture("custom_field/custom_fields") } - let(:pro_custom_field_json) { get_wizard_fixture("custom_field/pro_custom_fields") } + let(:subscription_custom_field_json) { get_wizard_fixture("custom_field/subscription_custom_fields") } before do custom_field_json['custom_fields'].each do |field_json| @@ -72,11 +72,11 @@ describe "custom field extensions" do end end - context "pro custom fields" do + context "subscription custom fields" do before do enable_subscription - pro_custom_field_json['custom_fields'].each do |field_json| + subscription_custom_field_json['custom_fields'].each do |field_json| custom_field = CustomWizard::CustomField.new(nil, field_json) custom_field.save end diff --git a/spec/fixtures/custom_field/pro_custom_fields.json b/spec/fixtures/custom_field/subscription_custom_fields.json similarity index 100% rename from spec/fixtures/custom_field/pro_custom_fields.json rename to spec/fixtures/custom_field/subscription_custom_fields.json diff --git a/spec/jobs/update_notices_spec.rb b/spec/jobs/update_notices_spec.rb new file mode 100644 index 00000000..d0e5a468 --- /dev/null +++ b/spec/jobs/update_notices_spec.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +require_relative '../plugin_helper' + +describe Jobs::CustomWizardUpdateNotices do + let(:subscription_message) { + { + message: "Message about subscription", + type: "info", + created_at: Time.now - 3.day, + expired_at: nil + } + } + let(:plugin_status) { + { + name: 'discourse-custom-wizard', + status: 'incompatible', + status_changed_at: Time.now - 3.day + } + } + + it "updates the notices" do + stub_request(:get, CustomWizard::Notice.subscription_messages_url).to_return(status: 200, body: { messages: [subscription_message] }.to_json) + stub_request(:get, CustomWizard::Notice.plugin_status_url).to_return(status: 200, body: { status: plugin_status }.to_json) + + described_class.new.execute + expect(CustomWizard::Notice.list.length).to eq(2) + end +end diff --git a/spec/jobs/update_pro_subscription_spec.rb b/spec/jobs/update_subscription_spec.rb similarity index 52% rename from spec/jobs/update_pro_subscription_spec.rb rename to spec/jobs/update_subscription_spec.rb index 0aae9668..c5adaf38 100644 --- a/spec/jobs/update_pro_subscription_spec.rb +++ b/spec/jobs/update_subscription_spec.rb @@ -2,10 +2,10 @@ require_relative '../plugin_helper' -describe CustomWizard::UpdateProSubscription do - it "updates the pro subscription" do +describe Jobs::CustomWizardUpdateSubscription do + it "updates the subscription" do stub_subscription_request(200, valid_subscription) described_class.new.execute - expect(CustomWizard::Pro.subscribed?).to eq(true) + expect(CustomWizard::Subscription.subscribed?).to eq(true) end end diff --git a/spec/plugin_helper.rb b/spec/plugin_helper.rb index 348e7791..d47c47c1 100644 --- a/spec/plugin_helper.rb +++ b/spec/plugin_helper.rb @@ -24,16 +24,16 @@ def get_wizard_fixture(path) ).with_indifferent_access end -def authenticate_pro - CustomWizard::ProAuthentication.any_instance.stubs(:active?).returns(true) +def authenticate_subscription + CustomWizard::Subscription::Authentication.any_instance.stubs(:active?).returns(true) end def enable_subscription - CustomWizard::Pro.any_instance.stubs(:subscribed?).returns(true) + CustomWizard::Subscription.any_instance.stubs(:subscribed?).returns(true) end -def disable_pro - CustomWizard::Pro.any_instance.stubs(:subscribed?).returns(false) +def disable_subscription + CustomWizard::Subscription.any_instance.stubs(:subscribed?).returns(false) end def valid_subscription @@ -52,7 +52,7 @@ def invalid_subscription end def stub_subscription_request(status, subscription) - authenticate_pro - pro = CustomWizard::Pro.new - stub_request(:get, "https://#{pro.server}/subscription-server/user-subscriptions/#{pro.subscription_type}/#{pro.client_name}").to_return(status: status, body: { subscriptions: [subscription] }.to_json) + authenticate_subscription + sub = CustomWizard::Subscription.new + stub_request(:get, "https://#{sub.server}/subscription-server/user-subscriptions/#{sub.subscription_type}/#{sub.client_name}").to_return(status: status, body: { subscriptions: [subscription] }.to_json) end diff --git a/spec/requests/custom_wizard/admin/notice_controller_spec.rb b/spec/requests/custom_wizard/admin/notice_controller_spec.rb new file mode 100644 index 00000000..bd174e90 --- /dev/null +++ b/spec/requests/custom_wizard/admin/notice_controller_spec.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true +require_relative '../../../plugin_helper' + +describe CustomWizard::AdminNoticeController do + fab!(:admin_user) { Fabricate(:user, admin: true) } + + before do + sign_in(admin_user) + @notice = CustomWizard::Notice.new( + message: "Message about subscription", + type: "info", + created_at: Time.now - 3.day, + expired_at: nil + ) + @notice.save + end + + it "lists notices" do + get "/admin/wizards/notice.json" + expect(response.status).to eq(200) + expect(response.parsed_body.length).to eq(1) + end + + it "dismisses notices" do + put "/admin/wizards/notice/#{@notice.id}.json" + expect(response.status).to eq(200) + + updated = CustomWizard::Notice.find(@notice.id) + expect(updated.dismissed?).to eq(true) + end +end diff --git a/spec/requests/custom_wizard/admin/pro_controller_spec.rb b/spec/requests/custom_wizard/admin/subscription_controller_spec.rb similarity index 53% rename from spec/requests/custom_wizard/admin/pro_controller_spec.rb rename to spec/requests/custom_wizard/admin/subscription_controller_spec.rb index 563572bd..2f8aad20 100644 --- a/spec/requests/custom_wizard/admin/pro_controller_spec.rb +++ b/spec/requests/custom_wizard/admin/subscription_controller_spec.rb @@ -1,12 +1,12 @@ # frozen_string_literal: true require_relative '../../../plugin_helper' -describe CustomWizard::AdminProController do +describe CustomWizard::AdminSubscriptionController do fab!(:admin_user) { Fabricate(:user, admin: true) } def generate_payload(request_id, user_id) - uri = URI(@pro.authentication_url(user_id, request_id)) - keys = @pro.authentication.get_keys(request_id) + uri = URI(@subscription.authentication_url(user_id, request_id)) + keys = @subscription.authentication.get_keys(request_id) raw_payload = { key: "12345", nonce: keys.nonce, @@ -18,19 +18,19 @@ describe CustomWizard::AdminProController do end before do - @pro = CustomWizard::Pro.new + @subscription = CustomWizard::Subscription.new sign_in(admin_user) end it "#index" do - get "/admin/wizards/pro.json" - expect(response.parsed_body['server']).to eq(@pro.server) - expect(response.parsed_body['authentication'].deep_symbolize_keys).to eq(CustomWizard::ProAuthenticationSerializer.new(@pro.authentication, root: false).as_json) - expect(response.parsed_body['subscription'].deep_symbolize_keys).to eq(CustomWizard::ProSubscriptionSerializer.new(@pro.subscription, root: false).as_json) + get "/admin/wizards/subscription.json" + expect(response.parsed_body['server']).to eq(@subscription.server) + expect(response.parsed_body['authentication'].deep_symbolize_keys).to eq(CustomWizard::Subscription::AuthenticationSerializer.new(@subscription.authentication, root: false).as_json) + expect(response.parsed_body['subscription'].deep_symbolize_keys).to eq(CustomWizard::Subscription::SubscriptionSerializer.new(@subscription.subscription, root: false).as_json) end it "#authorize" do - get "/admin/wizards/pro/authorize" + get "/admin/wizards/subscription/authorize" expect(response.status).to eq(302) expect(cookies[:user_api_request_id].present?).to eq(true) end @@ -38,12 +38,12 @@ describe CustomWizard::AdminProController do it "#destroy_authentication" do request_id = SecureRandom.hex(32) payload = generate_payload(request_id, admin_user.id) - @pro.authentication_response(request_id, payload) + @subscription.authentication_response(request_id, payload) - delete "/admin/wizards/pro/authorize.json" + delete "/admin/wizards/subscription/authorize.json" expect(response.status).to eq(200) - expect(CustomWizard::Pro.authorized?).to eq(false) + expect(CustomWizard::Subscription.authorized?).to eq(false) end context "subscription" do @@ -54,18 +54,18 @@ describe CustomWizard::AdminProController do it "handles authentication response and the updates subscription" do request_id = cookies[:user_api_request_id] = SecureRandom.hex(32) payload = generate_payload(request_id, admin_user.id) - get "/admin/wizards/pro/authorize/callback", params: { payload: payload } + get "/admin/wizards/subscription/authorize/callback", params: { payload: payload } - expect(response).to redirect_to("/admin/wizards/pro") - expect(CustomWizard::Pro.subscribed?).to eq(true) + expect(response).to redirect_to("/admin/wizards/subscription") + expect(CustomWizard::Subscription.subscribed?).to eq(true) end it "updates the subscription" do - authenticate_pro - post "/admin/wizards/pro/subscription.json" + authenticate_subscription + post "/admin/wizards/subscription.json" expect(response.status).to eq(200) - expect(CustomWizard::Pro.subscribed?).to eq(true) + expect(CustomWizard::Subscription.subscribed?).to eq(true) end end end diff --git a/spec/requests/custom_wizard/custom_field_extensions_spec.rb b/spec/requests/custom_wizard/custom_field_extensions_spec.rb index 9ec3f5b1..775e3ee0 100644 --- a/spec/requests/custom_wizard/custom_field_extensions_spec.rb +++ b/spec/requests/custom_wizard/custom_field_extensions_spec.rb @@ -9,7 +9,7 @@ describe "custom field extensions" do let!(:user) { Fabricate(:user) } let!(:group) { Fabricate(:group, users: [user]) } let(:custom_field_json) { get_wizard_fixture("custom_field/custom_fields") } - let(:pro_custom_field_json) { get_wizard_fixture("custom_field/pro_custom_fields") } + let(:subscription_custom_field_json) { get_wizard_fixture("custom_field/subscription_custom_fields") } before do custom_field_json['custom_fields'].each do |field_json| @@ -38,11 +38,11 @@ describe "custom field extensions" do expect(response.parsed_body['post_field_1']).to eq(7) end - context "with a pro subscription" do + context "with a subscription" do before do enable_subscription - pro_custom_field_json['custom_fields'].each do |field_json| + subscription_custom_field_json['custom_fields'].each do |field_json| custom_field = CustomWizard::CustomField.new(nil, field_json) custom_field.save end diff --git a/spec/requests/custom_wizard/steps_controller_spec.rb b/spec/requests/custom_wizard/steps_controller_spec.rb index d4ad4042..8396135c 100644 --- a/spec/requests/custom_wizard/steps_controller_spec.rb +++ b/spec/requests/custom_wizard/steps_controller_spec.rb @@ -119,7 +119,7 @@ describe CustomWizard::StepsController do expect(response.parsed_body['final']).to eq(true) end - context "pro" do + context "subscription" do before do enable_subscription end @@ -149,6 +149,38 @@ describe CustomWizard::StepsController do expect(response.parsed_body['wizard']['start']).to eq("step_3") end + it "returns the correct final step when the conditional final step and last step are the same" do + new_template = wizard_template.dup + new_template['steps'][0]['condition'] = user_condition_template['condition'] + new_template['steps'][2]['condition'] = wizard_field_condition_template['condition'] + CustomWizard::Template.save(new_template, skip_jobs: true) + end + + it "raises an error when user cant see the step due to conditions" do + sign_in(user2) + + new_wizard_template = wizard_template.dup + new_wizard_template['steps'][0]['condition'] = user_condition_template['condition'] + CustomWizard::Template.save(new_wizard_template, skip_jobs: true) + + put '/w/super-mega-fun-wizard/steps/step_1.json' + expect(response.status).to eq(403) + end + + it "returns an updated wizard when condition doesnt pass" do + new_template = wizard_template.dup + new_template['steps'][1]['condition'] = wizard_field_condition_template['condition'] + CustomWizard::Template.save(new_template, skip_jobs: true) + + put '/w/super-mega-fun-wizard/steps/step_1.json', params: { + fields: { + step_1_field_1: "Condition wont pass" + } + } + expect(response.status).to eq(200) + expect(response.parsed_body['wizard']['start']).to eq("step_3") + end + it "returns the correct final step when the conditional final step and last step are the same" do new_template = wizard_template.dup new_template['steps'][0]['condition'] = user_condition_template['condition'] diff --git a/spec/serializers/custom_wizard/notice_serializer_spec.rb b/spec/serializers/custom_wizard/notice_serializer_spec.rb new file mode 100644 index 00000000..5184d890 --- /dev/null +++ b/spec/serializers/custom_wizard/notice_serializer_spec.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +require_relative '../../plugin_helper' + +describe CustomWizard::NoticeSerializer do + before do + @notice = CustomWizard::Notice.new( + message: "Message about subscription", + type: "info", + created_at: Time.now - 3.day, + expired_at: nil + ) + @notice.save + end + + it 'should return notice attributes' do + serialized_notice = described_class.new(@notice) + expect(serialized_notice.message).to eq(@notice.message) + expect(serialized_notice.type).to eq(CustomWizard::Notice.types.key(@notice.type)) + expect(serialized_notice.dismissable).to eq(true) + end +end diff --git a/spec/serializers/custom_wizard/pro_serializer_spec.rb b/spec/serializers/custom_wizard/pro_serializer_spec.rb deleted file mode 100644 index 45c1956e..00000000 --- a/spec/serializers/custom_wizard/pro_serializer_spec.rb +++ /dev/null @@ -1,14 +0,0 @@ -# frozen_string_literal: true - -require_relative '../../plugin_helper' - -describe CustomWizard::ProSerializer do - it 'should return pro attributes' do - pro = CustomWizard::Pro.new - serialized = described_class.new(pro, root: false) - - expect(serialized.server).to eq(pro.server) - expect(serialized.authentication.class).to eq(CustomWizard::ProAuthentication) - expect(serialized.subscription.class).to eq(CustomWizard::ProSubscription) - end -end diff --git a/spec/serializers/custom_wizard/pro/authentication_serializer_spec.rb b/spec/serializers/custom_wizard/subscription/authentication_serializer_spec.rb similarity index 60% rename from spec/serializers/custom_wizard/pro/authentication_serializer_spec.rb rename to spec/serializers/custom_wizard/subscription/authentication_serializer_spec.rb index 53dd74c2..ac29f95a 100644 --- a/spec/serializers/custom_wizard/pro/authentication_serializer_spec.rb +++ b/spec/serializers/custom_wizard/subscription/authentication_serializer_spec.rb @@ -2,11 +2,11 @@ require_relative '../../../plugin_helper' -describe CustomWizard::ProAuthenticationSerializer do +describe CustomWizard::Subscription::AuthenticationSerializer do fab!(:user) { Fabricate(:user) } - it 'should return pro authentication attributes' do - auth = CustomWizard::ProAuthentication.new(OpenStruct.new(key: '1234', auth_at: Time.now, auth_by: user.id)) + it 'should return subscription authentication attributes' do + auth = CustomWizard::Subscription::Authentication.new(OpenStruct.new(key: '1234', auth_at: Time.now, auth_by: user.id)) serialized = described_class.new(auth, root: false).as_json expect(serialized[:active]).to eq(true) diff --git a/spec/serializers/custom_wizard/pro/subscription_serializer_spec.rb b/spec/serializers/custom_wizard/subscription/subscription_serializer_spec.rb similarity index 57% rename from spec/serializers/custom_wizard/pro/subscription_serializer_spec.rb rename to spec/serializers/custom_wizard/subscription/subscription_serializer_spec.rb index a775863e..63caf363 100644 --- a/spec/serializers/custom_wizard/pro/subscription_serializer_spec.rb +++ b/spec/serializers/custom_wizard/subscription/subscription_serializer_spec.rb @@ -2,9 +2,9 @@ require_relative '../../../plugin_helper' -describe CustomWizard::ProSubscriptionSerializer do - it 'should return pro subscription attributes' do - sub = CustomWizard::ProSubscription.new(OpenStruct.new(type: 'community', updated_at: Time.now)) +describe CustomWizard::Subscription::SubscriptionSerializer do + it 'should return subscription attributes' do + sub = CustomWizard::Subscription::Subscription.new(OpenStruct.new(type: 'community', updated_at: Time.now)) serialized = described_class.new(sub, root: false).as_json expect(serialized[:active]).to eq(true) diff --git a/spec/serializers/custom_wizard/subscription_serializer_spec.rb b/spec/serializers/custom_wizard/subscription_serializer_spec.rb new file mode 100644 index 00000000..c6ea0ef2 --- /dev/null +++ b/spec/serializers/custom_wizard/subscription_serializer_spec.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +require_relative '../../plugin_helper' + +describe CustomWizard::SubscriptionSerializer do + it 'should return subscription attributes' do + subscription = CustomWizard::Subscription.new + serialized = described_class.new(subscription, root: false) + + expect(serialized.server).to eq(subscription.server) + expect(serialized.authentication.class).to eq(CustomWizard::Subscription::Authentication) + expect(serialized.subscription.class).to eq(CustomWizard::Subscription::Subscription) + end +end From c9453a0bdd76027194e31953265555c2feb11c93 Mon Sep 17 00:00:00 2001 From: merefield Date: Mon, 4 Oct 2021 14:40:17 +0100 Subject: [PATCH 047/160] API: remove GET method from API options --- .../discourse/controllers/admin-wizards-api-show.js.es6 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/javascripts/discourse/controllers/admin-wizards-api-show.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards-api-show.js.es6 index 5dba2d7f..7d3c1084 100644 --- a/assets/javascripts/discourse/controllers/admin-wizards-api-show.js.es6 +++ b/assets/javascripts/discourse/controllers/admin-wizards-api-show.js.es6 @@ -11,7 +11,7 @@ export default Controller.extend({ queryParams: ["refresh_list"], loadingSubscriptions: false, notAuthorized: not("api.authorized"), - endpointMethods: selectKitContent(["GET", "PUT", "POST", "PATCH", "DELETE"]), + endpointMethods: selectKitContent(["PUT", "POST", "PATCH", "DELETE"]), showRemove: not("isNew"), showRedirectUri: and("threeLeggedOauth", "api.name"), responseIcon: null, From a2ebd5c463f09e7bf9801f76a30d0853e113a11c Mon Sep 17 00:00:00 2001 From: merefield Date: Mon, 4 Oct 2021 18:51:47 +0100 Subject: [PATCH 048/160] API: don't present an API menu entry if not subscribed --- assets/javascripts/discourse/templates/admin-wizards.hbs | 2 +- controllers/custom_wizard/admin/admin.rb | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/assets/javascripts/discourse/templates/admin-wizards.hbs b/assets/javascripts/discourse/templates/admin-wizards.hbs index 5ab666c7..a9169d66 100644 --- a/assets/javascripts/discourse/templates/admin-wizards.hbs +++ b/assets/javascripts/discourse/templates/admin-wizards.hbs @@ -2,7 +2,7 @@ {{nav-item route="adminWizardsWizard" label="admin.wizard.nav_label"}} {{nav-item route="adminWizardsCustomFields" label="admin.wizard.custom_field.nav_label"}} {{nav-item route="adminWizardsSubmissions" label="admin.wizard.submissions.nav_label"}} - {{#if siteSettings.wizard_apis_enabled}} + {{#if subscribed}} {{nav-item route="adminWizardsApi" label="admin.wizard.api.nav_label"}} {{/if}} {{nav-item route="adminWizardsLogs" label="admin.wizard.log.nav_label"}} diff --git a/controllers/custom_wizard/admin/admin.rb b/controllers/custom_wizard/admin/admin.rb index ff01ddac..63f148b9 100644 --- a/controllers/custom_wizard/admin/admin.rb +++ b/controllers/custom_wizard/admin/admin.rb @@ -4,6 +4,7 @@ class CustomWizard::AdminController < ::Admin::AdminController def index render_json_dump( + subscribed: CustomWizard::Subscription.subscribed?, notices: ActiveModel::ArraySerializer.new( CustomWizard::Notice.list, each_serializer: CustomWizard::NoticeSerializer From 6056351b7c7da031b80a0e580d39b45c66733957 Mon Sep 17 00:00:00 2001 From: merefield Date: Tue, 5 Oct 2021 10:06:15 +0100 Subject: [PATCH 049/160] API: fix request call --- lib/custom_wizard/api/endpoint.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/custom_wizard/api/endpoint.rb b/lib/custom_wizard/api/endpoint.rb index 9dbbc590..b82dd39a 100644 --- a/lib/custom_wizard/api/endpoint.rb +++ b/lib/custom_wizard/api/endpoint.rb @@ -74,7 +74,7 @@ class CustomWizard::Api::Endpoint headers["Authorization"] = auth_string if auth_string headers["Content-Type"] = content_type if content_type - connection = Excon.new(UrlHelper.encode_and_parse(endpoint.url), headers: headers) + connection = Excon.new(URI.encode(endpoint.url), headers: headers) params = { method: endpoint.method } From 5a424e8e3dfdd00202a5dcd07da4dca96e8fc4df Mon Sep 17 00:00:00 2001 From: merefield Date: Tue, 5 Oct 2021 10:07:20 +0100 Subject: [PATCH 050/160] API: add mocked request spec --- spec/components/custom_wizard/action_spec.rb | 27 +++++++++++++++++++ spec/fixtures/api/no_authorization.json | 3 +++ spec/fixtures/endpoints/test_endpoint.json | 5 ++++ .../endpoints/test_endpoint_body.json | 3 +++ spec/plugin_helper.rb | 1 + 5 files changed, 39 insertions(+) create mode 100644 spec/fixtures/api/no_authorization.json create mode 100644 spec/fixtures/endpoints/test_endpoint.json create mode 100644 spec/fixtures/endpoints/test_endpoint_body.json diff --git a/spec/components/custom_wizard/action_spec.rb b/spec/components/custom_wizard/action_spec.rb index e34dd861..f5296a54 100644 --- a/spec/components/custom_wizard/action_spec.rb +++ b/spec/components/custom_wizard/action_spec.rb @@ -13,6 +13,9 @@ describe CustomWizard::Action do let(:add_to_group) { get_wizard_fixture("actions/add_to_group") } let(:send_message) { get_wizard_fixture("actions/send_message") } let(:send_message_multi) { get_wizard_fixture("actions/send_message_multi") } + let(:api_test_endpoint) { get_wizard_fixture("endpoints/test_endpoint") } + let(:api_test_endpoint_body) { get_wizard_fixture("endpoints/test_endpoint_body")} + let(:api_test_no_authorization) { get_wizard_fixture("api/no_authorization")} def update_template(template) CustomWizard::Template.save(template, skip_jobs: true) @@ -265,5 +268,29 @@ describe CustomWizard::Action do expect(group.users.first.username).to eq('angus') end + + it '#send_to_api' do + stub_request(:put, "https://myexternalapi.com/update"). + with( + body: "some_body", + headers: { + 'Host'=>'myexternalapi.com' + }). + to_return(status: 200, body: "success", headers: {}) + + new_api = CustomWizard::Api.new("my_api") + CustomWizard::Api.set("my_api", title: "Mocked external api") + CustomWizard::Api::Authorization.set("my_api", api_test_no_authorization) + CustomWizard::Api::Endpoint.new("my_api") + CustomWizard::Api::Endpoint.set("my_api", api_test_endpoint) + endpoint_id = CustomWizard::Api::Endpoint.list("my_api").first.id + + result = CustomWizard::Api::Endpoint.request("my_api", endpoint_id, "some_body") + log_entry = CustomWizard::Api::LogEntry.list("my_api").first + byebug + expect(result).to eq('success') + expect(log_entry.status).to eq('SUCCESS') + end end end + diff --git a/spec/fixtures/api/no_authorization.json b/spec/fixtures/api/no_authorization.json new file mode 100644 index 00000000..b1c6506e --- /dev/null +++ b/spec/fixtures/api/no_authorization.json @@ -0,0 +1,3 @@ +{ + "auth_type": "none" +} \ No newline at end of file diff --git a/spec/fixtures/endpoints/test_endpoint.json b/spec/fixtures/endpoints/test_endpoint.json new file mode 100644 index 00000000..ba979064 --- /dev/null +++ b/spec/fixtures/endpoints/test_endpoint.json @@ -0,0 +1,5 @@ +{ + "method": "PUT", + "url": "https://myexternalapi.com/update", + "success_codes": [200] +} \ No newline at end of file diff --git a/spec/fixtures/endpoints/test_endpoint_body.json b/spec/fixtures/endpoints/test_endpoint_body.json new file mode 100644 index 00000000..837e7c72 --- /dev/null +++ b/spec/fixtures/endpoints/test_endpoint_body.json @@ -0,0 +1,3 @@ +{ + "data": "some_data" +} \ No newline at end of file diff --git a/spec/plugin_helper.rb b/spec/plugin_helper.rb index d47c47c1..e72fe355 100644 --- a/spec/plugin_helper.rb +++ b/spec/plugin_helper.rb @@ -15,6 +15,7 @@ require 'oj' Oj.default_options = Oj.default_options.merge(cache_str: -1) require 'rails_helper' +require 'webmock/rspec' def get_wizard_fixture(path) JSON.parse( From 31e275668cd9d9d509876e28930fae20def9956d Mon Sep 17 00:00:00 2001 From: merefield Date: Tue, 5 Oct 2021 10:19:47 +0100 Subject: [PATCH 051/160] API: remove byebug --- spec/components/custom_wizard/action_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/components/custom_wizard/action_spec.rb b/spec/components/custom_wizard/action_spec.rb index f5296a54..4bd626ad 100644 --- a/spec/components/custom_wizard/action_spec.rb +++ b/spec/components/custom_wizard/action_spec.rb @@ -287,7 +287,7 @@ describe CustomWizard::Action do result = CustomWizard::Api::Endpoint.request("my_api", endpoint_id, "some_body") log_entry = CustomWizard::Api::LogEntry.list("my_api").first - byebug + expect(result).to eq('success') expect(log_entry.status).to eq('SUCCESS') end From 1f939c1ca039778daa88ba6f179fdd6986af8cda Mon Sep 17 00:00:00 2001 From: merefield Date: Tue, 5 Oct 2021 10:20:36 +0100 Subject: [PATCH 052/160] API: only show API menu on business sub --- assets/javascripts/discourse/templates/admin-wizards.hbs | 2 +- config/settings.yml | 3 --- controllers/custom_wizard/admin/admin.rb | 3 ++- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/assets/javascripts/discourse/templates/admin-wizards.hbs b/assets/javascripts/discourse/templates/admin-wizards.hbs index a9169d66..b72f5a7b 100644 --- a/assets/javascripts/discourse/templates/admin-wizards.hbs +++ b/assets/javascripts/discourse/templates/admin-wizards.hbs @@ -2,7 +2,7 @@ {{nav-item route="adminWizardsWizard" label="admin.wizard.nav_label"}} {{nav-item route="adminWizardsCustomFields" label="admin.wizard.custom_field.nav_label"}} {{nav-item route="adminWizardsSubmissions" label="admin.wizard.submissions.nav_label"}} - {{#if subscribed}} + {{#if api_subscription}} {{nav-item route="adminWizardsApi" label="admin.wizard.api.nav_label"}} {{/if}} {{nav-item route="adminWizardsLogs" label="admin.wizard.log.nav_label"}} diff --git a/config/settings.yml b/config/settings.yml index 0d93524d..d88dbaf5 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -15,6 +15,3 @@ plugins: refresh: true type: list list_type: compact - wizard_apis_enabled: - client: true - default: false \ No newline at end of file diff --git a/controllers/custom_wizard/admin/admin.rb b/controllers/custom_wizard/admin/admin.rb index 63f148b9..df11d25c 100644 --- a/controllers/custom_wizard/admin/admin.rb +++ b/controllers/custom_wizard/admin/admin.rb @@ -4,7 +4,8 @@ class CustomWizard::AdminController < ::Admin::AdminController def index render_json_dump( - subscribed: CustomWizard::Subscription.subscribed?, + #TODO replace with appropriate static? + api_subscription: ["business"].includes?(CustomWizard::Subscription.type), notices: ActiveModel::ArraySerializer.new( CustomWizard::Notice.list, each_serializer: CustomWizard::NoticeSerializer From 925c8c009a180230004ca042e22128499624bc17 Mon Sep 17 00:00:00 2001 From: angusmcleod Date: Tue, 5 Oct 2021 20:54:06 +0800 Subject: [PATCH 053/160] DEV: Add notice specs and UI updates --- .../components/subscription-container.js.es6 | 2 +- .../discourse/components/wizard-notice.js.es6 | 10 +- .../custom-wizard-important-notice.hbs | 3 + .../custom-wizard-important-notice.js.es6 | 16 +++ .../custom-wizard-issue-notice.hbs | 3 - .../custom-wizard-issue-notice.js.es6 | 12 -- .../admin-wizards-subscription.js.es6 | 2 +- .../initializers/custom-wizard-edits.js.es6 | 10 +- .../models/custom-wizard-notice.js.es6 | 4 +- .../components/subscription-container.hbs | 6 +- .../templates/components/wizard-notice.hbs | 20 ++- assets/stylesheets/admin/admin.scss | 20 ++- config/locales/client.en.yml | 6 +- config/locales/server.en.yml | 18 ++- config/settings.yml | 5 +- controllers/custom_wizard/admin/notice.rb | 4 +- .../scheduled/custom_wizard/update_notices.rb | 2 +- lib/custom_wizard/notice.rb | 132 +++++++++--------- lib/custom_wizard/notice/connection_error.rb | 82 +++++++++++ plugin.rb | 6 +- .../custom_wizard/notice_serializer.rb | 1 + spec/components/custom_wizard/notice_spec.rb | 69 +++++++-- .../sprockets/require_tree_discourse_empty.js | 2 +- 23 files changed, 306 insertions(+), 129 deletions(-) create mode 100644 assets/javascripts/discourse/connectors/admin-dashboard-top/custom-wizard-important-notice.hbs create mode 100644 assets/javascripts/discourse/connectors/admin-dashboard-top/custom-wizard-important-notice.js.es6 delete mode 100644 assets/javascripts/discourse/connectors/admin-dashboard-top/custom-wizard-issue-notice.hbs delete mode 100644 assets/javascripts/discourse/connectors/admin-dashboard-top/custom-wizard-issue-notice.js.es6 create mode 100644 lib/custom_wizard/notice/connection_error.rb diff --git a/assets/javascripts/discourse/components/subscription-container.js.es6 b/assets/javascripts/discourse/components/subscription-container.js.es6 index 08498f6f..620c6d88 100644 --- a/assets/javascripts/discourse/components/subscription-container.js.es6 +++ b/assets/javascripts/discourse/components/subscription-container.js.es6 @@ -18,4 +18,4 @@ export default Component.extend({ subscribedTitle(subscribed) { return `admin.wizard.subscription_container.${subscribed ? 'subscribed' : 'not_subscribed'}.title`; } -}) \ No newline at end of file +}); \ No newline at end of file diff --git a/assets/javascripts/discourse/components/wizard-notice.js.es6 b/assets/javascripts/discourse/components/wizard-notice.js.es6 index 86a82c94..15da3f35 100644 --- a/assets/javascripts/discourse/components/wizard-notice.js.es6 +++ b/assets/javascripts/discourse/components/wizard-notice.js.es6 @@ -1,10 +1,10 @@ import Component from "@ember/component"; import discourseComputed from "discourse-common/utils/decorators"; -import { notEmpty, not } from "@ember/object/computed"; +import { not, notEmpty } from "@ember/object/computed"; import I18n from "I18n"; export default Component.extend({ - classNameBindings: [':wizard-notice', 'notice.type', 'dismissed', 'expired'], + classNameBindings: [':wizard-notice', 'notice.type', 'dismissed', 'expired', 'resolved'], showFull: false, resolved: notEmpty('notice.expired_at'), dismissed: notEmpty('notice.dismissed_at'), @@ -18,14 +18,16 @@ export default Component.extend({ @discourseComputed('notice.type') icon(type) { return { - warning: 'exclamation-circle', + plugin_status_warning: 'exclamation-circle', + plugin_status_connection_error: 'bolt', + subscription_messages_connection_error: 'bolt', info: 'info-circle' }[type]; }, actions: { dismiss() { - this.set('dismissing', true) + this.set('dismissing', true); this.notice.dismiss().then(() => { this.set('dismissing', false); }); diff --git a/assets/javascripts/discourse/connectors/admin-dashboard-top/custom-wizard-important-notice.hbs b/assets/javascripts/discourse/connectors/admin-dashboard-top/custom-wizard-important-notice.hbs new file mode 100644 index 00000000..9b01c468 --- /dev/null +++ b/assets/javascripts/discourse/connectors/admin-dashboard-top/custom-wizard-important-notice.hbs @@ -0,0 +1,3 @@ +{{#if importantNotice}} + {{wizard-notice notice=importantNotice importantOnDashboard=true}} +{{/if}} diff --git a/assets/javascripts/discourse/connectors/admin-dashboard-top/custom-wizard-important-notice.js.es6 b/assets/javascripts/discourse/connectors/admin-dashboard-top/custom-wizard-important-notice.js.es6 new file mode 100644 index 00000000..43a2152b --- /dev/null +++ b/assets/javascripts/discourse/connectors/admin-dashboard-top/custom-wizard-important-notice.js.es6 @@ -0,0 +1,16 @@ +import { getOwner } from "discourse-common/lib/get-owner"; + +export default { + shouldRender(attrs, ctx) { + return ctx.siteSettings.wizard_important_notices_on_dashboard; + }, + + setupComponent() { + const controller = getOwner(this).lookup('controller:admin-dashboard'); + const importantNotice = controller.get('customWizardImportantNotice'); + + if (importantNotice) { + this.set('importantNotice', importantNotice); + } + } +}; \ No newline at end of file diff --git a/assets/javascripts/discourse/connectors/admin-dashboard-top/custom-wizard-issue-notice.hbs b/assets/javascripts/discourse/connectors/admin-dashboard-top/custom-wizard-issue-notice.hbs deleted file mode 100644 index a8aad815..00000000 --- a/assets/javascripts/discourse/connectors/admin-dashboard-top/custom-wizard-issue-notice.hbs +++ /dev/null @@ -1,3 +0,0 @@ -{{#if wizardWarningNotice}} - {{wizard-notice notice=wizardWarningNotice}} -{{/if}} \ No newline at end of file diff --git a/assets/javascripts/discourse/connectors/admin-dashboard-top/custom-wizard-issue-notice.js.es6 b/assets/javascripts/discourse/connectors/admin-dashboard-top/custom-wizard-issue-notice.js.es6 deleted file mode 100644 index b92e7897..00000000 --- a/assets/javascripts/discourse/connectors/admin-dashboard-top/custom-wizard-issue-notice.js.es6 +++ /dev/null @@ -1,12 +0,0 @@ -import { getOwner } from "discourse-common/lib/get-owner"; - -export default { - setupComponent() { - const controller = getOwner(this).lookup('controller:admin-dashboard') - const wizardWarningNotice = controller.get('wizardWarningNotice'); - - if (wizardWarningNotice) { - this.set('wizardWarningNotice', wizardWarningNotice); - } - } -} \ No newline at end of file diff --git a/assets/javascripts/discourse/controllers/admin-wizards-subscription.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards-subscription.js.es6 index 844d5a25..76f16119 100644 --- a/assets/javascripts/discourse/controllers/admin-wizards-subscription.js.es6 +++ b/assets/javascripts/discourse/controllers/admin-wizards-subscription.js.es6 @@ -38,7 +38,7 @@ export default Controller.extend({ unauthorize() { this.set("unauthorizing", true); - CustomWizardPro.unauthorize() + CustomWizardSubscription.unauthorize() .then((result) => { if (result.success) { this.setProperties({ diff --git a/assets/javascripts/discourse/initializers/custom-wizard-edits.js.es6 b/assets/javascripts/discourse/initializers/custom-wizard-edits.js.es6 index 4bf50fa9..f53dc2cd 100644 --- a/assets/javascripts/discourse/initializers/custom-wizard-edits.js.es6 +++ b/assets/javascripts/discourse/initializers/custom-wizard-edits.js.es6 @@ -1,6 +1,5 @@ import DiscourseURL from "discourse/lib/url"; import { withPluginApi } from "discourse/lib/plugin-api"; -import { ajax } from "discourse/lib/ajax"; import CustomWizardNotice from "../models/custom-wizard-notice"; import { A } from "@ember/array"; @@ -31,12 +30,13 @@ export default { }); }, - setupController(controller, model) { + setupController(controller) { if (this.notices) { - let warningNotices = this.notices.filter(n => n.type === 'warning'); + let pluginStatusConnectionError = this.notices.filter(n => n.type === 'plugin_status_connection_error')[0]; + let pluginStatusWarning = this.notices.filter(n => n.type === 'plugin_status_warning')[0]; - if (warningNotices.length) { - controller.set('wizardWarningNotice', warningNotices[0]); + if (pluginStatusConnectionError || pluginStatusWarning) { + controller.set('customWizardImportantNotice', pluginStatusConnectionError || pluginStatusWarning); } } diff --git a/assets/javascripts/discourse/models/custom-wizard-notice.js.es6 b/assets/javascripts/discourse/models/custom-wizard-notice.js.es6 index bae81822..a6b47c40 100644 --- a/assets/javascripts/discourse/models/custom-wizard-notice.js.es6 +++ b/assets/javascripts/discourse/models/custom-wizard-notice.js.es6 @@ -10,13 +10,13 @@ CustomWizardNotice.reopen({ if (result.success) { this.set('dismissed_at', result.dismissed_at); } - }).catch(popupAjaxError) + }).catch(popupAjaxError); } }); CustomWizardNotice.reopenClass({ list() { - return ajax('/admin/wizards/notice').catch(popupAjaxError) + return ajax('/admin/wizards/notice').catch(popupAjaxError); } }); diff --git a/assets/javascripts/discourse/templates/components/subscription-container.hbs b/assets/javascripts/discourse/templates/components/subscription-container.hbs index 7a02f555..8b012671 100644 --- a/assets/javascripts/discourse/templates/components/subscription-container.hbs +++ b/assets/javascripts/discourse/templates/components/subscription-container.hbs @@ -1,5 +1,5 @@ - {{/if}} - +
{{d-icon icon}} {{title}}
- {{d-icon "calendar-alt"}} + {{d-icon "far-clock"}} {{i18n "admin.wizard.notice.issued"}} {{format-date notice.created_at leaveAgo="true"}}
+ {{#if notice.updated_at}} +
+ {{d-icon "calendar-alt"}} + {{i18n "admin.wizard.notice.updated"}} + {{format-date notice.updated_at leaveAgo="true"}} +
+ {{/if}} +
{{d-icon "plug"}} {{i18n "admin.wizard.notice.plugin"}} @@ -28,10 +36,16 @@ {{{notice.message}}}
+{{#if importantOnDashboard}} +
+ {{i18n "admin.wizard.notice.disable_important_on_dashboard"}} + +{{/if}} + {{#if canDismiss}} {{#if dismissing}} {{loading-spinner size="small"}} {{else}} {{d-icon "times"}} {{/if}} -{{/if}} \ No newline at end of file +{{/if}} diff --git a/assets/stylesheets/admin/admin.scss b/assets/stylesheets/admin/admin.scss index 2eb32bf3..862f6761 100644 --- a/assets/stylesheets/admin/admin.scss +++ b/assets/stylesheets/admin/admin.scss @@ -911,13 +911,18 @@ padding: 1em; margin-bottom: 1em; border: 1px solid var(--primary); - border-radius: 4px; position: relative; &.dismissed { display: none; } + &.resolved .notice-badge:not(.notice-expired-at), + &.resolved a, + &.resolved p { + color: var(--primary-medium) !important; + } + .d-icon { margin-right: .4em; } @@ -931,7 +936,6 @@ display: inline-flex; align-items: center; padding: 0 .5em; - border-radius: 4px; margin-right: 1em; font-size: .9em; line-height: 25px; @@ -957,7 +961,8 @@ } } - .notice-issued { + .notice-issued, + .notice-resolved { margin-right: .3em; } @@ -976,6 +981,13 @@ position: absolute; top: 1em; right: 1em; - color: var(--primary); + color: var(--primary-medium); + } + + .disable-important { + position: absolute; + right: 3em; + top: 1em; + color: var(--primary-medium); } } diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index a63677c6..61e8274c 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -485,9 +485,13 @@ en: notice: plugin: Custom Wizard Plugin issued: Issued + update: Updated resolved: Resolved title: - warning: Warning Notice + plugin_status_warning: Warning Notice + plugin_status_connection_error: Connection Notice + subscription_messages_connection_error: Connection Notice + disable_important_on_dashboard: disable wizard_js: group: diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index 63996b02..5fd7c076 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -53,16 +53,22 @@ en: subscription: "%{type} %{property} is subscription only" notice: - connection_error: "Failed to connect to [%{server}](http://%{server})" + connection_error: "Failed to connect to http://%{domain}" compatibility_issue: > - The Custom Wizard Plugin may have a compatibility issue with the latest version of Discourse. - Please check the Custom Wizard Plugin status on [%{server}](http://%{server}) before updating Discourse. - plugin_status_connection_error_limit: > - We're unable to connect to the plugin status server to determine whether there are any compatibility issues with the latest version of Discourse. - Please contact support@thepavilion.io for further assistance. + The Custom Wizard Plugin has a compatibility issue with the latest version of Discourse. + Please check the Custom Wizard Plugin status on [%{domain}](http://%{domain}) before updating Discourse. + plugin_status: + connection_error_limit: > + We're unable to connect to the Pavilion Plugin Status Server. Please check the Custom Wizard Plugin status on [%{domain}](http://%{domain}) before updating Discourse or the plugin. + If this connection issue persists please contact support@thepavilion.io for further assistance. + subscription_messages: + connection_error_limit: > + We're unable to connect to the Pavilion Subscription Server. This will not affect the operation of the plugin. + If this connection issue persists please contact support@thepavilion.io for further assistance. site_settings: custom_wizard_enabled: "Enable custom wizards." wizard_redirect_exclude_paths: "Routes excluded from wizard redirects." wizard_recognised_image_upload_formats: "File types which will result in upload displaying an image preview" wizard_apis_enabled: "Enable API features (experimental)." + wizard_important_notices_on_dashboard: "Show important notices about the custom wizard plugin on the admin dashboard." diff --git a/config/settings.yml b/config/settings.yml index 0d93524d..d7b34aa9 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -17,4 +17,7 @@ plugins: list_type: compact wizard_apis_enabled: client: true - default: false \ No newline at end of file + default: false + wizard_important_notices_on_dashboard: + client: true + default: true \ No newline at end of file diff --git a/controllers/custom_wizard/admin/notice.rb b/controllers/custom_wizard/admin/notice.rb index bb332810..f28240e3 100644 --- a/controllers/custom_wizard/admin/notice.rb +++ b/controllers/custom_wizard/admin/notice.rb @@ -4,7 +4,7 @@ class CustomWizard::AdminNoticeController < CustomWizard::AdminController before_action :find_notice, only: [:dismiss] def index - render_serialized(CustomWizard::Notice.list, CustomWizard::NoticeSerializer) + render_serialized(CustomWizard::Notice.list(include_recently_expired: true), CustomWizard::NoticeSerializer) end def dismiss @@ -19,4 +19,4 @@ class CustomWizard::AdminNoticeController < CustomWizard::AdminController @notice = CustomWizard::Notice.find(params[:notice_id]) raise Discourse::InvalidParameters.new(:notice_id) unless @notice end -end \ No newline at end of file +end diff --git a/jobs/scheduled/custom_wizard/update_notices.rb b/jobs/scheduled/custom_wizard/update_notices.rb index 5194e2b8..25ebdf3b 100644 --- a/jobs/scheduled/custom_wizard/update_notices.rb +++ b/jobs/scheduled/custom_wizard/update_notices.rb @@ -6,4 +6,4 @@ class Jobs::CustomWizardUpdateNotices < ::Jobs::Scheduled def execute(args = {}) CustomWizard::Notice.update end -end \ No newline at end of file +end diff --git a/lib/custom_wizard/notice.rb b/lib/custom_wizard/notice.rb index 096cc579..335ccbd4 100644 --- a/lib/custom_wizard/notice.rb +++ b/lib/custom_wizard/notice.rb @@ -3,7 +3,14 @@ class CustomWizard::Notice include ActiveModel::Serialization + PLUGIN_STATUS_DOMAINS = { + "tests-passed" => "try.thepavilion.io", + "stable" => "stable.try.thepavilion.io" + } + SUBSCRIPTION_MESSAGES_DOMAIN = "thepavilion.io" + LOCALHOST_DOMAIN = "localhost:3000" PLUGIN_STATUSES_TO_WARN = %w(incompatible tests_failing) + CHECK_PLUGIN_STATUS_ON_BRANCH = %w(tests-passed main stable) attr_reader :id, :message, @@ -11,6 +18,7 @@ class CustomWizard::Notice :created_at attr_accessor :retrieved_at, + :updated_at, :dismissed_at, :expired_at @@ -19,6 +27,7 @@ class CustomWizard::Notice @message = attrs[:message] @type = attrs[:type].to_i @created_at = attrs[:created_at] + @updated_at = attrs[:updated_at] @retrieved_at = attrs[:retrieved_at] @dismissed_at = attrs[:dismissed_at] @expired_at = attrs[:expired_at] @@ -52,7 +61,6 @@ class CustomWizard::Notice attrs = { expired_at: expired_at, created_at: created_at, - expired_at: expired_at, message: message, type: type } @@ -67,14 +75,9 @@ class CustomWizard::Notice def self.types @types ||= Enum.new( info: 0, - warning: 1 - ) - end - - def self.connection_types - @connection_types ||= Enum.new( - plugin_status: 0, - subscription: 1 + plugin_status_warning: 1, + plugin_status_connection_error: 2, + subscription_messages_connection_error: 3 ) end @@ -82,23 +85,20 @@ class CustomWizard::Notice notices = [] if !skip_subscription - subscription_messages = request(subscription_messages_url) + subscription_messages = request(:subscription_messages) + if subscription_messages.present? subscription_notices = convert_subscription_messages_to_notices(subscription_messages[:messages]) notices.push(*subscription_notices) end end - if !skip_plugin && (Discourse.git_branch === 'tests-passed' || (Rails.env.test? || Rails.env.development?)) - plugin_status = request(plugin_status_url) + if !skip_plugin && request_plugin_status? + plugin_status = request(:plugin_status) if plugin_status.present? && plugin_status[:status].present? && plugin_status[:status].is_a?(Hash) plugin_notice = convert_plugin_status_to_notice(plugin_status[:status]) notices.push(plugin_notice) if plugin_notice - - expire_connection_errors(connection_types[:plugin_status]) - else - create_connection_error(connection_types[:plugin_status]) end end @@ -107,14 +107,6 @@ class CustomWizard::Notice notice.retrieved_at = Time.now notice.save end - - if reached_connection_error_limit(connection_types[:plugin_status]) - new( - message: I18n.t("wizard.notice.plugin_status_connection_error_limit"), - type: types[:warning], - created_at: Time.now - ) - end end def self.convert_subscription_messages_to_notices(messages) @@ -133,19 +125,46 @@ class CustomWizard::Notice if PLUGIN_STATUSES_TO_WARN.include?(plugin_status[:status]) notice = { - message: PrettyText.cook(I18n.t('wizard.notice.compatibility_issue', server: plugin_status_domain)), - type: types[:warning], + message: PrettyText.cook(I18n.t('wizard.notice.compatibility_issue', domain: plugin_status_domain)), + type: types[:plugin_status_warning], created_at: plugin_status[:status_changed_at] } else - list(types[:warning]).each(&:expire) + expire_notices(types[:plugin_status_warning]) end notice end + def self.notify_connection_errors(connection_type_key) + domain = self.send("#{connection_type_key.to_s}_domain") + message = PrettyText.cook(I18n.t("wizard.notice.#{connection_type_key.to_s}.connection_error_limit", domain: domain)) + notices = list(type: types[:connection_error], message: message) + + if notices.any? + notice = notices.first + notice.updated_at = Time.now + notice.save + else + notice = new( + message: message, + type: types["#{connection_type_key}_connection_error".to_sym], + created_at: Time.now + ) + notice.save + end + end + + def self.expire_notices(type) + list(type: type).each(&:expire) + end + + def self.request_plugin_status? + CHECK_PLUGIN_STATUS_ON_BRANCH.include?(Discourse.git_branch) || Rails.env.test? || Rails.env.development? + end + def self.subscription_messages_domain - "localhost:3000" + (Rails.env.test? || Rails.env.development?) ? LOCALHOST_DOMAIN : SUBSCRIPTION_MESSAGES_DOMAIN end def self.subscription_messages_url @@ -153,25 +172,34 @@ class CustomWizard::Notice end def self.plugin_status_domain - "localhost:4200" + return LOCALHOST_DOMAIN if (Rails.env.test? || Rails.env.development?) + PLUGIN_STATUS_DOMAINS[Discourse.git_branch] end def self.plugin_status_url "http://#{plugin_status_domain}/plugin-manager/status/discourse-custom-wizard" end - - def self.request(url) + + def self.request(type) + url = self.send("#{type.to_s}_url") response = Excon.get(url) + connection_error = CustomWizard::Notice::ConnectionError.new(type) if response.status == 200 + connection_error.expire! + expire_notices(types["#{type}_connection_error".to_sym]) + begin data = JSON.parse(response.body).deep_symbolize_keys rescue JSON::ParserError return nil end - + data else + connection_error.create! + notify_connection_errors(type) if connection_error.reached_limit? + nil end end @@ -180,10 +208,6 @@ class CustomWizard::Notice "#{CustomWizard::PLUGIN_NAME}_notice" end - def self.namespace_connection - "#{CustomWizard::PLUGIN_NAME}_notice_connection" - end - def self.find(id) raw = PluginStore.get(namespace, id) new(raw.symbolize_keys) if raw.present? @@ -193,42 +217,16 @@ class CustomWizard::Notice PluginStore.set(namespace, id, raw_notice) end - def self.plugin_status_connection_error_limit - 5 - end - - def self.list_connection_query(type) - query = PluginStoreRow.where(plugin_name: namespace_connection) - query.where("(value::json->>'type')::integer = ?", type) - end - - def self.expire_connection_errors(type) - list_connection_query(type).update_all("value = jsonb_set(value::jsonb, '{ expired_at }', (to_char(current_timestamp, 'HH12:MI:SS'))::jsonb)") - end - - def self.create_connection_error(type) - id = SecureRandom.hex(16) - attrs = { - message: I18n.t("wizard.notice.connection_error", domain: self.send("#{type}_domain")), - type: type, - created_at: Time.now - } - PluginStore.set(namespace_connection, id, attrs) - end - - def self.reached_connection_error_limit(type) - list_connection_query(type).size >= self.send("#{connection_types.key(type)}_connection_error_limit") - end - - def self.list_query(type = nil) + def self.list_query(type: nil, message: nil, include_recently_expired: false) query = PluginStoreRow.where(plugin_name: namespace) - query = query.where("(value::json->>'expired_at') IS NULL OR (value::json->>'expired_at')::date > now()::date - 1") + query = query.where("(value::json->>'expired_at') IS NULL#{include_recently_expired ? " OR (value::json->>'expired_at')::date > now()::date - 1" : ""}") query = query.where("(value::json->>'type')::integer = ?", type) if type + query = query.where("(value::json->>'message') = ?", message) if message query.order("value::json->>'created_at' DESC") end - def self.list(type = nil) - list_query(type) + def self.list(type: nil, message: nil, include_recently_expired: false) + list_query(type: type, message: message, include_recently_expired: include_recently_expired) .map { |r| self.new(JSON.parse(r.value).symbolize_keys) } end end diff --git a/lib/custom_wizard/notice/connection_error.rb b/lib/custom_wizard/notice/connection_error.rb new file mode 100644 index 00000000..84620c0d --- /dev/null +++ b/lib/custom_wizard/notice/connection_error.rb @@ -0,0 +1,82 @@ +# frozen_string_literal: true + +class CustomWizard::Notice::ConnectionError + + attr_reader :type_key + + def initialize(type_key) + @type_key = type_key + end + + def create! + id = "#{type_key.to_s}_error" + + if attrs = PluginStore.get(namespace, id) + attrs['updated_at'] = Time.now + attrs['count'] = attrs['count'].to_i + 1 + else + domain = CustomWizard::Notice.send("#{type_key.to_s}_domain") + attrs = { + message: I18n.t("wizard.notice.connection_error", domain: domain), + type: self.class.types[type_key], + created_at: Time.now, + count: 1 + } + end + + PluginStore.set(namespace, id, attrs) + + @errors = nil + end + + def expire! + if errors.exists? + errors.each do |error_row| + error = JSON.parse(error_row.value) + error['expired_at'] = Time.now + error_row.value = error + error_row.save + end + end + end + + def self.types + @types ||= Enum.new( + plugin_status: 0, + subscription_messages: 1 + ) + end + + def plugin_status_limit + 5 + end + + def subscription_messages_limit + 10 + end + + def limit + self.send("#{type_key.to_s}_limit") + end + + def reached_limit? + return false unless errors.exists? + current_error['count'].to_i >= limit + end + + def current_error + JSON.parse(errors.first.value) + end + + def namespace + "#{CustomWizard::PLUGIN_NAME}_notice_connection" + end + + def errors + @errors ||= begin + query = PluginStoreRow.where(plugin_name: namespace) + query = query.where("(value::json->>'type')::integer = ?", self.class.types[type_key]) + query.where("(value::json->>'expired_at') IS NULL") + end + end +end diff --git a/plugin.rb b/plugin.rb index c3063d29..97d5472b 100644 --- a/plugin.rb +++ b/plugin.rb @@ -40,7 +40,7 @@ if respond_to?(:register_svg_icon) register_svg_icon "comment-alt" register_svg_icon "far-life-ring" register_svg_icon "arrow-right" - register_svg_icon "shield-virus" + register_svg_icon "bolt" end class ::Sprockets::DirectiveProcessor @@ -98,6 +98,7 @@ after_initialize do ../lib/custom_wizard/template.rb ../lib/custom_wizard/wizard.rb ../lib/custom_wizard/notice.rb + ../lib/custom_wizard/notice/connection_error.rb ../lib/custom_wizard/subscription.rb ../lib/custom_wizard/subscription/subscription.rb ../lib/custom_wizard/subscription/authentication.rb @@ -242,10 +243,9 @@ after_initialize do end AdminDashboardData.add_problem_check do - warning_notices = CustomWizard::Notice.list(CustomWizard::Notice.types[:warning]) + warning_notices = CustomWizard::Notice.list(type: CustomWizard::Notice.types[:plugin_status_warning]) warning_notices.any? ? ActionView::Base.full_sanitizer.sanitize(warning_notices.first.message, tags: %w(a)) : nil end - Jobs.enqueue(:custom_wizard_update_notices) DiscourseEvent.trigger(:custom_wizard_ready) end diff --git a/serializers/custom_wizard/notice_serializer.rb b/serializers/custom_wizard/notice_serializer.rb index 310827f7..48b7b381 100644 --- a/serializers/custom_wizard/notice_serializer.rb +++ b/serializers/custom_wizard/notice_serializer.rb @@ -6,6 +6,7 @@ class CustomWizard::NoticeSerializer < ApplicationSerializer :type, :created_at, :expired_at, + :updated_at, :dismissed_at, :retrieved_at, :dismissable diff --git a/spec/components/custom_wizard/notice_spec.rb b/spec/components/custom_wizard/notice_spec.rb index 373d2e31..ef29ea21 100644 --- a/spec/components/custom_wizard/notice_spec.rb +++ b/spec/components/custom_wizard/notice_spec.rb @@ -33,13 +33,13 @@ describe CustomWizard::Notice do expect(notice.message).to eq(subscription_message[:message]) expect(notice.created_at.to_datetime).to be_within(1.second).of (subscription_message[:created_at].to_datetime) end - + it "expires notice if subscription message is expired" do subscription_message[:expired_at] = Time.now stub_request(:get, described_class.subscription_messages_url).to_return(status: 200, body: { messages: [subscription_message] }.to_json) described_class.update(skip_plugin: true) - notice = described_class.list.first + notice = described_class.list(include_recently_expired: true).first expect(notice.expired?).to eq(true) end end @@ -51,20 +51,20 @@ describe CustomWizard::Notice do described_class.update(skip_subscription: true) end - it "converts plugin statuses to warn into notices" do + it "converts warning into notice" do notice = described_class.list.first - expect(notice.type).to eq(described_class.types[:warning]) + expect(notice.type).to eq(described_class.types[:plugin_status_warning]) expect(notice.message).to eq(PrettyText.cook(I18n.t("wizard.notice.compatibility_issue", server: described_class.plugin_status_domain))) expect(notice.created_at.to_datetime).to be_within(1.second).of (plugin_status[:status_changed_at].to_datetime) end - - it "expires unexpired warning notices if status is recommended or compatible" do + + it "expires warning notices if status is recommended or compatible" do plugin_status[:status] = 'compatible' plugin_status[:status_changed_at] = Time.now stub_request(:get, described_class.plugin_status_url).to_return(status: 200, body: { status: plugin_status }.to_json) described_class.update(skip_subscription: true) - notice = described_class.list(described_class.types[:warning]).first + notice = described_class.list(type: described_class.types[:plugin_status_warning], include_recently_expired: true).first expect(notice.expired?).to eq(true) end end @@ -75,6 +75,57 @@ describe CustomWizard::Notice do stub_request(:get, described_class.plugin_status_url).to_return(status: 200, body: { status: plugin_status }.to_json) described_class.update - expect(described_class.list.length).to eq(2) + expect(described_class.list(include_recently_expired: true).length).to eq(2) end -end \ No newline at end of file + + context "connection errors" do + before do + freeze_time + end + + it "creates an error if connection to notice server fails" do + stub_request(:get, described_class.plugin_status_url).to_return(status: 400, body: { status: plugin_status }.to_json) + described_class.update(skip_subscription: true) + + error = CustomWizard::Notice::ConnectionError.new(:plugin_status) + expect(error.errors.exists?).to eq(true) + end + + it "only creates one connection error per type at a time" do + stub_request(:get, described_class.subscription_messages_url).to_return(status: 400, body: { messages: [subscription_message] }.to_json) + stub_request(:get, described_class.plugin_status_url).to_return(status: 400, body: { status: plugin_status }.to_json) + + 5.times { described_class.update } + + plugin_status_errors = CustomWizard::Notice::ConnectionError.new(:plugin_status) + subscription_message_errors = CustomWizard::Notice::ConnectionError.new(:subscription_messages) + + expect(plugin_status_errors.errors.length).to eq(1) + expect(subscription_message_errors.errors.length).to eq(1) + end + + it "creates a connection error notice if connection errors reach limit" do + stub_request(:get, described_class.plugin_status_url).to_return(status: 400, body: { status: plugin_status }.to_json) + + error = CustomWizard::Notice::ConnectionError.new(:plugin_status) + error.limit.times { described_class.update(skip_subscription: true) } + notice = described_class.list(type: described_class.types[:plugin_status_connection_error]).first + + expect(error.current_error['count']).to eq(error.limit) + expect(notice.type).to eq(described_class.types[:plugin_status_connection_error]) + end + + it "expires a connection error notice if connection succeeds" do + stub_request(:get, described_class.plugin_status_url).to_return(status: 400, body: { status: plugin_status }.to_json) + error = CustomWizard::Notice::ConnectionError.new(:plugin_status) + error.limit.times { described_class.update(skip_subscription: true) } + + stub_request(:get, described_class.plugin_status_url).to_return(status: 200, body: { status: plugin_status }.to_json) + described_class.update(skip_subscription: true) + notice = described_class.list(type: described_class.types[:plugin_status_connection_error], include_recently_expired: true).first + + expect(notice.type).to eq(described_class.types[:plugin_status_connection_error]) + expect(notice.expired_at.present?).to eq(true) + end + end +end diff --git a/spec/fixtures/sprockets/require_tree_discourse_empty.js b/spec/fixtures/sprockets/require_tree_discourse_empty.js index df264ec5..953c5ec4 100644 --- a/spec/fixtures/sprockets/require_tree_discourse_empty.js +++ b/spec/fixtures/sprockets/require_tree_discourse_empty.js @@ -1 +1 @@ -//= require_tree_discourse \ No newline at end of file +//= require_tree_discourse \ No newline at end of file From b475e39ee9698083ddf933ae6e4b0c31029bbaf5 Mon Sep 17 00:00:00 2001 From: angusmcleod Date: Thu, 7 Oct 2021 21:19:19 +0800 Subject: [PATCH 054/160] Fix specs --- coverage/.last_run.json | 2 +- lib/custom_wizard/notice.rb | 6 +++--- serializers/custom_wizard/notice_serializer.rb | 4 ++++ spec/components/custom_wizard/notice_spec.rb | 2 +- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/coverage/.last_run.json b/coverage/.last_run.json index 1c721888..a5de03be 100644 --- a/coverage/.last_run.json +++ b/coverage/.last_run.json @@ -1,5 +1,5 @@ { "result": { - "line": 92.3 + "line": 92.45 } } diff --git a/lib/custom_wizard/notice.rb b/lib/custom_wizard/notice.rb index 335ccbd4..8d41d634 100644 --- a/lib/custom_wizard/notice.rb +++ b/lib/custom_wizard/notice.rb @@ -125,7 +125,7 @@ class CustomWizard::Notice if PLUGIN_STATUSES_TO_WARN.include?(plugin_status[:status]) notice = { - message: PrettyText.cook(I18n.t('wizard.notice.compatibility_issue', domain: plugin_status_domain)), + message: I18n.t('wizard.notice.compatibility_issue', domain: plugin_status_domain), type: types[:plugin_status_warning], created_at: plugin_status[:status_changed_at] } @@ -138,7 +138,7 @@ class CustomWizard::Notice def self.notify_connection_errors(connection_type_key) domain = self.send("#{connection_type_key.to_s}_domain") - message = PrettyText.cook(I18n.t("wizard.notice.#{connection_type_key.to_s}.connection_error_limit", domain: domain)) + message = I18n.t("wizard.notice.#{connection_type_key.to_s}.connection_error_limit", domain: domain) notices = list(type: types[:connection_error], message: message) if notices.any? @@ -221,7 +221,7 @@ class CustomWizard::Notice query = PluginStoreRow.where(plugin_name: namespace) query = query.where("(value::json->>'expired_at') IS NULL#{include_recently_expired ? " OR (value::json->>'expired_at')::date > now()::date - 1" : ""}") query = query.where("(value::json->>'type')::integer = ?", type) if type - query = query.where("(value::json->>'message') = ?", message) if message + query = query.where("(value::json->>'message')::text = ?", message) if message query.order("value::json->>'created_at' DESC") end diff --git a/serializers/custom_wizard/notice_serializer.rb b/serializers/custom_wizard/notice_serializer.rb index 48b7b381..5564de1f 100644 --- a/serializers/custom_wizard/notice_serializer.rb +++ b/serializers/custom_wizard/notice_serializer.rb @@ -18,4 +18,8 @@ class CustomWizard::NoticeSerializer < ApplicationSerializer def type CustomWizard::Notice.types.key(object.type) end + + def messsage + PrettyText.cook(object.message) + end end diff --git a/spec/components/custom_wizard/notice_spec.rb b/spec/components/custom_wizard/notice_spec.rb index ef29ea21..b51305e1 100644 --- a/spec/components/custom_wizard/notice_spec.rb +++ b/spec/components/custom_wizard/notice_spec.rb @@ -54,7 +54,7 @@ describe CustomWizard::Notice do it "converts warning into notice" do notice = described_class.list.first expect(notice.type).to eq(described_class.types[:plugin_status_warning]) - expect(notice.message).to eq(PrettyText.cook(I18n.t("wizard.notice.compatibility_issue", server: described_class.plugin_status_domain))) + expect(notice.message).to eq(I18n.t("wizard.notice.compatibility_issue", domain: described_class.plugin_status_domain)) expect(notice.created_at.to_datetime).to be_within(1.second).of (plugin_status[:status_changed_at].to_datetime) end From 702ccc868c7d5b845937d5a97ff2405ac57d122e Mon Sep 17 00:00:00 2001 From: angusmcleod Date: Tue, 12 Oct 2021 09:45:50 +0800 Subject: [PATCH 055/160] Update connection_error.rb --- lib/custom_wizard/notice/connection_error.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/custom_wizard/notice/connection_error.rb b/lib/custom_wizard/notice/connection_error.rb index 84620c0d..8b8b944a 100644 --- a/lib/custom_wizard/notice/connection_error.rb +++ b/lib/custom_wizard/notice/connection_error.rb @@ -25,7 +25,6 @@ class CustomWizard::Notice::ConnectionError end PluginStore.set(namespace, id, attrs) - @errors = nil end @@ -34,7 +33,7 @@ class CustomWizard::Notice::ConnectionError errors.each do |error_row| error = JSON.parse(error_row.value) error['expired_at'] = Time.now - error_row.value = error + error_row.value = error.to_json error_row.save end end From 14e7e9c0db9718b259e5446065a81e4b83377370 Mon Sep 17 00:00:00 2001 From: Robert Barrow Date: Tue, 12 Oct 2021 09:54:52 +0100 Subject: [PATCH 056/160] improve api section switch --- assets/javascripts/discourse/routes/admin-wizards.js.es6 | 1 + assets/javascripts/discourse/templates/admin-wizards.hbs | 2 +- controllers/custom_wizard/admin/admin.rb | 2 +- lib/custom_wizard/subscription.rb | 8 ++++++++ lib/custom_wizard/subscription/subscription.rb | 2 +- 5 files changed, 12 insertions(+), 3 deletions(-) diff --git a/assets/javascripts/discourse/routes/admin-wizards.js.es6 b/assets/javascripts/discourse/routes/admin-wizards.js.es6 index da184d93..4e4e3c40 100644 --- a/assets/javascripts/discourse/routes/admin-wizards.js.es6 +++ b/assets/javascripts/discourse/routes/admin-wizards.js.es6 @@ -9,6 +9,7 @@ export default DiscourseRoute.extend({ setupController(controller, model) { controller.set('notices', A(model.notices)); + controller.set('api_section', model.api_section); }, afterModel(model, transition) { diff --git a/assets/javascripts/discourse/templates/admin-wizards.hbs b/assets/javascripts/discourse/templates/admin-wizards.hbs index b72f5a7b..aa3b2cf5 100644 --- a/assets/javascripts/discourse/templates/admin-wizards.hbs +++ b/assets/javascripts/discourse/templates/admin-wizards.hbs @@ -2,7 +2,7 @@ {{nav-item route="adminWizardsWizard" label="admin.wizard.nav_label"}} {{nav-item route="adminWizardsCustomFields" label="admin.wizard.custom_field.nav_label"}} {{nav-item route="adminWizardsSubmissions" label="admin.wizard.submissions.nav_label"}} - {{#if api_subscription}} + {{#if api_section}} {{nav-item route="adminWizardsApi" label="admin.wizard.api.nav_label"}} {{/if}} {{nav-item route="adminWizardsLogs" label="admin.wizard.log.nav_label"}} diff --git a/controllers/custom_wizard/admin/admin.rb b/controllers/custom_wizard/admin/admin.rb index df11d25c..8a3b69ac 100644 --- a/controllers/custom_wizard/admin/admin.rb +++ b/controllers/custom_wizard/admin/admin.rb @@ -5,7 +5,7 @@ class CustomWizard::AdminController < ::Admin::AdminController def index render_json_dump( #TODO replace with appropriate static? - api_subscription: ["business"].includes?(CustomWizard::Subscription.type), + api_section: ["business"].include?(CustomWizard::Subscription.type), notices: ActiveModel::ArraySerializer.new( CustomWizard::Notice.list, each_serializer: CustomWizard::NoticeSerializer diff --git a/lib/custom_wizard/subscription.rb b/lib/custom_wizard/subscription.rb index 11e6d4d3..6c535602 100644 --- a/lib/custom_wizard/subscription.rb +++ b/lib/custom_wizard/subscription.rb @@ -27,6 +27,10 @@ class CustomWizard::Subscription "stripe" end + def type + @subscription.type + end + def client_name "custom-wizard" end @@ -119,6 +123,10 @@ class CustomWizard::Subscription self.new.subscribed? end + def self.type + self.new.type + end + def self.authorized? self.new.authorized? end diff --git a/lib/custom_wizard/subscription/subscription.rb b/lib/custom_wizard/subscription/subscription.rb index 129f993a..b2ddf744 100644 --- a/lib/custom_wizard/subscription/subscription.rb +++ b/lib/custom_wizard/subscription/subscription.rb @@ -13,7 +13,7 @@ class CustomWizard::Subscription::Subscription end def types - %w(community business) + %w(core advanced business) end def active? From 67cfeb6ed95663809d0768e1adab3bb481bbbfed Mon Sep 17 00:00:00 2001 From: Robert Barrow Date: Tue, 12 Oct 2021 12:51:38 +0100 Subject: [PATCH 057/160] Distinguish actions which require additional subscription --- .../components/wizard-custom-action.js.es6 | 23 ++++++++-------- .../discourse/lib/wizard-schema.js.es6 | 27 +++++++++++++------ .../routes/admin-wizards-wizard-show.js.es6 | 1 + .../templates/admin-wizards-wizard-show.hbs | 3 ++- controllers/custom_wizard/admin/wizard.rb | 3 ++- 5 files changed, 36 insertions(+), 21 deletions(-) diff --git a/assets/javascripts/discourse/components/wizard-custom-action.js.es6 b/assets/javascripts/discourse/components/wizard-custom-action.js.es6 index ffe5b514..39c84fa2 100644 --- a/assets/javascripts/discourse/components/wizard-custom-action.js.es6 +++ b/assets/javascripts/discourse/components/wizard-custom-action.js.es6 @@ -1,5 +1,7 @@ import { default as discourseComputed } from "discourse-common/utils/decorators"; -import wizardSchema from "discourse/plugins/discourse-custom-wizard/discourse/lib/wizard-schema"; +import wizardSchema, { + actionsAvailableWithAdditionalSubscription, +} from "discourse/plugins/discourse-custom-wizard/discourse/lib/wizard-schema"; import { empty, equal, or } from "@ember/object/computed"; import { notificationLevels, selectKitContent } from "../lib/wizard"; import { computed } from "@ember/object"; @@ -94,17 +96,16 @@ export default Component.extend(UndoChanges, { return apis.find((a) => a.name === api).endpoints; }, - @discourseComputed("subscribed") - actionTypes(subscribed) { + @discourseComputed("subscribed", "subscription") + actionTypes(subscribed, subscription) { + let unsubscribedActions = + actionsAvailableWithAdditionalSubscription(subscription); return Object.keys(wizardSchema.action.types).reduce((result, type) => { - let subscription = wizardSchema.action.subscriptionTypes.includes(type); - if (subscribed || !subscription) { - result.push({ - id: type, - name: I18n.t(`admin.wizard.action.${type}.label`), - subscription, - }); - } + result.push({ + id: type, + name: I18n.t(`admin.wizard.action.${type}.label`), + subscribed: unsubscribedActions.includes(type), + }); return result; }, []); }, diff --git a/assets/javascripts/discourse/lib/wizard-schema.js.es6 b/assets/javascripts/discourse/lib/wizard-schema.js.es6 index 5d876ed0..f36110da 100644 --- a/assets/javascripts/discourse/lib/wizard-schema.js.es6 +++ b/assets/javascripts/discourse/lib/wizard-schema.js.es6 @@ -200,14 +200,10 @@ const action = { "create_group", "send_to_api", ], - required: ["id", "type"], - proTypes: [ - "send_message", - "add_to_group", - "create_category", - "create_group", - "send_to_api", - ], + actionTypesWithSubscription: { + advanced: ["send_message", "add_to_group", "watch_categories"], + business: ["create_category", "create_group", "send_to_api"], + }, dependent: {}, objectArrays: {}, }; @@ -219,6 +215,21 @@ const wizardSchema = { action, }; +export function actionsAvailableWithAdditionalSubscription( + currentSubscription +) { + switch (currentSubscription) { + case "business": + return []; + case "advanced": + return action.actionTypesWithSubscription["business"]; + case "community": + return action.actionTypesWithSubscription["advanced"].concat( + action.actionTypesWithSubscription["business"] + ); + } +} + export function buildFieldTypes(types) { wizardSchema.field.types = types; } diff --git a/assets/javascripts/discourse/routes/admin-wizards-wizard-show.js.es6 b/assets/javascripts/discourse/routes/admin-wizards-wizard-show.js.es6 index 046fc6d4..d6263471 100644 --- a/assets/javascripts/discourse/routes/admin-wizards-wizard-show.js.es6 +++ b/assets/javascripts/discourse/routes/admin-wizards-wizard-show.js.es6 @@ -40,6 +40,7 @@ export default DiscourseRoute.extend({ currentAction: wizard.actions[0], creating: model.create, subscribed: parentModel.subscribed, + subscription: parentModel.subscription, }; controller.setProperties(props); diff --git a/assets/javascripts/discourse/templates/admin-wizards-wizard-show.hbs b/assets/javascripts/discourse/templates/admin-wizards-wizard-show.hbs index 3966a50c..abf06ba9 100644 --- a/assets/javascripts/discourse/templates/admin-wizards-wizard-show.hbs +++ b/assets/javascripts/discourse/templates/admin-wizards-wizard-show.hbs @@ -178,7 +178,8 @@ apis=apis removeAction="removeAction" wizardFields=wizardFields - subscribed=subscribed}} + subscribed=subscribed + subscription=subscription}} {{/each}}
diff --git a/controllers/custom_wizard/admin/wizard.rb b/controllers/custom_wizard/admin/wizard.rb index fdf338bf..e6ea684a 100644 --- a/controllers/custom_wizard/admin/wizard.rb +++ b/controllers/custom_wizard/admin/wizard.rb @@ -11,7 +11,8 @@ class CustomWizard::AdminWizardController < CustomWizard::AdminController field_types: CustomWizard::Field.types, realtime_validations: CustomWizard::RealtimeValidation.types, custom_fields: custom_field_list, - subscribed: CustomWizard::Subscription.subscribed? + subscribed: CustomWizard::Subscription.subscribed?, + subscription: CustomWizard::Subscription.type ) end From d4e489456e61e39359c79d77834e094b371b2b70 Mon Sep 17 00:00:00 2001 From: Robert Barrow Date: Tue, 12 Oct 2021 13:01:39 +0100 Subject: [PATCH 058/160] rename functional levels --- assets/javascripts/discourse/lib/wizard-schema.js.es6 | 4 ++-- lib/custom_wizard/subscription/subscription.rb | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/assets/javascripts/discourse/lib/wizard-schema.js.es6 b/assets/javascripts/discourse/lib/wizard-schema.js.es6 index f36110da..34df21d2 100644 --- a/assets/javascripts/discourse/lib/wizard-schema.js.es6 +++ b/assets/javascripts/discourse/lib/wizard-schema.js.es6 @@ -219,11 +219,11 @@ export function actionsAvailableWithAdditionalSubscription( currentSubscription ) { switch (currentSubscription) { - case "business": + case "complete": return []; case "advanced": return action.actionTypesWithSubscription["business"]; - case "community": + case "core": return action.actionTypesWithSubscription["advanced"].concat( action.actionTypesWithSubscription["business"] ); diff --git a/lib/custom_wizard/subscription/subscription.rb b/lib/custom_wizard/subscription/subscription.rb index b2ddf744..84d21c9d 100644 --- a/lib/custom_wizard/subscription/subscription.rb +++ b/lib/custom_wizard/subscription/subscription.rb @@ -13,7 +13,7 @@ class CustomWizard::Subscription::Subscription end def types - %w(core advanced business) + %w(core advanced complete) end def active? From 3e2907abc22a30a9181e39644011c3edae1b01cc Mon Sep 17 00:00:00 2001 From: Robert Barrow Date: Tue, 12 Oct 2021 14:37:36 +0100 Subject: [PATCH 059/160] show api for complete sub --- controllers/custom_wizard/admin/admin.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/controllers/custom_wizard/admin/admin.rb b/controllers/custom_wizard/admin/admin.rb index 8a3b69ac..9f48e632 100644 --- a/controllers/custom_wizard/admin/admin.rb +++ b/controllers/custom_wizard/admin/admin.rb @@ -5,7 +5,7 @@ class CustomWizard::AdminController < ::Admin::AdminController def index render_json_dump( #TODO replace with appropriate static? - api_section: ["business"].include?(CustomWizard::Subscription.type), + api_section: ["complete"].include?(CustomWizard::Subscription.type), notices: ActiveModel::ArraySerializer.new( CustomWizard::Notice.list, each_serializer: CustomWizard::NoticeSerializer From e5fbc408f52be36cad318c1a595cde04f17657c2 Mon Sep 17 00:00:00 2001 From: Robert Barrow Date: Tue, 12 Oct 2021 14:49:09 +0100 Subject: [PATCH 060/160] Add failing API call test --- spec/components/custom_wizard/action_spec.rb | 25 +++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/spec/components/custom_wizard/action_spec.rb b/spec/components/custom_wizard/action_spec.rb index 4bd626ad..8c8e2d31 100644 --- a/spec/components/custom_wizard/action_spec.rb +++ b/spec/components/custom_wizard/action_spec.rb @@ -269,7 +269,7 @@ describe CustomWizard::Action do expect(group.users.first.username).to eq('angus') end - it '#send_to_api' do + it '#send_to_api successful' do stub_request(:put, "https://myexternalapi.com/update"). with( body: "some_body", @@ -291,6 +291,29 @@ describe CustomWizard::Action do expect(result).to eq('success') expect(log_entry.status).to eq('SUCCESS') end + + it '#send_to_api failure' do + stub_request(:put, "https://myexternalapi.com/update"). + with( + body: "some_body", + headers: { + 'Host'=>'myexternalapi.com' + }). + to_return(status: 500, body: "failure", headers: {}) + + new_api = CustomWizard::Api.new("my_api") + CustomWizard::Api.set("my_api", title: "Mocked external api") + CustomWizard::Api::Authorization.set("my_api", api_test_no_authorization) + CustomWizard::Api::Endpoint.new("my_api") + CustomWizard::Api::Endpoint.set("my_api", api_test_endpoint) + endpoint_id = CustomWizard::Api::Endpoint.list("my_api").first.id + + result = CustomWizard::Api::Endpoint.request("my_api", endpoint_id, "some_body") + log_entry = CustomWizard::Api::LogEntry.list("my_api").first + + expect(result).to eq('failure') + expect(log_entry.status).to eq('FAILURE') + end end end From 450f7bfc2520829814a9004ffd033e2ecaa6fc9f Mon Sep 17 00:00:00 2001 From: angusmcleod Date: Wed, 13 Oct 2021 19:32:49 +0800 Subject: [PATCH 061/160] Update notice.rb --- lib/custom_wizard/notice.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/custom_wizard/notice.rb b/lib/custom_wizard/notice.rb index 8d41d634..fb10b724 100644 --- a/lib/custom_wizard/notice.rb +++ b/lib/custom_wizard/notice.rb @@ -4,10 +4,10 @@ class CustomWizard::Notice include ActiveModel::Serialization PLUGIN_STATUS_DOMAINS = { - "tests-passed" => "try.thepavilion.io", - "stable" => "stable.try.thepavilion.io" + "tests-passed" => "plugins.thepavilion.io", + "stable" => "stable.plugins.thepavilion.io" } - SUBSCRIPTION_MESSAGES_DOMAIN = "thepavilion.io" + SUBSCRIPTION_MESSAGES_DOMAIN = "test.thepavilion.io" LOCALHOST_DOMAIN = "localhost:3000" PLUGIN_STATUSES_TO_WARN = %w(incompatible tests_failing) CHECK_PLUGIN_STATUS_ON_BRANCH = %w(tests-passed main stable) From a42e23d352434059acf5c396c0435f03d6c72527 Mon Sep 17 00:00:00 2001 From: merefield Date: Thu, 14 Oct 2021 13:41:24 +0100 Subject: [PATCH 062/160] Show but differentiate unsubbed action options --- .../components/wizard-custom-action.js.es6 | 8 ++++- .../wizard-subscription-selector-row.js.es6 | 19 +++++++++++- .../discourse/lib/wizard-schema.js.es6 | 30 ++++++++++++++----- .../wizard-subscription-selector-row.hbs | 8 ++++- assets/stylesheets/admin/admin.scss | 4 +++ config/locales/client.en.yml | 1 + controllers/custom_wizard/admin/admin.rb | 2 +- .../subscription/subscription.rb | 2 +- 8 files changed, 61 insertions(+), 13 deletions(-) diff --git a/assets/javascripts/discourse/components/wizard-custom-action.js.es6 b/assets/javascripts/discourse/components/wizard-custom-action.js.es6 index 39c84fa2..915e83aa 100644 --- a/assets/javascripts/discourse/components/wizard-custom-action.js.es6 +++ b/assets/javascripts/discourse/components/wizard-custom-action.js.es6 @@ -1,6 +1,7 @@ import { default as discourseComputed } from "discourse-common/utils/decorators"; import wizardSchema, { actionsAvailableWithAdditionalSubscription, + actionsAvailableWithCurrentSubscription } from "discourse/plugins/discourse-custom-wizard/discourse/lib/wizard-schema"; import { empty, equal, or } from "@ember/object/computed"; import { notificationLevels, selectKitContent } from "../lib/wizard"; @@ -100,11 +101,16 @@ export default Component.extend(UndoChanges, { actionTypes(subscribed, subscription) { let unsubscribedActions = actionsAvailableWithAdditionalSubscription(subscription); + let subscribedActions = actionsAvailableWithCurrentSubscription(subscription); return Object.keys(wizardSchema.action.types).reduce((result, type) => { + let subscriptionLabel = (subscribedActions.includes(type) || unsubscribedActions.includes(type)); + let disabled = unsubscribedActions.includes(type); + console.log(subscriptionLabel, disabled); result.push({ id: type, name: I18n.t(`admin.wizard.action.${type}.label`), - subscribed: unsubscribedActions.includes(type), + subscription: subscriptionLabel, + disabled: disabled, }); return result; }, []); diff --git a/assets/javascripts/discourse/components/wizard-subscription-selector/wizard-subscription-selector-row.js.es6 b/assets/javascripts/discourse/components/wizard-subscription-selector/wizard-subscription-selector-row.js.es6 index 23034ac1..11dd314a 100644 --- a/assets/javascripts/discourse/components/wizard-subscription-selector/wizard-subscription-selector-row.js.es6 +++ b/assets/javascripts/discourse/components/wizard-subscription-selector/wizard-subscription-selector-row.js.es6 @@ -1,3 +1,20 @@ import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row"; +import { default as discourseComputed } from "discourse-common/utils/decorators"; -export default SelectKitRowComponent.extend(); +export default SelectKitRowComponent.extend({ + classNameBindings: ["isDisabled:disabled"], + + @discourseComputed("item") + isDisabled() { + return this.item.disabled; + }, + + click(event) { + event.preventDefault(); + event.stopPropagation(); + if (!this.item.disabled) { + this.selectKit.select(this.rowValue, this.item); + } + return false; + } +}); diff --git a/assets/javascripts/discourse/lib/wizard-schema.js.es6 b/assets/javascripts/discourse/lib/wizard-schema.js.es6 index 34df21d2..3559c98d 100644 --- a/assets/javascripts/discourse/lib/wizard-schema.js.es6 +++ b/assets/javascripts/discourse/lib/wizard-schema.js.es6 @@ -201,8 +201,8 @@ const action = { "send_to_api", ], actionTypesWithSubscription: { - advanced: ["send_message", "add_to_group", "watch_categories"], - business: ["create_category", "create_group", "send_to_api"], + basic: ["send_message", "add_to_group", "watch_categories"], + advanced: ["create_category", "create_group", "send_to_api"], }, dependent: {}, objectArrays: {}, @@ -219,17 +219,31 @@ export function actionsAvailableWithAdditionalSubscription( currentSubscription ) { switch (currentSubscription) { - case "complete": - return []; case "advanced": - return action.actionTypesWithSubscription["business"]; - case "core": - return action.actionTypesWithSubscription["advanced"].concat( - action.actionTypesWithSubscription["business"] + return []; + case "basic": + return action.actionTypesWithSubscription["advanced"]; + case "none", "": + return action.actionTypesWithSubscription["basic"].concat( + action.actionTypesWithSubscription["advanced"] ); } } +export function actionsAvailableWithCurrentSubscription( + currentSubscription +) { + switch (currentSubscription) { + case "advanced": + return action.actionTypesWithSubscription["advanced"].concat( + action.actionTypesWithSubscription["basic"]); + case "basic": + return action.actionTypesWithSubscription["basic"]; + case "none", "": + return []; + } +} + export function buildFieldTypes(types) { wizardSchema.field.types = types; } diff --git a/assets/javascripts/discourse/templates/components/wizard-subscription-selector/wizard-subscription-selector-row.hbs b/assets/javascripts/discourse/templates/components/wizard-subscription-selector/wizard-subscription-selector-row.hbs index e2650408..92cb2750 100644 --- a/assets/javascripts/discourse/templates/components/wizard-subscription-selector/wizard-subscription-selector-row.hbs +++ b/assets/javascripts/discourse/templates/components/wizard-subscription-selector/wizard-subscription-selector-row.hbs @@ -10,6 +10,12 @@
{{html-safe label}} {{#if item.subscription}} - {{i18n "admin.wizard.subscription.label"}} + + {{#if item.disabled}} + {{i18n "admin.wizard.subscription.additional_label"}} + {{else}} + {{i18n "admin.wizard.subscription.label"}} + {{/if}} + {{/if}}
diff --git a/assets/stylesheets/admin/admin.scss b/assets/stylesheets/admin/admin.scss index 862f6761..8a8c7239 100644 --- a/assets/stylesheets/admin/admin.scss +++ b/assets/stylesheets/admin/admin.scss @@ -427,6 +427,10 @@ margin-bottom: 0; } + .wizard-custom-action .select-kit-row.disabled { + background: var(--primary-low); + } + .select-box-kit-header { height: initial; } diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 61e8274c..ced433ce 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -467,6 +467,7 @@ en: subscription: nav_label: Subscription label: Subscription + additional_label: "Add subscription" title: Custom Wizard Subscription authorize: Authorize authorized: Authorized diff --git a/controllers/custom_wizard/admin/admin.rb b/controllers/custom_wizard/admin/admin.rb index 9f48e632..2bd5c86e 100644 --- a/controllers/custom_wizard/admin/admin.rb +++ b/controllers/custom_wizard/admin/admin.rb @@ -5,7 +5,7 @@ class CustomWizard::AdminController < ::Admin::AdminController def index render_json_dump( #TODO replace with appropriate static? - api_section: ["complete"].include?(CustomWizard::Subscription.type), + api_section: ["advanced"].include?(CustomWizard::Subscription.type), notices: ActiveModel::ArraySerializer.new( CustomWizard::Notice.list, each_serializer: CustomWizard::NoticeSerializer diff --git a/lib/custom_wizard/subscription/subscription.rb b/lib/custom_wizard/subscription/subscription.rb index 84d21c9d..4c804513 100644 --- a/lib/custom_wizard/subscription/subscription.rb +++ b/lib/custom_wizard/subscription/subscription.rb @@ -13,7 +13,7 @@ class CustomWizard::Subscription::Subscription end def types - %w(core advanced complete) + %w(none basic advanced) end def active? From deb4460d2ccfb9fddb225eaed331b8b72d1eca62 Mon Sep 17 00:00:00 2001 From: merefield Date: Thu, 14 Oct 2021 13:53:58 +0100 Subject: [PATCH 063/160] modify dropdown sub text slightly --- config/locales/client.en.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index ced433ce..0b318922 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -466,7 +466,7 @@ en: subscription: nav_label: Subscription - label: Subscription + label: In subscription additional_label: "Add subscription" title: Custom Wizard Subscription authorize: Authorize From 231051d8ea00263ba364a4c8d9ee7775e60b050f Mon Sep 17 00:00:00 2001 From: angusmcleod Date: Tue, 19 Oct 2021 16:08:03 +0800 Subject: [PATCH 064/160] Update status domains --- lib/custom_wizard/notice.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/custom_wizard/notice.rb b/lib/custom_wizard/notice.rb index fb10b724..3ba710c5 100644 --- a/lib/custom_wizard/notice.rb +++ b/lib/custom_wizard/notice.rb @@ -4,8 +4,8 @@ class CustomWizard::Notice include ActiveModel::Serialization PLUGIN_STATUS_DOMAINS = { - "tests-passed" => "plugins.thepavilion.io", - "stable" => "stable.plugins.thepavilion.io" + "tests-passed" => "plugins.discourse.pavilion.tech", + "stable" => "stable.plugins.discourse.pavilion.tech" } SUBSCRIPTION_MESSAGES_DOMAIN = "test.thepavilion.io" LOCALHOST_DOMAIN = "localhost:3000" From 487ad3c46d5f9e083f3f5d69916195d543617b2b Mon Sep 17 00:00:00 2001 From: merefield Date: Tue, 19 Oct 2021 13:09:11 +0100 Subject: [PATCH 065/160] Fix test --- spec/components/custom_wizard/action_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/components/custom_wizard/action_spec.rb b/spec/components/custom_wizard/action_spec.rb index 8c8e2d31..230a1760 100644 --- a/spec/components/custom_wizard/action_spec.rb +++ b/spec/components/custom_wizard/action_spec.rb @@ -311,7 +311,7 @@ describe CustomWizard::Action do result = CustomWizard::Api::Endpoint.request("my_api", endpoint_id, "some_body") log_entry = CustomWizard::Api::LogEntry.list("my_api").first - expect(result).to eq('failure') + expect(result).to eq({:error=>"API request failed"}) expect(log_entry.status).to eq('FAILURE') end end From b616adaf7147227f8e7f215d94150c7c93121c4e Mon Sep 17 00:00:00 2001 From: merefield Date: Tue, 19 Oct 2021 13:13:32 +0100 Subject: [PATCH 066/160] fix linting --- .../discourse/components/wizard-custom-action.js.es6 | 1 - assets/javascripts/discourse/lib/wizard-schema.js.es6 | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/assets/javascripts/discourse/components/wizard-custom-action.js.es6 b/assets/javascripts/discourse/components/wizard-custom-action.js.es6 index 915e83aa..f9d90a8c 100644 --- a/assets/javascripts/discourse/components/wizard-custom-action.js.es6 +++ b/assets/javascripts/discourse/components/wizard-custom-action.js.es6 @@ -105,7 +105,6 @@ export default Component.extend(UndoChanges, { return Object.keys(wizardSchema.action.types).reduce((result, type) => { let subscriptionLabel = (subscribedActions.includes(type) || unsubscribedActions.includes(type)); let disabled = unsubscribedActions.includes(type); - console.log(subscriptionLabel, disabled); result.push({ id: type, name: I18n.t(`admin.wizard.action.${type}.label`), diff --git a/assets/javascripts/discourse/lib/wizard-schema.js.es6 b/assets/javascripts/discourse/lib/wizard-schema.js.es6 index 3559c98d..14970334 100644 --- a/assets/javascripts/discourse/lib/wizard-schema.js.es6 +++ b/assets/javascripts/discourse/lib/wizard-schema.js.es6 @@ -223,7 +223,7 @@ export function actionsAvailableWithAdditionalSubscription( return []; case "basic": return action.actionTypesWithSubscription["advanced"]; - case "none", "": + case "none": return action.actionTypesWithSubscription["basic"].concat( action.actionTypesWithSubscription["advanced"] ); @@ -239,7 +239,7 @@ export function actionsAvailableWithCurrentSubscription( action.actionTypesWithSubscription["basic"]); case "basic": return action.actionTypesWithSubscription["basic"]; - case "none", "": + case "none": return []; } } From b4f463778db7b127c384839e0affe383dcee3c68 Mon Sep 17 00:00:00 2001 From: merefield Date: Tue, 19 Oct 2021 13:49:06 +0100 Subject: [PATCH 067/160] fix linting issues --- .../components/subscription-container.js.es6 | 20 ++++++----- .../components/wizard-custom-action.js.es6 | 14 +++++--- .../discourse/components/wizard-notice.js.es6 | 36 +++++++++++-------- .../wizard-subscription-selector.js.es6 | 3 +- .../wizard-subscription-selector-row.js.es6 | 2 +- .../components/wizard-subscription.js.es6 | 4 ++- .../custom-wizard-important-notice.js.es6 | 10 +++--- .../controllers/admin-wizards.js.es6 | 10 +++--- .../initializers/custom-wizard-edits.js.es6 | 26 +++++++++----- .../discourse/lib/wizard-schema.js.es6 | 7 ++-- .../models/custom-wizard-notice.js.es6 | 20 ++++++----- .../discourse/routes/admin-wizards.js.es6 | 8 ++--- assets/stylesheets/admin/admin.scss | 10 +++--- 13 files changed, 99 insertions(+), 71 deletions(-) diff --git a/assets/javascripts/discourse/components/subscription-container.js.es6 b/assets/javascripts/discourse/components/subscription-container.js.es6 index 620c6d88..a12b8949 100644 --- a/assets/javascripts/discourse/components/subscription-container.js.es6 +++ b/assets/javascripts/discourse/components/subscription-container.js.es6 @@ -4,18 +4,22 @@ import discourseComputed from "discourse-common/utils/decorators"; export default Component.extend({ classNameBindings: [":subscription-container", "subscribed"], - @discourseComputed('subscribed') + @discourseComputed("subscribed") subscribedIcon(subscribed) { - return subscribed ? 'check' : 'dash'; + return subscribed ? "check" : "dash"; }, - @discourseComputed('subscribed') + @discourseComputed("subscribed") subscribedLabel(subscribed) { - return `admin.wizard.subscription_container.${subscribed ? 'subscribed' : 'not_subscribed'}.label`; + return `admin.wizard.subscription_container.${ + subscribed ? "subscribed" : "not_subscribed" + }.label`; }, - @discourseComputed('subscribed') + @discourseComputed("subscribed") subscribedTitle(subscribed) { - return `admin.wizard.subscription_container.${subscribed ? 'subscribed' : 'not_subscribed'}.title`; - } -}); \ No newline at end of file + return `admin.wizard.subscription_container.${ + subscribed ? "subscribed" : "not_subscribed" + }.title`; + }, +}); diff --git a/assets/javascripts/discourse/components/wizard-custom-action.js.es6 b/assets/javascripts/discourse/components/wizard-custom-action.js.es6 index f9d90a8c..4ee28d17 100644 --- a/assets/javascripts/discourse/components/wizard-custom-action.js.es6 +++ b/assets/javascripts/discourse/components/wizard-custom-action.js.es6 @@ -1,7 +1,7 @@ import { default as discourseComputed } from "discourse-common/utils/decorators"; import wizardSchema, { actionsAvailableWithAdditionalSubscription, - actionsAvailableWithCurrentSubscription + actionsAvailableWithCurrentSubscription, } from "discourse/plugins/discourse-custom-wizard/discourse/lib/wizard-schema"; import { empty, equal, or } from "@ember/object/computed"; import { notificationLevels, selectKitContent } from "../lib/wizard"; @@ -99,11 +99,15 @@ export default Component.extend(UndoChanges, { @discourseComputed("subscribed", "subscription") actionTypes(subscribed, subscription) { - let unsubscribedActions = - actionsAvailableWithAdditionalSubscription(subscription); - let subscribedActions = actionsAvailableWithCurrentSubscription(subscription); + let unsubscribedActions = actionsAvailableWithAdditionalSubscription( + subscription + ); + let subscribedActions = actionsAvailableWithCurrentSubscription( + subscription + ); return Object.keys(wizardSchema.action.types).reduce((result, type) => { - let subscriptionLabel = (subscribedActions.includes(type) || unsubscribedActions.includes(type)); + let subscriptionLabel = + subscribedActions.includes(type) || unsubscribedActions.includes(type); let disabled = unsubscribedActions.includes(type); result.push({ id: type, diff --git a/assets/javascripts/discourse/components/wizard-notice.js.es6 b/assets/javascripts/discourse/components/wizard-notice.js.es6 index 15da3f35..fcd77606 100644 --- a/assets/javascripts/discourse/components/wizard-notice.js.es6 +++ b/assets/javascripts/discourse/components/wizard-notice.js.es6 @@ -4,33 +4,39 @@ import { not, notEmpty } from "@ember/object/computed"; import I18n from "I18n"; export default Component.extend({ - classNameBindings: [':wizard-notice', 'notice.type', 'dismissed', 'expired', 'resolved'], + classNameBindings: [ + ":wizard-notice", + "notice.type", + "dismissed", + "expired", + "resolved", + ], showFull: false, - resolved: notEmpty('notice.expired_at'), - dismissed: notEmpty('notice.dismissed_at'), - canDismiss: not('dismissed'), + resolved: notEmpty("notice.expired_at"), + dismissed: notEmpty("notice.dismissed_at"), + canDismiss: not("dismissed"), - @discourseComputed('notice.type') + @discourseComputed("notice.type") title(type) { return I18n.t(`admin.wizard.notice.title.${type}`); }, - @discourseComputed('notice.type') + @discourseComputed("notice.type") icon(type) { return { - plugin_status_warning: 'exclamation-circle', - plugin_status_connection_error: 'bolt', - subscription_messages_connection_error: 'bolt', - info: 'info-circle' + plugin_status_warning: "exclamation-circle", + plugin_status_connection_error: "bolt", + subscription_messages_connection_error: "bolt", + info: "info-circle", }[type]; }, actions: { dismiss() { - this.set('dismissing', true); + this.set("dismissing", true); this.notice.dismiss().then(() => { - this.set('dismissing', false); + this.set("dismissing", false); }); - } - } -}); \ No newline at end of file + }, + }, +}); diff --git a/assets/javascripts/discourse/components/wizard-subscription-selector.js.es6 b/assets/javascripts/discourse/components/wizard-subscription-selector.js.es6 index ec94bce5..ea1aa5d9 100644 --- a/assets/javascripts/discourse/components/wizard-subscription-selector.js.es6 +++ b/assets/javascripts/discourse/components/wizard-subscription-selector.js.es6 @@ -7,7 +7,8 @@ export default SingleSelectComponent.extend({ autoFilterable: false, filterable: false, showFullTitle: true, - headerComponent: "wizard-subscription-selector/wizard-subscription-selector-header", + headerComponent: + "wizard-subscription-selector/wizard-subscription-selector-header", caretUpIcon: "caret-up", caretDownIcon: "caret-down", }, diff --git a/assets/javascripts/discourse/components/wizard-subscription-selector/wizard-subscription-selector-row.js.es6 b/assets/javascripts/discourse/components/wizard-subscription-selector/wizard-subscription-selector-row.js.es6 index 11dd314a..1d43047a 100644 --- a/assets/javascripts/discourse/components/wizard-subscription-selector/wizard-subscription-selector-row.js.es6 +++ b/assets/javascripts/discourse/components/wizard-subscription-selector/wizard-subscription-selector-row.js.es6 @@ -16,5 +16,5 @@ export default SelectKitRowComponent.extend({ this.selectKit.select(this.rowValue, this.item); } return false; - } + }, }); diff --git a/assets/javascripts/discourse/components/wizard-subscription.js.es6 b/assets/javascripts/discourse/components/wizard-subscription.js.es6 index 0d839a5f..29e47bc2 100644 --- a/assets/javascripts/discourse/components/wizard-subscription.js.es6 +++ b/assets/javascripts/discourse/components/wizard-subscription.js.es6 @@ -25,7 +25,9 @@ export default Component.extend({ @discourseComputed("stateClass") stateLabel(stateClass) { - return I18n.t(`admin.wizard.subscription.subscription.status.${stateClass}`); + return I18n.t( + `admin.wizard.subscription.subscription.status.${stateClass}` + ); }, actions: { diff --git a/assets/javascripts/discourse/connectors/admin-dashboard-top/custom-wizard-important-notice.js.es6 b/assets/javascripts/discourse/connectors/admin-dashboard-top/custom-wizard-important-notice.js.es6 index 43a2152b..5962f255 100644 --- a/assets/javascripts/discourse/connectors/admin-dashboard-top/custom-wizard-important-notice.js.es6 +++ b/assets/javascripts/discourse/connectors/admin-dashboard-top/custom-wizard-important-notice.js.es6 @@ -6,11 +6,11 @@ export default { }, setupComponent() { - const controller = getOwner(this).lookup('controller:admin-dashboard'); - const importantNotice = controller.get('customWizardImportantNotice'); + const controller = getOwner(this).lookup("controller:admin-dashboard"); + const importantNotice = controller.get("customWizardImportantNotice"); if (importantNotice) { - this.set('importantNotice', importantNotice); + this.set("importantNotice", importantNotice); } - } -}; \ No newline at end of file + }, +}; diff --git a/assets/javascripts/discourse/controllers/admin-wizards.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards.js.es6 index a94222c1..d128d851 100644 --- a/assets/javascripts/discourse/controllers/admin-wizards.js.es6 +++ b/assets/javascripts/discourse/controllers/admin-wizards.js.es6 @@ -8,13 +8,13 @@ export default Controller.extend({ ajax(`/admin/wizards/notice/${this.id}`, { type: "DELETE", }) - .then(result => { + .then((result) => { if (result.success) { const notices = this.notices; - notices.removeObject(notices.findBy('id', noticeId)); + notices.removeObject(notices.findBy("id", noticeId)); } }) .catch(popupAjaxError); - } - } -}); \ No newline at end of file + }, + }, +}); diff --git a/assets/javascripts/discourse/initializers/custom-wizard-edits.js.es6 b/assets/javascripts/discourse/initializers/custom-wizard-edits.js.es6 index f53dc2cd..21a9d745 100644 --- a/assets/javascripts/discourse/initializers/custom-wizard-edits.js.es6 +++ b/assets/javascripts/discourse/initializers/custom-wizard-edits.js.es6 @@ -21,27 +21,37 @@ export default { }; withPluginApi("0.8.36", (api) => { - api.modifyClass('route:admin-dashboard', { + api.modifyClass("route:admin-dashboard", { afterModel() { - return CustomWizardNotice.list().then(result => { + return CustomWizardNotice.list().then((result) => { if (result && result.length) { - this.set('notices', A(result.map(n => CustomWizardNotice.create(n)))); + this.set( + "notices", + A(result.map((n) => CustomWizardNotice.create(n))) + ); } - }); + }); }, setupController(controller) { if (this.notices) { - let pluginStatusConnectionError = this.notices.filter(n => n.type === 'plugin_status_connection_error')[0]; - let pluginStatusWarning = this.notices.filter(n => n.type === 'plugin_status_warning')[0]; + let pluginStatusConnectionError = this.notices.filter( + (n) => n.type === "plugin_status_connection_error" + )[0]; + let pluginStatusWarning = this.notices.filter( + (n) => n.type === "plugin_status_warning" + )[0]; if (pluginStatusConnectionError || pluginStatusWarning) { - controller.set('customWizardImportantNotice', pluginStatusConnectionError || pluginStatusWarning); + controller.set( + "customWizardImportantNotice", + pluginStatusConnectionError || pluginStatusWarning + ); } } this._super(...arguments); - } + }, }); }); }, diff --git a/assets/javascripts/discourse/lib/wizard-schema.js.es6 b/assets/javascripts/discourse/lib/wizard-schema.js.es6 index 14970334..76943e2b 100644 --- a/assets/javascripts/discourse/lib/wizard-schema.js.es6 +++ b/assets/javascripts/discourse/lib/wizard-schema.js.es6 @@ -230,13 +230,12 @@ export function actionsAvailableWithAdditionalSubscription( } } -export function actionsAvailableWithCurrentSubscription( - currentSubscription -) { +export function actionsAvailableWithCurrentSubscription(currentSubscription) { switch (currentSubscription) { case "advanced": return action.actionTypesWithSubscription["advanced"].concat( - action.actionTypesWithSubscription["basic"]); + action.actionTypesWithSubscription["basic"] + ); case "basic": return action.actionTypesWithSubscription["basic"]; case "none": diff --git a/assets/javascripts/discourse/models/custom-wizard-notice.js.es6 b/assets/javascripts/discourse/models/custom-wizard-notice.js.es6 index a6b47c40..1eb21e73 100644 --- a/assets/javascripts/discourse/models/custom-wizard-notice.js.es6 +++ b/assets/javascripts/discourse/models/custom-wizard-notice.js.es6 @@ -6,18 +6,20 @@ const CustomWizardNotice = EmberObject.extend(); CustomWizardNotice.reopen({ dismiss() { - return ajax(`/admin/wizards/notice/${this.id}`, { type: 'PUT' }).then(result => { - if (result.success) { - this.set('dismissed_at', result.dismissed_at); - } - }).catch(popupAjaxError); - } + return ajax(`/admin/wizards/notice/${this.id}`, { type: "PUT" }) + .then((result) => { + if (result.success) { + this.set("dismissed_at", result.dismissed_at); + } + }) + .catch(popupAjaxError); + }, }); CustomWizardNotice.reopenClass({ list() { - return ajax('/admin/wizards/notice').catch(popupAjaxError); - } + return ajax("/admin/wizards/notice").catch(popupAjaxError); + }, }); -export default CustomWizardNotice; \ No newline at end of file +export default CustomWizardNotice; diff --git a/assets/javascripts/discourse/routes/admin-wizards.js.es6 b/assets/javascripts/discourse/routes/admin-wizards.js.es6 index 4e4e3c40..de67a8b9 100644 --- a/assets/javascripts/discourse/routes/admin-wizards.js.es6 +++ b/assets/javascripts/discourse/routes/admin-wizards.js.es6 @@ -4,17 +4,17 @@ import { A } from "@ember/array"; export default DiscourseRoute.extend({ model() { - return ajax('/admin/wizards'); + return ajax("/admin/wizards"); }, setupController(controller, model) { - controller.set('notices', A(model.notices)); - controller.set('api_section', model.api_section); + controller.set("notices", A(model.notices)); + controller.set("api_section", model.api_section); }, afterModel(model, transition) { if (transition.targetName === "adminWizards.index") { this.transitionTo("adminWizardsWizard"); } - } + }, }); diff --git a/assets/stylesheets/admin/admin.scss b/assets/stylesheets/admin/admin.scss index 8a8c7239..5973a1e7 100644 --- a/assets/stylesheets/admin/admin.scss +++ b/assets/stylesheets/admin/admin.scss @@ -928,7 +928,7 @@ } .d-icon { - margin-right: .4em; + margin-right: 0.4em; } .notice-header { @@ -939,9 +939,9 @@ border: 1px solid var(--primary); display: inline-flex; align-items: center; - padding: 0 .5em; + padding: 0 0.5em; margin-right: 1em; - font-size: .9em; + font-size: 0.9em; line-height: 25px; min-height: 25px; box-sizing: border-box; @@ -967,12 +967,12 @@ .notice-issued, .notice-resolved { - margin-right: .3em; + margin-right: 0.3em; } .notice-message { p { - margin: .5em 0; + margin: 0.5em 0; } p:last-of-type { From 30fe73570a437c268c9a794d902ed51c3831eefe Mon Sep 17 00:00:00 2001 From: merefield Date: Fri, 22 Oct 2021 15:38:38 +0100 Subject: [PATCH 068/160] remove triple curlies --- .../discourse/templates/components/wizard-notice.hbs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/javascripts/discourse/templates/components/wizard-notice.hbs b/assets/javascripts/discourse/templates/components/wizard-notice.hbs index 6a7d0afb..89bbf5d4 100644 --- a/assets/javascripts/discourse/templates/components/wizard-notice.hbs +++ b/assets/javascripts/discourse/templates/components/wizard-notice.hbs @@ -33,7 +33,7 @@
- {{{notice.message}}} + {{html-safe notice.message}}
{{#if importantOnDashboard}} From 0b8fced879373e699f6d5d2ac8fcb42f270b49da Mon Sep 17 00:00:00 2001 From: merefield Date: Fri, 22 Oct 2021 19:04:37 +0100 Subject: [PATCH 069/160] rubocop changes --- spec/components/custom_wizard/action_spec.rb | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/spec/components/custom_wizard/action_spec.rb b/spec/components/custom_wizard/action_spec.rb index 230a1760..94192aa7 100644 --- a/spec/components/custom_wizard/action_spec.rb +++ b/spec/components/custom_wizard/action_spec.rb @@ -14,8 +14,8 @@ describe CustomWizard::Action do let(:send_message) { get_wizard_fixture("actions/send_message") } let(:send_message_multi) { get_wizard_fixture("actions/send_message_multi") } let(:api_test_endpoint) { get_wizard_fixture("endpoints/test_endpoint") } - let(:api_test_endpoint_body) { get_wizard_fixture("endpoints/test_endpoint_body")} - let(:api_test_no_authorization) { get_wizard_fixture("api/no_authorization")} + let(:api_test_endpoint_body) { get_wizard_fixture("endpoints/test_endpoint_body") } + let(:api_test_no_authorization) { get_wizard_fixture("api/no_authorization") } def update_template(template) CustomWizard::Template.save(template, skip_jobs: true) @@ -271,12 +271,12 @@ describe CustomWizard::Action do it '#send_to_api successful' do stub_request(:put, "https://myexternalapi.com/update"). - with( + with( body: "some_body", headers: { - 'Host'=>'myexternalapi.com' + 'Host' => 'myexternalapi.com' }). - to_return(status: 200, body: "success", headers: {}) + to_return(status: 200, body: "success", headers: {}) new_api = CustomWizard::Api.new("my_api") CustomWizard::Api.set("my_api", title: "Mocked external api") @@ -294,12 +294,12 @@ describe CustomWizard::Action do it '#send_to_api failure' do stub_request(:put, "https://myexternalapi.com/update"). - with( + with( body: "some_body", headers: { - 'Host'=>'myexternalapi.com' + 'Host' => 'myexternalapi.com' }). - to_return(status: 500, body: "failure", headers: {}) + to_return(status: 500, body: "failure", headers: {}) new_api = CustomWizard::Api.new("my_api") CustomWizard::Api.set("my_api", title: "Mocked external api") @@ -311,9 +311,8 @@ describe CustomWizard::Action do result = CustomWizard::Api::Endpoint.request("my_api", endpoint_id, "some_body") log_entry = CustomWizard::Api::LogEntry.list("my_api").first - expect(result).to eq({:error=>"API request failed"}) + expect(result).to eq({ error: "API request failed" }) expect(log_entry.status).to eq('FAILURE') end end end - From 50176b400d6c068674b57e01de5b4654804b0b9a Mon Sep 17 00:00:00 2001 From: merefield Date: Fri, 22 Oct 2021 19:07:45 +0100 Subject: [PATCH 070/160] more rubocop --- lib/custom_wizard/api/endpoint.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/custom_wizard/api/endpoint.rb b/lib/custom_wizard/api/endpoint.rb index b82dd39a..38000106 100644 --- a/lib/custom_wizard/api/endpoint.rb +++ b/lib/custom_wizard/api/endpoint.rb @@ -74,7 +74,7 @@ class CustomWizard::Api::Endpoint headers["Authorization"] = auth_string if auth_string headers["Content-Type"] = content_type if content_type - connection = Excon.new(URI.encode(endpoint.url), headers: headers) + connection = Excon.new(CGI.escape(endpoint.url), headers: headers) params = { method: endpoint.method } From 41e0f13b255441409988889549715a36fcb59bcf Mon Sep 17 00:00:00 2001 From: merefield Date: Fri, 22 Oct 2021 19:24:28 +0100 Subject: [PATCH 071/160] update subscription types --- controllers/custom_wizard/admin/admin.rb | 2 +- lib/custom_wizard/subscription/subscription.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/controllers/custom_wizard/admin/admin.rb b/controllers/custom_wizard/admin/admin.rb index 2bd5c86e..8a3b69ac 100644 --- a/controllers/custom_wizard/admin/admin.rb +++ b/controllers/custom_wizard/admin/admin.rb @@ -5,7 +5,7 @@ class CustomWizard::AdminController < ::Admin::AdminController def index render_json_dump( #TODO replace with appropriate static? - api_section: ["advanced"].include?(CustomWizard::Subscription.type), + api_section: ["business"].include?(CustomWizard::Subscription.type), notices: ActiveModel::ArraySerializer.new( CustomWizard::Notice.list, each_serializer: CustomWizard::NoticeSerializer diff --git a/lib/custom_wizard/subscription/subscription.rb b/lib/custom_wizard/subscription/subscription.rb index 4c804513..0b3bb84d 100644 --- a/lib/custom_wizard/subscription/subscription.rb +++ b/lib/custom_wizard/subscription/subscription.rb @@ -13,7 +13,7 @@ class CustomWizard::Subscription::Subscription end def types - %w(none basic advanced) + %w(none standard business) end def active? From fbab8d89a52adc600e3f2e55b01de21b15e4d82f Mon Sep 17 00:00:00 2001 From: merefield Date: Fri, 22 Oct 2021 19:56:00 +0100 Subject: [PATCH 072/160] Update action dropdown behaviour to show sub level --- .../components/wizard-custom-action.js.es6 | 13 ++----- .../discourse/lib/wizard-schema.js.es6 | 37 +++++++++---------- .../wizard-subscription-selector-row.hbs | 6 +-- 3 files changed, 23 insertions(+), 33 deletions(-) diff --git a/assets/javascripts/discourse/components/wizard-custom-action.js.es6 b/assets/javascripts/discourse/components/wizard-custom-action.js.es6 index 4ee28d17..9024b401 100644 --- a/assets/javascripts/discourse/components/wizard-custom-action.js.es6 +++ b/assets/javascripts/discourse/components/wizard-custom-action.js.es6 @@ -1,7 +1,7 @@ import { default as discourseComputed } from "discourse-common/utils/decorators"; import wizardSchema, { - actionsAvailableWithAdditionalSubscription, - actionsAvailableWithCurrentSubscription, + actionsRequiringAdditionalSubscription, + actionSubscriptionLevel, } from "discourse/plugins/discourse-custom-wizard/discourse/lib/wizard-schema"; import { empty, equal, or } from "@ember/object/computed"; import { notificationLevels, selectKitContent } from "../lib/wizard"; @@ -99,20 +99,15 @@ export default Component.extend(UndoChanges, { @discourseComputed("subscribed", "subscription") actionTypes(subscribed, subscription) { - let unsubscribedActions = actionsAvailableWithAdditionalSubscription( - subscription - ); - let subscribedActions = actionsAvailableWithCurrentSubscription( + let unsubscribedActions = actionsRequiringAdditionalSubscription( subscription ); return Object.keys(wizardSchema.action.types).reduce((result, type) => { - let subscriptionLabel = - subscribedActions.includes(type) || unsubscribedActions.includes(type); let disabled = unsubscribedActions.includes(type); result.push({ id: type, name: I18n.t(`admin.wizard.action.${type}.label`), - subscription: subscriptionLabel, + subscription: actionSubscriptionLevel(type), disabled: disabled, }); return result; diff --git a/assets/javascripts/discourse/lib/wizard-schema.js.es6 b/assets/javascripts/discourse/lib/wizard-schema.js.es6 index 76943e2b..e55e8a44 100644 --- a/assets/javascripts/discourse/lib/wizard-schema.js.es6 +++ b/assets/javascripts/discourse/lib/wizard-schema.js.es6 @@ -201,8 +201,8 @@ const action = { "send_to_api", ], actionTypesWithSubscription: { - basic: ["send_message", "add_to_group", "watch_categories"], - advanced: ["create_category", "create_group", "send_to_api"], + standard: ["send_message", "add_to_group", "watch_categories"], + business: ["create_category", "create_group", "send_to_api"], }, dependent: {}, objectArrays: {}, @@ -215,31 +215,30 @@ const wizardSchema = { action, }; -export function actionsAvailableWithAdditionalSubscription( +export function actionsRequiringAdditionalSubscription( currentSubscription ) { switch (currentSubscription) { - case "advanced": + case "business": return []; - case "basic": - return action.actionTypesWithSubscription["advanced"]; - case "none": - return action.actionTypesWithSubscription["basic"].concat( - action.actionTypesWithSubscription["advanced"] + case "standard": + return action.actionTypesWithSubscription["business"]; + default: + return action.actionTypesWithSubscription["standard"].concat( + action.actionTypesWithSubscription["business"] ); } } -export function actionsAvailableWithCurrentSubscription(currentSubscription) { - switch (currentSubscription) { - case "advanced": - return action.actionTypesWithSubscription["advanced"].concat( - action.actionTypesWithSubscription["basic"] - ); - case "basic": - return action.actionTypesWithSubscription["basic"]; - case "none": - return []; +export function actionSubscriptionLevel(type) { + if (action.actionTypesWithSubscription["business"].includes(type)) { + return "business" + } else { + if (action.actionTypesWithSubscription["standard"].includes(type)) { + return "standard" + } else { + return "" + } } } diff --git a/assets/javascripts/discourse/templates/components/wizard-subscription-selector/wizard-subscription-selector-row.hbs b/assets/javascripts/discourse/templates/components/wizard-subscription-selector/wizard-subscription-selector-row.hbs index 92cb2750..87f2a1e1 100644 --- a/assets/javascripts/discourse/templates/components/wizard-subscription-selector/wizard-subscription-selector-row.hbs +++ b/assets/javascripts/discourse/templates/components/wizard-subscription-selector/wizard-subscription-selector-row.hbs @@ -11,11 +11,7 @@ {{html-safe label}} {{#if item.subscription}} - {{#if item.disabled}} - {{i18n "admin.wizard.subscription.additional_label"}} - {{else}} - {{i18n "admin.wizard.subscription.label"}} - {{/if}} + {{item.subscription}} {{/if}}
From 5334d12f10555fbcb78fd33f9f8108f92f85afdb Mon Sep 17 00:00:00 2001 From: merefield Date: Fri, 22 Oct 2021 19:57:16 +0100 Subject: [PATCH 073/160] format code --- assets/javascripts/discourse/lib/wizard-schema.js.es6 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/javascripts/discourse/lib/wizard-schema.js.es6 b/assets/javascripts/discourse/lib/wizard-schema.js.es6 index e55e8a44..250df371 100644 --- a/assets/javascripts/discourse/lib/wizard-schema.js.es6 +++ b/assets/javascripts/discourse/lib/wizard-schema.js.es6 @@ -232,7 +232,7 @@ export function actionsRequiringAdditionalSubscription( export function actionSubscriptionLevel(type) { if (action.actionTypesWithSubscription["business"].includes(type)) { - return "business" + return "business" } else { if (action.actionTypesWithSubscription["standard"].includes(type)) { return "standard" From a806e14c64e097987e6a175255b1cda229ccb23c Mon Sep 17 00:00:00 2001 From: merefield Date: Sat, 23 Oct 2021 13:06:12 +0100 Subject: [PATCH 074/160] Admin custom fields subscription differentiation --- .../components/custom-field-input.js.es6 | 83 +++++++++++-------- .../discourse/lib/wizard-schema.js.es6 | 66 +++++++++++++++ .../routes/admin-wizards-custom-fields.js.es6 | 2 + .../templates/admin-wizards-custom-fields.hbs | 3 +- .../components/custom-field-input.hbs | 4 +- assets/stylesheets/admin/admin.scss | 22 +++-- .../custom_wizard/admin/custom_fields.rb | 3 +- 7 files changed, 132 insertions(+), 51 deletions(-) diff --git a/assets/javascripts/discourse/components/custom-field-input.js.es6 b/assets/javascripts/discourse/components/custom-field-input.js.es6 index a673112b..bd6c78a1 100644 --- a/assets/javascripts/discourse/components/custom-field-input.js.es6 +++ b/assets/javascripts/discourse/components/custom-field-input.js.es6 @@ -4,27 +4,12 @@ import { alias, equal, or } from "@ember/object/computed"; import { computed } from "@ember/object"; import I18n from "I18n"; -const klasses = ["topic", "post", "group", "category"]; -const types = ["string", "boolean", "integer", "json"]; -const subscriptionTypes = { - klass: ["group", "category"], - type: ["json"], -}; - -const generateContent = function (array, type, subscribed = false) { - return array.reduce((result, key) => { - let subArr = subscriptionTypes[type]; - let subscription = subArr && subArr.includes(key); - if (!subscription || subscribed) { - result.push({ - id: key, - name: I18n.t(`admin.wizard.custom_field.${type}.${key}`), - subscription, - }); - } - return result; - }, []); -}; +import wizardSchema, { + customFieldsKlassesRequiringAdditionalSubscription, + customFieldsKlassSubscriptionLevel, + customFieldsTypesRequiringAdditionalSubscription, + customFieldsTypeSubscriptionLevel, +} from "discourse/plugins/discourse-custom-wizard/discourse/lib/wizard-schema"; export default Component.extend({ tagName: "tr", @@ -32,12 +17,6 @@ export default Component.extend({ postSerializers: ["post"], groupSerializers: ["basic_group"], categorySerializers: ["basic_category"], - klassContent: computed("subscribed", function () { - return generateContent(klasses, "klass", this.subscribed); - }), - typeContent: computed("subscribed", function () { - return generateContent(types, "type", this.subscribed); - }), showInputs: or("field.new", "field.edit"), classNames: ["custom-field-input"], loading: or("saving", "destroying"), @@ -49,15 +28,49 @@ export default Component.extend({ this.set("originalField", JSON.parse(JSON.stringify(this.field))); }, - @discourseComputed("field.klass") - serializerContent(klass) { - const serializers = this.get(`${klass}Serializers`); + // @discourseComputed("field.klass") + // serializerContent(klass) { + // const serializers = this.get(`${klass}Serializers`); - if (serializers) { - return generateContent(serializers, "serializers", this.subscribed); - } else { - return []; - } + // if (serializers) { + // return generateContent(serializers, "serializers", this.subscribed); + // } else { + // return []; + // } + // }, + + @discourseComputed("subscription") + customFieldTypes(subscription) { + let unsubscribedCustomFields = customFieldsTypesRequiringAdditionalSubscription( + subscription + ); + return wizardSchema.custom_field.types.reduce((result, type) => { + let disabled = unsubscribedCustomFields.includes(type); + result.push({ + id: type, + name: I18n.t(`admin.wizard.custom_field.type.${type}`), + subscription: customFieldsTypeSubscriptionLevel(type), + disabled: disabled, + }); + return result; + }, []); + }, + + @discourseComputed("subscription") + customFieldKlasses(subscription) { + let unsubscribedCustomFields = customFieldsKlassesRequiringAdditionalSubscription( + subscription + ); + return wizardSchema.custom_field.klasses.reduce((result, klass) => { + let disabled = unsubscribedCustomFields.includes(klass); + result.push({ + id: klass, + name: I18n.t(`admin.wizard.custom_field.klass.${klass}`), + subscription: customFieldsKlassSubscriptionLevel(klass), + disabled: disabled, + }); + return result; + }, []); }, @observes("field.klass") diff --git a/assets/javascripts/discourse/lib/wizard-schema.js.es6 b/assets/javascripts/discourse/lib/wizard-schema.js.es6 index 250df371..a49dec14 100644 --- a/assets/javascripts/discourse/lib/wizard-schema.js.es6 +++ b/assets/javascripts/discourse/lib/wizard-schema.js.es6 @@ -208,10 +208,24 @@ const action = { objectArrays: {}, }; +const custom_field = { + klasses: ["topic", "post", "group", "category"], + types: ["string", "boolean", "integer", "json"], + customFieldKlassWithSubscription: { + standard: [], + business: ["group", "category"], + }, + customFieldTypeWithSubscription: { + standard: ["json"], + business: [], + }, +} + const wizardSchema = { wizard, step, field, + custom_field, action, }; @@ -242,6 +256,58 @@ export function actionSubscriptionLevel(type) { } } + + +export function customFieldsKlassesRequiringAdditionalSubscription( + currentSubscription +) { + switch (currentSubscription) { + case "business": + return []; + case "standard": + return custom_field.customFieldKlassWithSubscription["business"]; + default: + return custom_field.customFieldKlassWithSubscription["business"].concat(custom_field.customFieldKlassWithSubscription["standard"]); + } +} + +export function customFieldsKlassSubscriptionLevel(type) { + if (custom_field.customFieldKlassWithSubscription["business"].includes(type)) { + return "business" + } else { + if (custom_field.customFieldKlassWithSubscription["standard"].includes(type)) { + return "standard" + } else { + return "" + } + } +} + +export function customFieldsTypesRequiringAdditionalSubscription( + currentSubscription +) { + switch (currentSubscription) { + case "business": + return []; + case "standard": + return custom_field.customFieldTypeWithSubscription["business"]; + default: + return custom_field.customFieldTypeWithSubscription["business"].concat(custom_field.customFieldTypeWithSubscription["standard"]); + } +} + +export function customFieldsTypeSubscriptionLevel(type) { + if (custom_field.customFieldTypeWithSubscription["business"].includes(type)) { + return "business" + } else { + if (custom_field.customFieldTypeWithSubscription["standard"].includes(type)) { + return "standard" + } else { + return "" + } + } +} + export function buildFieldTypes(types) { wizardSchema.field.types = types; } diff --git a/assets/javascripts/discourse/routes/admin-wizards-custom-fields.js.es6 b/assets/javascripts/discourse/routes/admin-wizards-custom-fields.js.es6 index ca9c4c40..4992f92d 100644 --- a/assets/javascripts/discourse/routes/admin-wizards-custom-fields.js.es6 +++ b/assets/javascripts/discourse/routes/admin-wizards-custom-fields.js.es6 @@ -10,10 +10,12 @@ export default DiscourseRoute.extend({ setupController(controller, model) { const customFields = A(model.custom_fields || []); const subscribed = model.subscribed; + const subscription = model.subscription; controller.setProperties({ customFields, subscribed, + subscription, }); }, }); diff --git a/assets/javascripts/discourse/templates/admin-wizards-custom-fields.hbs b/assets/javascripts/discourse/templates/admin-wizards-custom-fields.hbs index 361d2d44..dceed458 100644 --- a/assets/javascripts/discourse/templates/admin-wizards-custom-fields.hbs +++ b/assets/javascripts/discourse/templates/admin-wizards-custom-fields.hbs @@ -33,7 +33,8 @@ field=field removeField=(action "removeField") saveField=(action "saveField") - subscribed=subscribed}} + subscribed=subscribed + subscription=subscription}} {{/each}} diff --git a/assets/javascripts/discourse/templates/components/custom-field-input.hbs b/assets/javascripts/discourse/templates/components/custom-field-input.hbs index ac7e8689..b4ddcc81 100644 --- a/assets/javascripts/discourse/templates/components/custom-field-input.hbs +++ b/assets/javascripts/discourse/templates/components/custom-field-input.hbs @@ -2,14 +2,14 @@ {{wizard-subscription-selector value=field.klass - content=klassContent + content=customFieldKlasses none="admin.wizard.custom_field.klass.select" onChange=(action (mut field.klass))}} {{wizard-subscription-selector value=field.type - content=typeContent + content=customFieldTypes none="admin.wizard.custom_field.type.select" onChange=(action (mut field.type))}} diff --git a/assets/stylesheets/admin/admin.scss b/assets/stylesheets/admin/admin.scss index 5973a1e7..94fe6839 100644 --- a/assets/stylesheets/admin/admin.scss +++ b/assets/stylesheets/admin/admin.scss @@ -427,10 +427,6 @@ margin-bottom: 0; } - .wizard-custom-action .select-kit-row.disabled { - background: var(--primary-low); - } - .select-box-kit-header { height: initial; } @@ -792,11 +788,6 @@ vertical-align: middle; } -.subscription-label { - color: var(--tertiary); - font-size: 0.75em; -} - .admin-wizards-subscription { .admin-wizard-controls { h3, @@ -857,14 +848,21 @@ } .wizard-subscription-selector.select-kit.single-select { - .select-kit-row .texts { - display: flex; - align-items: center; + .select-kit-row { + .texts { + display: flex; + align-items: center; + } + &.disabled { + background: var(--primary-low); + } } .subscription-label { margin-left: 0.75em; padding-top: 0.25em; + color: var(--tertiary); + font-size: 0.75em; } } diff --git a/controllers/custom_wizard/admin/custom_fields.rb b/controllers/custom_wizard/admin/custom_fields.rb index d10f82ed..1cd20f5d 100644 --- a/controllers/custom_wizard/admin/custom_fields.rb +++ b/controllers/custom_wizard/admin/custom_fields.rb @@ -3,7 +3,8 @@ class CustomWizard::AdminCustomFieldsController < CustomWizard::AdminController def index render_json_dump( custom_fields: custom_field_list, - subscribed: CustomWizard::Subscription.subscribed? + subscribed: CustomWizard::Subscription.subscribed?, + subscription: CustomWizard::Subscription.type ) end From 7b129debacdf174e9b390e788ea8c43d74f72451 Mon Sep 17 00:00:00 2001 From: merefield Date: Mon, 25 Oct 2021 15:31:44 +0100 Subject: [PATCH 075/160] partial generalisation of subscription logic --- .../components/wizard-custom-action.js.es6 | 5 +- .../discourse/lib/wizard-schema.js.es6 | 64 ++++++++++++++++++- 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/assets/javascripts/discourse/components/wizard-custom-action.js.es6 b/assets/javascripts/discourse/components/wizard-custom-action.js.es6 index 9024b401..122df003 100644 --- a/assets/javascripts/discourse/components/wizard-custom-action.js.es6 +++ b/assets/javascripts/discourse/components/wizard-custom-action.js.es6 @@ -1,6 +1,7 @@ import { default as discourseComputed } from "discourse-common/utils/decorators"; import wizardSchema, { actionsRequiringAdditionalSubscription, + requiringAdditionalSubscription, actionSubscriptionLevel, } from "discourse/plugins/discourse-custom-wizard/discourse/lib/wizard-schema"; import { empty, equal, or } from "@ember/object/computed"; @@ -99,7 +100,9 @@ export default Component.extend(UndoChanges, { @discourseComputed("subscribed", "subscription") actionTypes(subscribed, subscription) { - let unsubscribedActions = actionsRequiringAdditionalSubscription( + let unsubscribedActions = requiringAdditionalSubscription (subscription, "actions"); + debugger; + let unsubscribedActionslong = actionsRequiringAdditionalSubscription( subscription ); return Object.keys(wizardSchema.action.types).reduce((result, type) => { diff --git a/assets/javascripts/discourse/lib/wizard-schema.js.es6 b/assets/javascripts/discourse/lib/wizard-schema.js.es6 index a49dec14..d8befe97 100644 --- a/assets/javascripts/discourse/lib/wizard-schema.js.es6 +++ b/assets/javascripts/discourse/lib/wizard-schema.js.es6 @@ -221,13 +221,75 @@ const custom_field = { }, } +const subscription_levels = { + standard: { + actions: ["send_message", "add_to_group", "watch_categories"], + custom_fields: { + klasses: [], + types: ["json"] + } + }, + + business: { + actions: ["create_category", "create_group", "send_to_api"], + custom_fields: { + klasses: ["group", "category"], + types: [] + } + } +} + const wizardSchema = { wizard, step, field, custom_field, action, -}; + subscription_levels +} + +export function requiringAdditionalSubscription( + currentSubscription, category +) { + switch (currentSubscription) { + case "business": + return []; + case "standard": + return subscription_levels["business"].[category]; + default: + return subscription_levels["standard"].[category].concat( + subscription_levels["business"].[category] + ); + } +} + + +export function SubscriptionLevel(category, type, subCategory) { + switch (category) { + case "actions": + if (subscription_levels.["business"].actions.includes(type)) { + return "business" + } else { + if (subscription_levels.["standard"].actions.includes(type)) { + return "standard" + } else { + return "" + } + } + case "custom_fields": + if (subscription_levels.["business"].custom_fields[subCategory].includes(type)) { + return "business" + } else { + if (subscription_levels.["standard"].custom_fields[subCategory].includes(type)) { + return "standard" + } else { + return "" + } + } + default: + return ""; + } +} export function actionsRequiringAdditionalSubscription( currentSubscription From 9350db5424e2043d2ad2fa13c8f27baf65766714 Mon Sep 17 00:00:00 2001 From: merefield Date: Mon, 25 Oct 2021 18:55:28 +0100 Subject: [PATCH 076/160] REFACTOR: abstract subscription logic to reduce code --- .../components/custom-field-input.js.es6 | 18 ++- .../components/wizard-custom-action.js.es6 | 15 +- .../discourse/lib/wizard-schema.js.es6 | 134 ++++-------------- 3 files changed, 39 insertions(+), 128 deletions(-) diff --git a/assets/javascripts/discourse/components/custom-field-input.js.es6 b/assets/javascripts/discourse/components/custom-field-input.js.es6 index bd6c78a1..51064974 100644 --- a/assets/javascripts/discourse/components/custom-field-input.js.es6 +++ b/assets/javascripts/discourse/components/custom-field-input.js.es6 @@ -5,10 +5,8 @@ import { computed } from "@ember/object"; import I18n from "I18n"; import wizardSchema, { - customFieldsKlassesRequiringAdditionalSubscription, - customFieldsKlassSubscriptionLevel, - customFieldsTypesRequiringAdditionalSubscription, - customFieldsTypeSubscriptionLevel, + requiringAdditionalSubscription, + subscriptionLevel, } from "discourse/plugins/discourse-custom-wizard/discourse/lib/wizard-schema"; export default Component.extend({ @@ -41,15 +39,15 @@ export default Component.extend({ @discourseComputed("subscription") customFieldTypes(subscription) { - let unsubscribedCustomFields = customFieldsTypesRequiringAdditionalSubscription( - subscription + let unsubscribedCustomFields = requiringAdditionalSubscription( + subscription, "custom_fields", "types" ); return wizardSchema.custom_field.types.reduce((result, type) => { let disabled = unsubscribedCustomFields.includes(type); result.push({ id: type, name: I18n.t(`admin.wizard.custom_field.type.${type}`), - subscription: customFieldsTypeSubscriptionLevel(type), + subscription: subscriptionLevel(type, "custom_fields", "types"), disabled: disabled, }); return result; @@ -58,15 +56,15 @@ export default Component.extend({ @discourseComputed("subscription") customFieldKlasses(subscription) { - let unsubscribedCustomFields = customFieldsKlassesRequiringAdditionalSubscription( - subscription + let unsubscribedCustomFields = requiringAdditionalSubscription( + subscription, "custom_fields", "klasses" ); return wizardSchema.custom_field.klasses.reduce((result, klass) => { let disabled = unsubscribedCustomFields.includes(klass); result.push({ id: klass, name: I18n.t(`admin.wizard.custom_field.klass.${klass}`), - subscription: customFieldsKlassSubscriptionLevel(klass), + subscription: subscriptionLevel(klass, "custom_fields", "klasses"), disabled: disabled, }); return result; diff --git a/assets/javascripts/discourse/components/wizard-custom-action.js.es6 b/assets/javascripts/discourse/components/wizard-custom-action.js.es6 index 122df003..31d2099f 100644 --- a/assets/javascripts/discourse/components/wizard-custom-action.js.es6 +++ b/assets/javascripts/discourse/components/wizard-custom-action.js.es6 @@ -1,8 +1,7 @@ import { default as discourseComputed } from "discourse-common/utils/decorators"; import wizardSchema, { - actionsRequiringAdditionalSubscription, requiringAdditionalSubscription, - actionSubscriptionLevel, + subscriptionLevel, } from "discourse/plugins/discourse-custom-wizard/discourse/lib/wizard-schema"; import { empty, equal, or } from "@ember/object/computed"; import { notificationLevels, selectKitContent } from "../lib/wizard"; @@ -98,19 +97,15 @@ export default Component.extend(UndoChanges, { return apis.find((a) => a.name === api).endpoints; }, - @discourseComputed("subscribed", "subscription") - actionTypes(subscribed, subscription) { - let unsubscribedActions = requiringAdditionalSubscription (subscription, "actions"); - debugger; - let unsubscribedActionslong = actionsRequiringAdditionalSubscription( - subscription - ); + @discourseComputed("subscription") + actionTypes(subscription) { + let unsubscribedActions = requiringAdditionalSubscription (subscription, "actions", ""); return Object.keys(wizardSchema.action.types).reduce((result, type) => { let disabled = unsubscribedActions.includes(type); result.push({ id: type, name: I18n.t(`admin.wizard.action.${type}.label`), - subscription: actionSubscriptionLevel(type), + subscription: subscriptionLevel(type, "actions", ""), disabled: disabled, }); return result; diff --git a/assets/javascripts/discourse/lib/wizard-schema.js.es6 b/assets/javascripts/discourse/lib/wizard-schema.js.es6 index d8befe97..d62a2df8 100644 --- a/assets/javascripts/discourse/lib/wizard-schema.js.es6 +++ b/assets/javascripts/discourse/lib/wizard-schema.js.es6 @@ -193,17 +193,6 @@ const action = { "members_visibility_level", ], required: ["id", "type"], - subscriptionTypes: [ - "send_message", - "add_to_group", - "create_category", - "create_group", - "send_to_api", - ], - actionTypesWithSubscription: { - standard: ["send_message", "add_to_group", "watch_categories"], - business: ["create_category", "create_group", "send_to_api"], - }, dependent: {}, objectArrays: {}, }; @@ -211,14 +200,6 @@ const action = { const custom_field = { klasses: ["topic", "post", "group", "category"], types: ["string", "boolean", "integer", "json"], - customFieldKlassWithSubscription: { - standard: [], - business: ["group", "category"], - }, - customFieldTypeWithSubscription: { - standard: ["json"], - business: [], - }, } const subscription_levels = { @@ -249,22 +230,38 @@ const wizardSchema = { } export function requiringAdditionalSubscription( - currentSubscription, category + currentSubscription, category, subCategory ) { - switch (currentSubscription) { - case "business": - return []; - case "standard": - return subscription_levels["business"].[category]; + switch (category) { + case "actions": + switch (currentSubscription) { + case "business": + return []; + case "standard": + return subscription_levels["business"].[category]; + default: + return subscription_levels["standard"].[category].concat( + subscription_levels["business"].[category] + ); + } + case "custom_fields": + switch (currentSubscription) { + case "business": + return []; + case "standard": + return subscription_levels["business"].[category].[subCategory]; + default: + return subscription_levels["standard"].[category].[subCategory].concat( + subscription_levels["business"].[category].[subCategory] + ); + } default: - return subscription_levels["standard"].[category].concat( - subscription_levels["business"].[category] - ); + return []; } } -export function SubscriptionLevel(category, type, subCategory) { +export function subscriptionLevel(type, category, subCategory) { switch (category) { case "actions": if (subscription_levels.["business"].actions.includes(type)) { @@ -291,85 +288,6 @@ export function SubscriptionLevel(category, type, subCategory) { } } -export function actionsRequiringAdditionalSubscription( - currentSubscription -) { - switch (currentSubscription) { - case "business": - return []; - case "standard": - return action.actionTypesWithSubscription["business"]; - default: - return action.actionTypesWithSubscription["standard"].concat( - action.actionTypesWithSubscription["business"] - ); - } -} - -export function actionSubscriptionLevel(type) { - if (action.actionTypesWithSubscription["business"].includes(type)) { - return "business" - } else { - if (action.actionTypesWithSubscription["standard"].includes(type)) { - return "standard" - } else { - return "" - } - } -} - - - -export function customFieldsKlassesRequiringAdditionalSubscription( - currentSubscription -) { - switch (currentSubscription) { - case "business": - return []; - case "standard": - return custom_field.customFieldKlassWithSubscription["business"]; - default: - return custom_field.customFieldKlassWithSubscription["business"].concat(custom_field.customFieldKlassWithSubscription["standard"]); - } -} - -export function customFieldsKlassSubscriptionLevel(type) { - if (custom_field.customFieldKlassWithSubscription["business"].includes(type)) { - return "business" - } else { - if (custom_field.customFieldKlassWithSubscription["standard"].includes(type)) { - return "standard" - } else { - return "" - } - } -} - -export function customFieldsTypesRequiringAdditionalSubscription( - currentSubscription -) { - switch (currentSubscription) { - case "business": - return []; - case "standard": - return custom_field.customFieldTypeWithSubscription["business"]; - default: - return custom_field.customFieldTypeWithSubscription["business"].concat(custom_field.customFieldTypeWithSubscription["standard"]); - } -} - -export function customFieldsTypeSubscriptionLevel(type) { - if (custom_field.customFieldTypeWithSubscription["business"].includes(type)) { - return "business" - } else { - if (custom_field.customFieldTypeWithSubscription["standard"].includes(type)) { - return "standard" - } else { - return "" - } - } -} - export function buildFieldTypes(types) { wizardSchema.field.types = types; } From 36257fbdfeac4bf5284e8475215719a2d1d9de5b Mon Sep 17 00:00:00 2001 From: merefield Date: Wed, 27 Oct 2021 13:59:50 +0100 Subject: [PATCH 077/160] fix excon call --- lib/custom_wizard/api/endpoint.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/custom_wizard/api/endpoint.rb b/lib/custom_wizard/api/endpoint.rb index 38000106..97fc1127 100644 --- a/lib/custom_wizard/api/endpoint.rb +++ b/lib/custom_wizard/api/endpoint.rb @@ -74,7 +74,7 @@ class CustomWizard::Api::Endpoint headers["Authorization"] = auth_string if auth_string headers["Content-Type"] = content_type if content_type - connection = Excon.new(CGI.escape(endpoint.url), headers: headers) + connection = Excon.new(endpoint.url, headers: headers) params = { method: endpoint.method } From 791eab6c470d32952d1b9de8925baa75f2581a79 Mon Sep 17 00:00:00 2001 From: merefield Date: Wed, 27 Oct 2021 14:02:21 +0100 Subject: [PATCH 078/160] Fix action spec example --- spec/components/custom_wizard/action_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/components/custom_wizard/action_spec.rb b/spec/components/custom_wizard/action_spec.rb index 94192aa7..0c677300 100644 --- a/spec/components/custom_wizard/action_spec.rb +++ b/spec/components/custom_wizard/action_spec.rb @@ -312,7 +312,7 @@ describe CustomWizard::Action do log_entry = CustomWizard::Api::LogEntry.list("my_api").first expect(result).to eq({ error: "API request failed" }) - expect(log_entry.status).to eq('FAILURE') + expect(log_entry.status).to eq('FAIL') end end end From 0752f8068a492cfbec9b8682313943a6865986e3 Mon Sep 17 00:00:00 2001 From: merefield Date: Wed, 27 Oct 2021 14:09:34 +0100 Subject: [PATCH 079/160] fix subscription spec --- .../subscription/subscription_serializer_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/serializers/custom_wizard/subscription/subscription_serializer_spec.rb b/spec/serializers/custom_wizard/subscription/subscription_serializer_spec.rb index 63caf363..91dc59b1 100644 --- a/spec/serializers/custom_wizard/subscription/subscription_serializer_spec.rb +++ b/spec/serializers/custom_wizard/subscription/subscription_serializer_spec.rb @@ -4,11 +4,11 @@ require_relative '../../../plugin_helper' describe CustomWizard::Subscription::SubscriptionSerializer do it 'should return subscription attributes' do - sub = CustomWizard::Subscription::Subscription.new(OpenStruct.new(type: 'community', updated_at: Time.now)) + sub = CustomWizard::Subscription::Subscription.new(OpenStruct.new(type: 'standard', updated_at: Time.now)) serialized = described_class.new(sub, root: false).as_json expect(serialized[:active]).to eq(true) - expect(serialized[:type]).to eq('community') + expect(serialized[:type]).to eq('standard') expect(serialized[:updated_at]).to eq(sub.updated_at) end end From 0c9acb9b200de60766d46f4f2d26ccacb7337696 Mon Sep 17 00:00:00 2001 From: merefield Date: Wed, 27 Oct 2021 14:31:24 +0100 Subject: [PATCH 080/160] Update subscription titles --- config/locales/client.en.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 0b318922..7c3b449b 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -475,7 +475,7 @@ en: not_subscribed: You're not currently subscribed subscription: title: - community: Community Subscription + standard: Standard Subscription business: Business Subscription status: active: Active From 31b4663b3f66533a31de08fa14caf2efb0557fc5 Mon Sep 17 00:00:00 2001 From: merefield Date: Wed, 27 Oct 2021 14:41:49 +0100 Subject: [PATCH 081/160] Fix linting errors --- .../javascripts/discourse/lib/wizard-schema.js.es6 | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/assets/javascripts/discourse/lib/wizard-schema.js.es6 b/assets/javascripts/discourse/lib/wizard-schema.js.es6 index d62a2df8..3dd69be0 100644 --- a/assets/javascripts/discourse/lib/wizard-schema.js.es6 +++ b/assets/javascripts/discourse/lib/wizard-schema.js.es6 @@ -238,10 +238,10 @@ export function requiringAdditionalSubscription( case "business": return []; case "standard": - return subscription_levels["business"].[category]; + return subscription_levels["business"][category]; default: - return subscription_levels["standard"].[category].concat( - subscription_levels["business"].[category] + return subscription_levels["standard"][category].concat( + subscription_levels["business"][category] ); } case "custom_fields": @@ -249,10 +249,10 @@ export function requiringAdditionalSubscription( case "business": return []; case "standard": - return subscription_levels["business"].[category].[subCategory]; + return subscription_levels["business"][category][subCategory]; default: - return subscription_levels["standard"].[category].[subCategory].concat( - subscription_levels["business"].[category].[subCategory] + return subscription_levels["standard"][category][subCategory].concat( + subscription_levels["business"][category][subCategory] ); } default: From 60388b7dab2e0fc1f208d4208aa2f36606810959 Mon Sep 17 00:00:00 2001 From: merefield Date: Wed, 27 Oct 2021 14:52:22 +0100 Subject: [PATCH 082/160] fix more linting errors --- assets/javascripts/discourse/lib/wizard-schema.js.es6 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/assets/javascripts/discourse/lib/wizard-schema.js.es6 b/assets/javascripts/discourse/lib/wizard-schema.js.es6 index 3dd69be0..b482ca2b 100644 --- a/assets/javascripts/discourse/lib/wizard-schema.js.es6 +++ b/assets/javascripts/discourse/lib/wizard-schema.js.es6 @@ -264,20 +264,20 @@ export function requiringAdditionalSubscription( export function subscriptionLevel(type, category, subCategory) { switch (category) { case "actions": - if (subscription_levels.["business"].actions.includes(type)) { + if (subscription_levels["business"].actions.includes(type)) { return "business" } else { - if (subscription_levels.["standard"].actions.includes(type)) { + if (subscription_levels["standard"].actions.includes(type)) { return "standard" } else { return "" } } case "custom_fields": - if (subscription_levels.["business"].custom_fields[subCategory].includes(type)) { + if (subscription_levels["business"].custom_fields[subCategory].includes(type)) { return "business" } else { - if (subscription_levels.["standard"].custom_fields[subCategory].includes(type)) { + if (subscription_levels["standard"].custom_fields[subCategory].includes(type)) { return "standard" } else { return "" From 203876e9271d10d7b20a2f6cfb576b376916cfe5 Mon Sep 17 00:00:00 2001 From: merefield Date: Wed, 27 Oct 2021 15:01:29 +0100 Subject: [PATCH 083/160] linting fixes --- .../components/custom-field-input.js.es6 | 5 ++--- .../components/wizard-custom-action.js.es6 | 2 +- .../discourse/lib/wizard-schema.js.es6 | 19 +++++++++---------- 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/assets/javascripts/discourse/components/custom-field-input.js.es6 b/assets/javascripts/discourse/components/custom-field-input.js.es6 index 51064974..6d4c6366 100644 --- a/assets/javascripts/discourse/components/custom-field-input.js.es6 +++ b/assets/javascripts/discourse/components/custom-field-input.js.es6 @@ -1,7 +1,6 @@ import Component from "@ember/component"; import discourseComputed, { observes } from "discourse-common/utils/decorators"; import { alias, equal, or } from "@ember/object/computed"; -import { computed } from "@ember/object"; import I18n from "I18n"; import wizardSchema, { @@ -48,7 +47,7 @@ export default Component.extend({ id: type, name: I18n.t(`admin.wizard.custom_field.type.${type}`), subscription: subscriptionLevel(type, "custom_fields", "types"), - disabled: disabled, + disabled, }); return result; }, []); @@ -65,7 +64,7 @@ export default Component.extend({ id: klass, name: I18n.t(`admin.wizard.custom_field.klass.${klass}`), subscription: subscriptionLevel(klass, "custom_fields", "klasses"), - disabled: disabled, + disabled, }); return result; }, []); diff --git a/assets/javascripts/discourse/components/wizard-custom-action.js.es6 b/assets/javascripts/discourse/components/wizard-custom-action.js.es6 index 31d2099f..53d79098 100644 --- a/assets/javascripts/discourse/components/wizard-custom-action.js.es6 +++ b/assets/javascripts/discourse/components/wizard-custom-action.js.es6 @@ -106,7 +106,7 @@ export default Component.extend(UndoChanges, { id: type, name: I18n.t(`admin.wizard.action.${type}.label`), subscription: subscriptionLevel(type, "actions", ""), - disabled: disabled, + disabled, }); return result; }, []); diff --git a/assets/javascripts/discourse/lib/wizard-schema.js.es6 b/assets/javascripts/discourse/lib/wizard-schema.js.es6 index b482ca2b..65e003a8 100644 --- a/assets/javascripts/discourse/lib/wizard-schema.js.es6 +++ b/assets/javascripts/discourse/lib/wizard-schema.js.es6 @@ -200,7 +200,7 @@ const action = { const custom_field = { klasses: ["topic", "post", "group", "category"], types: ["string", "boolean", "integer", "json"], -} +}; const subscription_levels = { standard: { @@ -218,7 +218,7 @@ const subscription_levels = { types: [] } } -} +}; const wizardSchema = { wizard, @@ -227,7 +227,7 @@ const wizardSchema = { custom_field, action, subscription_levels -} +}; export function requiringAdditionalSubscription( currentSubscription, category, subCategory @@ -258,8 +258,7 @@ export function requiringAdditionalSubscription( default: return []; } -} - +}; export function subscriptionLevel(type, category, subCategory) { switch (category) { @@ -286,15 +285,15 @@ export function subscriptionLevel(type, category, subCategory) { default: return ""; } -} +}; export function buildFieldTypes(types) { wizardSchema.field.types = types; -} +}; export function buildFieldValidations(validations) { wizardSchema.field.validations = validations; -} +}; const siteSettings = getOwner(this).lookup("site-settings:main"); if (siteSettings.wizard_apis_enabled) { @@ -303,7 +302,7 @@ if (siteSettings.wizard_apis_enabled) { api_endpoint: null, api_body: null, }; -} +}; export function setWizardDefaults(obj, itemType) { const objSchema = wizardSchema[itemType]; @@ -329,6 +328,6 @@ export function setWizardDefaults(obj, itemType) { } return obj; -} +}; export default wizardSchema; From a3d59caee81917398ddd8eb402e6e3fbc237dbbd Mon Sep 17 00:00:00 2001 From: merefield Date: Wed, 27 Oct 2021 15:05:09 +0100 Subject: [PATCH 084/160] yet more linting fixes --- .../javascripts/discourse/lib/wizard-schema.js.es6 | 12 ++++++------ assets/javascripts/wizard/lib/text-lite.js.es6 | 2 +- assets/javascripts/wizard/lib/user-search.js.es6 | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/assets/javascripts/discourse/lib/wizard-schema.js.es6 b/assets/javascripts/discourse/lib/wizard-schema.js.es6 index 65e003a8..fcdd37b4 100644 --- a/assets/javascripts/discourse/lib/wizard-schema.js.es6 +++ b/assets/javascripts/discourse/lib/wizard-schema.js.es6 @@ -264,22 +264,22 @@ export function subscriptionLevel(type, category, subCategory) { switch (category) { case "actions": if (subscription_levels["business"].actions.includes(type)) { - return "business" + return "business"; } else { if (subscription_levels["standard"].actions.includes(type)) { - return "standard" + return "standard"; } else { - return "" + return ""; } } case "custom_fields": if (subscription_levels["business"].custom_fields[subCategory].includes(type)) { - return "business" + return "business"; } else { if (subscription_levels["standard"].custom_fields[subCategory].includes(type)) { - return "standard" + return "standard"; } else { - return "" + return ""; } } default: diff --git a/assets/javascripts/wizard/lib/text-lite.js.es6 b/assets/javascripts/wizard/lib/text-lite.js.es6 index c93f6708..cc161426 100644 --- a/assets/javascripts/wizard/lib/text-lite.js.es6 +++ b/assets/javascripts/wizard/lib/text-lite.js.es6 @@ -7,7 +7,7 @@ import { getOwner } from "discourse-common/lib/get-owner"; export function cook(text, options) { if (!options) { options = buildOptions({ - getURL: getURL, + getURL, siteSettings: getOwner(this).lookup("site-settings:main"), }); } diff --git a/assets/javascripts/wizard/lib/user-search.js.es6 b/assets/javascripts/wizard/lib/user-search.js.es6 index 8ef96266..e7171f18 100644 --- a/assets/javascripts/wizard/lib/user-search.js.es6 +++ b/assets/javascripts/wizard/lib/user-search.js.es6 @@ -27,12 +27,12 @@ function performSearch( // need to be able to cancel this oldSearch = $.ajax(getUrl("/u/search/users"), { data: { - term: term, + term, topic_id: topicId, include_groups: includeGroups, include_mentionable_groups: includeMentionableGroups, include_messageable_groups: includeMessageableGroups, - group: group, + group, topic_allowed_users: allowedUsers, }, }); From bd5edaffe99027385df62ebc79903150b4b6f647 Mon Sep 17 00:00:00 2001 From: merefield Date: Tue, 2 Nov 2021 08:58:15 +0000 Subject: [PATCH 085/160] custom field input: further make generic, fix serializers dropdown --- .../components/custom-field-input.js.es6 | 66 +++++++++---------- .../discourse/lib/wizard-schema.js.es6 | 12 ++-- 2 files changed, 37 insertions(+), 41 deletions(-) diff --git a/assets/javascripts/discourse/components/custom-field-input.js.es6 b/assets/javascripts/discourse/components/custom-field-input.js.es6 index 6d4c6366..f60052c9 100644 --- a/assets/javascripts/discourse/components/custom-field-input.js.es6 +++ b/assets/javascripts/discourse/components/custom-field-input.js.es6 @@ -8,6 +8,22 @@ import wizardSchema, { subscriptionLevel, } from "discourse/plugins/discourse-custom-wizard/discourse/lib/wizard-schema"; +const generateContent = function (kategory, subscription) { + let unsubscribedCustomFields = requiringAdditionalSubscription( + subscription, "custom_fields", kategory + ); + return wizardSchema.custom_field[kategory].reduce((result, item) => { + let disabled = unsubscribedCustomFields.includes(item); + result.push({ + id: item, + name: I18n.t(`admin.wizard.custom_field.${kategory}.${item}`), + subscription: subscriptionLevel(item, "custom_fields", kategory), + disabled, + }); + return result; + }, []); +}; + export default Component.extend({ tagName: "tr", topicSerializers: ["topic_view", "topic_list_item"], @@ -25,49 +41,29 @@ export default Component.extend({ this.set("originalField", JSON.parse(JSON.stringify(this.field))); }, - // @discourseComputed("field.klass") - // serializerContent(klass) { - // const serializers = this.get(`${klass}Serializers`); + @discourseComputed("field.klass") + serializerContent(klass) { + const serializers = this.get(`${klass}Serializers`); - // if (serializers) { - // return generateContent(serializers, "serializers", this.subscribed); - // } else { - // return []; - // } - // }, + if (serializers) { + return serializers.reduce((result, key) => { + result.push({ + id: key, + name: I18n.t(`admin.wizard.custom_field.serializers.${key}`), + }); + return result; + }, []); + } + }, @discourseComputed("subscription") customFieldTypes(subscription) { - let unsubscribedCustomFields = requiringAdditionalSubscription( - subscription, "custom_fields", "types" - ); - return wizardSchema.custom_field.types.reduce((result, type) => { - let disabled = unsubscribedCustomFields.includes(type); - result.push({ - id: type, - name: I18n.t(`admin.wizard.custom_field.type.${type}`), - subscription: subscriptionLevel(type, "custom_fields", "types"), - disabled, - }); - return result; - }, []); + return generateContent("type", this.subscription); }, @discourseComputed("subscription") customFieldKlasses(subscription) { - let unsubscribedCustomFields = requiringAdditionalSubscription( - subscription, "custom_fields", "klasses" - ); - return wizardSchema.custom_field.klasses.reduce((result, klass) => { - let disabled = unsubscribedCustomFields.includes(klass); - result.push({ - id: klass, - name: I18n.t(`admin.wizard.custom_field.klass.${klass}`), - subscription: subscriptionLevel(klass, "custom_fields", "klasses"), - disabled, - }); - return result; - }, []); + return generateContent("klass", this.subscription); }, @observes("field.klass") diff --git a/assets/javascripts/discourse/lib/wizard-schema.js.es6 b/assets/javascripts/discourse/lib/wizard-schema.js.es6 index fcdd37b4..fb2e697d 100644 --- a/assets/javascripts/discourse/lib/wizard-schema.js.es6 +++ b/assets/javascripts/discourse/lib/wizard-schema.js.es6 @@ -198,24 +198,24 @@ const action = { }; const custom_field = { - klasses: ["topic", "post", "group", "category"], - types: ["string", "boolean", "integer", "json"], + klass: ["topic", "post", "group", "category"], + type: ["string", "boolean", "integer", "json"], }; const subscription_levels = { standard: { actions: ["send_message", "add_to_group", "watch_categories"], custom_fields: { - klasses: [], - types: ["json"] + klass: [], + type: ["json"] } }, business: { actions: ["create_category", "create_group", "send_to_api"], custom_fields: { - klasses: ["group", "category"], - types: [] + klass: ["group", "category"], + type: [] } } }; From 8cbc8745b90efe66375117df6128478631580734 Mon Sep 17 00:00:00 2001 From: merefield Date: Tue, 2 Nov 2021 09:06:00 +0000 Subject: [PATCH 086/160] prettier --- .../components/custom-field-input.js.es6 | 4 +- .../components/wizard-custom-action.js.es6 | 6 ++- .../discourse/lib/wizard-schema.js.es6 | 40 ++++++++++++------- 3 files changed, 33 insertions(+), 17 deletions(-) diff --git a/assets/javascripts/discourse/components/custom-field-input.js.es6 b/assets/javascripts/discourse/components/custom-field-input.js.es6 index f60052c9..83f668d0 100644 --- a/assets/javascripts/discourse/components/custom-field-input.js.es6 +++ b/assets/javascripts/discourse/components/custom-field-input.js.es6 @@ -10,7 +10,9 @@ import wizardSchema, { const generateContent = function (kategory, subscription) { let unsubscribedCustomFields = requiringAdditionalSubscription( - subscription, "custom_fields", kategory + subscription, + "custom_fields", + kategory ); return wizardSchema.custom_field[kategory].reduce((result, item) => { let disabled = unsubscribedCustomFields.includes(item); diff --git a/assets/javascripts/discourse/components/wizard-custom-action.js.es6 b/assets/javascripts/discourse/components/wizard-custom-action.js.es6 index 53d79098..6cf22942 100644 --- a/assets/javascripts/discourse/components/wizard-custom-action.js.es6 +++ b/assets/javascripts/discourse/components/wizard-custom-action.js.es6 @@ -99,7 +99,11 @@ export default Component.extend(UndoChanges, { @discourseComputed("subscription") actionTypes(subscription) { - let unsubscribedActions = requiringAdditionalSubscription (subscription, "actions", ""); + let unsubscribedActions = requiringAdditionalSubscription( + subscription, + "actions", + "" + ); return Object.keys(wizardSchema.action.types).reduce((result, type) => { let disabled = unsubscribedActions.includes(type); result.push({ diff --git a/assets/javascripts/discourse/lib/wizard-schema.js.es6 b/assets/javascripts/discourse/lib/wizard-schema.js.es6 index fb2e697d..af4fa758 100644 --- a/assets/javascripts/discourse/lib/wizard-schema.js.es6 +++ b/assets/javascripts/discourse/lib/wizard-schema.js.es6 @@ -207,17 +207,17 @@ const subscription_levels = { actions: ["send_message", "add_to_group", "watch_categories"], custom_fields: { klass: [], - type: ["json"] - } + type: ["json"], + }, }, business: { actions: ["create_category", "create_group", "send_to_api"], custom_fields: { klass: ["group", "category"], - type: [] - } - } + type: [], + }, + }, }; const wizardSchema = { @@ -226,11 +226,13 @@ const wizardSchema = { field, custom_field, action, - subscription_levels + subscription_levels, }; export function requiringAdditionalSubscription( - currentSubscription, category, subCategory + currentSubscription, + category, + subCategory ) { switch (category) { case "actions": @@ -258,7 +260,7 @@ export function requiringAdditionalSubscription( default: return []; } -}; +} export function subscriptionLevel(type, category, subCategory) { switch (category) { @@ -273,10 +275,18 @@ export function subscriptionLevel(type, category, subCategory) { } } case "custom_fields": - if (subscription_levels["business"].custom_fields[subCategory].includes(type)) { + if ( + subscription_levels["business"].custom_fields[subCategory].includes( + type + ) + ) { return "business"; } else { - if (subscription_levels["standard"].custom_fields[subCategory].includes(type)) { + if ( + subscription_levels["standard"].custom_fields[subCategory].includes( + type + ) + ) { return "standard"; } else { return ""; @@ -285,15 +295,15 @@ export function subscriptionLevel(type, category, subCategory) { default: return ""; } -}; +} export function buildFieldTypes(types) { wizardSchema.field.types = types; -}; +} export function buildFieldValidations(validations) { wizardSchema.field.validations = validations; -}; +} const siteSettings = getOwner(this).lookup("site-settings:main"); if (siteSettings.wizard_apis_enabled) { @@ -302,7 +312,7 @@ if (siteSettings.wizard_apis_enabled) { api_endpoint: null, api_body: null, }; -}; +} export function setWizardDefaults(obj, itemType) { const objSchema = wizardSchema[itemType]; @@ -328,6 +338,6 @@ export function setWizardDefaults(obj, itemType) { } return obj; -}; +} export default wizardSchema; From 3add43e81b312137383c226b3bcd236b0233c31c Mon Sep 17 00:00:00 2001 From: merefield Date: Tue, 2 Nov 2021 09:09:06 +0000 Subject: [PATCH 087/160] eslint --- .../discourse/components/custom-field-input.js.es6 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/assets/javascripts/discourse/components/custom-field-input.js.es6 b/assets/javascripts/discourse/components/custom-field-input.js.es6 index 83f668d0..7ee70716 100644 --- a/assets/javascripts/discourse/components/custom-field-input.js.es6 +++ b/assets/javascripts/discourse/components/custom-field-input.js.es6 @@ -60,12 +60,12 @@ export default Component.extend({ @discourseComputed("subscription") customFieldTypes(subscription) { - return generateContent("type", this.subscription); + return generateContent("type", subscription); }, @discourseComputed("subscription") customFieldKlasses(subscription) { - return generateContent("klass", this.subscription); + return generateContent("klass", subscription); }, @observes("field.klass") From 14a337e00c20c07387d020019c42d5dce16a4874 Mon Sep 17 00:00:00 2001 From: merefield Date: Tue, 2 Nov 2021 09:11:30 +0000 Subject: [PATCH 088/160] template lint --- .../wizard-subscription-selector-row.hbs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/javascripts/discourse/templates/components/wizard-subscription-selector/wizard-subscription-selector-row.hbs b/assets/javascripts/discourse/templates/components/wizard-subscription-selector/wizard-subscription-selector-row.hbs index 87f2a1e1..ecd77cb1 100644 --- a/assets/javascripts/discourse/templates/components/wizard-subscription-selector/wizard-subscription-selector-row.hbs +++ b/assets/javascripts/discourse/templates/components/wizard-subscription-selector/wizard-subscription-selector-row.hbs @@ -11,7 +11,7 @@ {{html-safe label}} {{#if item.subscription}} - {{item.subscription}} + {{item.subscription}} {{/if}}
From 9ab441f8880e2f39fad565cf1edc48e7d2d102df Mon Sep 17 00:00:00 2001 From: angusmcleod Date: Tue, 9 Nov 2021 20:38:53 +0800 Subject: [PATCH 089/160] Update plugin metadata --- plugin.rb | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/plugin.rb b/plugin.rb index 97d5472b..6845bf3a 100644 --- a/plugin.rb +++ b/plugin.rb @@ -1,10 +1,12 @@ # frozen_string_literal: true # name: discourse-custom-wizard -# about: Create custom wizards +# about: Create custom wizards for topic creation, onboarding, user surveys and much more. # version: 0.8.1 -# authors: Angus McLeod +# authors: Angus McLeod, Faizaan Gagan, Robert Barrow, Keegan George +# contact_emails: support@thepavilion.io # url: https://github.com/paviliondev/discourse-custom-wizard -# contact emails: angus@thepavilion.io +# tests_passed_test_url: https://plugins.discourse.pavilion.tech/w/super-mega-fun-wizard +# stable_test_url: https://stable.plugins.discourse.pavilion.tech/w/super-mega-fun-wizard gem 'liquid', '5.0.1', require: true register_asset 'stylesheets/admin/admin.scss', :desktop From c6b8e08e38e516d3da13b84ae635deb23c5992a4 Mon Sep 17 00:00:00 2001 From: merefield Date: Tue, 9 Nov 2021 14:57:33 +0000 Subject: [PATCH 090/160] Add subscription level logic to back-end validation --- lib/custom_wizard/custom_field.rb | 8 ++-- lib/custom_wizard/subscription.rb | 46 +++++++++++++++++++ spec/components/custom_wizard/action_spec.rb | 2 +- spec/components/custom_wizard/builder_spec.rb | 12 ++--- .../custom_wizard/custom_field_spec.rb | 2 +- spec/components/custom_wizard/mapper_spec.rb | 2 +- .../custom_wizard/template_validator_spec.rb | 2 +- .../custom_field_extensions_spec.rb | 2 +- spec/plugin_helper.rb | 4 +- .../custom_field_extensions_spec.rb | 3 +- .../custom_wizard/steps_controller_spec.rb | 2 +- .../wizard_step_serializer_spec.rb | 2 +- 12 files changed, 66 insertions(+), 21 deletions(-) diff --git a/lib/custom_wizard/custom_field.rb b/lib/custom_wizard/custom_field.rb index eb93e292..99dcfbbb 100644 --- a/lib/custom_wizard/custom_field.rb +++ b/lib/custom_wizard/custom_field.rb @@ -17,9 +17,8 @@ class ::CustomWizard::CustomField category: ["basic_category"], post: ["post"] } - SUBSCRIPTION_CLASSES ||= ['category', 'group'] + TYPES ||= ["string", "boolean", "integer", "json"] - SUBSCRIPTION_TYPES ||= ["json"] LIST_CACHE_KEY ||= 'custom_field_list' def self.serializers @@ -84,8 +83,7 @@ class ::CustomWizard::CustomField add_error(I18n.t("#{i18n_key}.unsupported_class", class: value)) next end - - if attr == 'klass' && SUBSCRIPTION_CLASSES.include?(value) && !@subscription.subscribed? + if attr == 'klass' && @subscription.requires_additional_subscription("custom_fields", "klass").include?(value) add_error(I18n.t("wizard.custom_field.error.subscription_type", type: value)) end @@ -100,7 +98,7 @@ class ::CustomWizard::CustomField add_error(I18n.t("#{i18n_key}.unsupported_type", type: value)) end - if attr == 'type' && SUBSCRIPTION_TYPES.include?(value) && !@subscription.subscribed? + if attr == 'type' && @subscription.requires_additional_subscription("custom_fields", "type").include?(value) add_error(I18n.t("wizard.custom_field.error.subscription_type", type: value)) end diff --git a/lib/custom_wizard/subscription.rb b/lib/custom_wizard/subscription.rb index 6c535602..7d94eb89 100644 --- a/lib/custom_wizard/subscription.rb +++ b/lib/custom_wizard/subscription.rb @@ -6,6 +6,23 @@ class CustomWizard::Subscription attr_accessor :authentication, :subscription + SUBSCRIPTION_LEVELS = { + standard: { + actions: ["send_message", "add_to_group", "watch_categories"], + custom_fields: { + klass: [], + type: ["json"], + }, + }, + business: { + actions: ["create_category", "create_group", "send_to_api"], + custom_fields: { + klass: ["group", "category"], + type: [], + }, + }, + } + def initialize @authentication = CustomWizard::Subscription::Authentication.new(get_authentication) @subscription = CustomWizard::Subscription::Subscription.new(get_subscription) @@ -39,6 +56,31 @@ class CustomWizard::Subscription "discourse-subscription-server:user_subscription" end + def requires_additional_subscription(kategory, sub_kategory) + case kategory + when "actions" + case self.type + when "business" + return [] + when "standard" + return SUBSCRIPTION_LEVELS[:business][kategory.to_sym] + else + return SUBSCRIPTION_LEVELS[:standard][kategory.to_sym] + SUBSCRIPTION_LEVELS[:business][kategory.to_sym] + end + when "custom_fields" + case self.type + when "business" + return [] + when "standard" + return SUBSCRIPTION_LEVELS[:business][kategory.to_sym][sub_kategory.to_sym]; + else + return SUBSCRIPTION_LEVELS[:standard][kategory.to_sym][sub_kategory.to_sym] + SUBSCRIPTION_LEVELS[:business][kategory.to_sym][sub_kategory.to_sym] + end + else + return [] + end + end + def update if @authentication.active? response = Excon.get( @@ -127,6 +169,10 @@ class CustomWizard::Subscription self.new.type end + def self.requires_additional_subscription(kategory, sub_kategory) + self.new.requires_additional_subscription(kategory, sub_kategory) + end + def self.authorized? self.new.authorized? end diff --git a/spec/components/custom_wizard/action_spec.rb b/spec/components/custom_wizard/action_spec.rb index 0c677300..284f63d3 100644 --- a/spec/components/custom_wizard/action_spec.rb +++ b/spec/components/custom_wizard/action_spec.rb @@ -179,7 +179,7 @@ describe CustomWizard::Action do context "subscription actions" do before do - enable_subscription + enable_subscription("standard") end it '#send_message' do diff --git a/spec/components/custom_wizard/builder_spec.rb b/spec/components/custom_wizard/builder_spec.rb index 28880409..f4a6d2fc 100644 --- a/spec/components/custom_wizard/builder_spec.rb +++ b/spec/components/custom_wizard/builder_spec.rb @@ -176,7 +176,7 @@ describe CustomWizard::Builder do context "restart is enabled" do before do - enable_subscription + enable_subscription("standard") @template[:restart_on_revisit] = true CustomWizard::Template.save(@template.as_json) end @@ -205,7 +205,7 @@ describe CustomWizard::Builder do context 'with required data' do before do - enable_subscription + enable_subscription("standard") @template[:steps][0][:required_data] = required_data_json['required_data'] @template[:steps][0][:required_data_message] = required_data_json['required_data_message'] CustomWizard::Template.save(@template.as_json) @@ -241,7 +241,7 @@ describe CustomWizard::Builder do context "with permitted params" do before do - enable_subscription + enable_subscription("standard") @template[:steps][0][:permitted_params] = permitted_param_json['permitted_params'] CustomWizard::Template.save(@template.as_json) end @@ -256,7 +256,7 @@ describe CustomWizard::Builder do context "with condition" do before do - enable_subscription + enable_subscription("standard") @template[:steps][0][:condition] = user_condition_json['condition'] CustomWizard::Template.save(@template.as_json) end @@ -295,7 +295,7 @@ describe CustomWizard::Builder do context "with condition" do before do - enable_subscription + enable_subscription("standard") @template[:steps][0][:fields][0][:condition] = user_condition_json['condition'] CustomWizard::Template.save(@template.as_json) end @@ -327,7 +327,7 @@ describe CustomWizard::Builder do context 'save submissions disabled' do before do - enable_subscription + enable_subscription("standard") @template[:save_submissions] = false CustomWizard::Template.save(@template.as_json) @wizard = CustomWizard::Builder.new(@template[:id], user).build diff --git a/spec/components/custom_wizard/custom_field_spec.rb b/spec/components/custom_wizard/custom_field_spec.rb index a30ec02b..fd0fa4fc 100644 --- a/spec/components/custom_wizard/custom_field_spec.rb +++ b/spec/components/custom_wizard/custom_field_spec.rb @@ -217,7 +217,7 @@ describe CustomWizard::CustomField do context "with a subscription" do before do - enable_subscription + enable_subscription("business") end it "saves subscription field types" do diff --git a/spec/components/custom_wizard/mapper_spec.rb b/spec/components/custom_wizard/mapper_spec.rb index 1ac945d0..71c1e7de 100644 --- a/spec/components/custom_wizard/mapper_spec.rb +++ b/spec/components/custom_wizard/mapper_spec.rb @@ -359,7 +359,7 @@ describe CustomWizard::Mapper do context "with a subscription" do before do - enable_subscription + enable_subscription("standard") end it "treats replaced values as string literals" do diff --git a/spec/components/custom_wizard/template_validator_spec.rb b/spec/components/custom_wizard/template_validator_spec.rb index e8b8af28..14214e4d 100644 --- a/spec/components/custom_wizard/template_validator_spec.rb +++ b/spec/components/custom_wizard/template_validator_spec.rb @@ -75,7 +75,7 @@ describe CustomWizard::TemplateValidator do context "with subscription" do before do - enable_subscription + enable_subscription("standard") end it "validates wizard attributes" do diff --git a/spec/extensions/custom_field_extensions_spec.rb b/spec/extensions/custom_field_extensions_spec.rb index 7bba27a0..3660644b 100644 --- a/spec/extensions/custom_field_extensions_spec.rb +++ b/spec/extensions/custom_field_extensions_spec.rb @@ -74,7 +74,7 @@ describe "custom field extensions" do context "subscription custom fields" do before do - enable_subscription + enable_subscription("business") subscription_custom_field_json['custom_fields'].each do |field_json| custom_field = CustomWizard::CustomField.new(nil, field_json) diff --git a/spec/plugin_helper.rb b/spec/plugin_helper.rb index e72fe355..232a2411 100644 --- a/spec/plugin_helper.rb +++ b/spec/plugin_helper.rb @@ -29,8 +29,10 @@ def authenticate_subscription CustomWizard::Subscription::Authentication.any_instance.stubs(:active?).returns(true) end -def enable_subscription +def enable_subscription(type) + # CustomWizard::Subscription.new CustomWizard::Subscription.any_instance.stubs(:subscribed?).returns(true) + CustomWizard::Subscription.any_instance.stubs(:type).returns(type) end def disable_subscription diff --git a/spec/requests/custom_wizard/custom_field_extensions_spec.rb b/spec/requests/custom_wizard/custom_field_extensions_spec.rb index 775e3ee0..19a8417a 100644 --- a/spec/requests/custom_wizard/custom_field_extensions_spec.rb +++ b/spec/requests/custom_wizard/custom_field_extensions_spec.rb @@ -40,8 +40,7 @@ describe "custom field extensions" do context "with a subscription" do before do - enable_subscription - + enable_subscription("business") subscription_custom_field_json['custom_fields'].each do |field_json| custom_field = CustomWizard::CustomField.new(nil, field_json) custom_field.save diff --git a/spec/requests/custom_wizard/steps_controller_spec.rb b/spec/requests/custom_wizard/steps_controller_spec.rb index 8396135c..b55de419 100644 --- a/spec/requests/custom_wizard/steps_controller_spec.rb +++ b/spec/requests/custom_wizard/steps_controller_spec.rb @@ -121,7 +121,7 @@ describe CustomWizard::StepsController do context "subscription" do before do - enable_subscription + enable_subscription("standard") end it "raises an error when user cant see the step due to conditions" do diff --git a/spec/serializers/custom_wizard/wizard_step_serializer_spec.rb b/spec/serializers/custom_wizard/wizard_step_serializer_spec.rb index 0a9f7e89..f0128765 100644 --- a/spec/serializers/custom_wizard/wizard_step_serializer_spec.rb +++ b/spec/serializers/custom_wizard/wizard_step_serializer_spec.rb @@ -34,7 +34,7 @@ describe CustomWizard::StepSerializer do context 'with required data' do before do - enable_subscription + enable_subscription("standard") wizard_template['steps'][0]['required_data'] = required_data_json['required_data'] wizard_template['steps'][0]['required_data_message'] = required_data_json['required_data_message'] CustomWizard::Template.save(wizard_template) From 584ee6d24edb0937d7a8846dc0bfb0d0bf4496e2 Mon Sep 17 00:00:00 2001 From: merefield Date: Tue, 9 Nov 2021 15:01:17 +0000 Subject: [PATCH 091/160] remove redundant returns --- lib/custom_wizard/subscription.rb | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/custom_wizard/subscription.rb b/lib/custom_wizard/subscription.rb index 7d94eb89..1ab53dc9 100644 --- a/lib/custom_wizard/subscription.rb +++ b/lib/custom_wizard/subscription.rb @@ -61,23 +61,23 @@ class CustomWizard::Subscription when "actions" case self.type when "business" - return [] + [] when "standard" - return SUBSCRIPTION_LEVELS[:business][kategory.to_sym] + SUBSCRIPTION_LEVELS[:business][kategory.to_sym] else - return SUBSCRIPTION_LEVELS[:standard][kategory.to_sym] + SUBSCRIPTION_LEVELS[:business][kategory.to_sym] + SUBSCRIPTION_LEVELS[:standard][kategory.to_sym] + SUBSCRIPTION_LEVELS[:business][kategory.to_sym] end when "custom_fields" case self.type when "business" - return [] + [] when "standard" - return SUBSCRIPTION_LEVELS[:business][kategory.to_sym][sub_kategory.to_sym]; + SUBSCRIPTION_LEVELS[:business][kategory.to_sym][sub_kategory.to_sym]; else - return SUBSCRIPTION_LEVELS[:standard][kategory.to_sym][sub_kategory.to_sym] + SUBSCRIPTION_LEVELS[:business][kategory.to_sym][sub_kategory.to_sym] + SUBSCRIPTION_LEVELS[:standard][kategory.to_sym][sub_kategory.to_sym] + SUBSCRIPTION_LEVELS[:business][kategory.to_sym][sub_kategory.to_sym] end else - return [] + [] end end From e37b2a6e852544e71622366a24fcacfe31736ff7 Mon Sep 17 00:00:00 2001 From: merefield Date: Tue, 9 Nov 2021 15:04:04 +0000 Subject: [PATCH 092/160] Remove semi colon! --- lib/custom_wizard/subscription.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/custom_wizard/subscription.rb b/lib/custom_wizard/subscription.rb index 1ab53dc9..7bac7f59 100644 --- a/lib/custom_wizard/subscription.rb +++ b/lib/custom_wizard/subscription.rb @@ -72,7 +72,7 @@ class CustomWizard::Subscription when "business" [] when "standard" - SUBSCRIPTION_LEVELS[:business][kategory.to_sym][sub_kategory.to_sym]; + SUBSCRIPTION_LEVELS[:business][kategory.to_sym][sub_kategory.to_sym] else SUBSCRIPTION_LEVELS[:standard][kategory.to_sym][sub_kategory.to_sym] + SUBSCRIPTION_LEVELS[:business][kategory.to_sym][sub_kategory.to_sym] end From 457463f7c76fc79accff00b8509165f4d7df70c4 Mon Sep 17 00:00:00 2001 From: angusmcleod Date: Wed, 10 Nov 2021 22:25:42 +0800 Subject: [PATCH 093/160] Add blankspace --- lib/custom_wizard/custom_field.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/custom_wizard/custom_field.rb b/lib/custom_wizard/custom_field.rb index 99dcfbbb..ed0c9594 100644 --- a/lib/custom_wizard/custom_field.rb +++ b/lib/custom_wizard/custom_field.rb @@ -83,6 +83,7 @@ class ::CustomWizard::CustomField add_error(I18n.t("#{i18n_key}.unsupported_class", class: value)) next end + if attr == 'klass' && @subscription.requires_additional_subscription("custom_fields", "klass").include?(value) add_error(I18n.t("wizard.custom_field.error.subscription_type", type: value)) end From 81bb7e56c254c9912882f6b27434e00cdb9aef53 Mon Sep 17 00:00:00 2001 From: angusmcleod Date: Mon, 1 Nov 2021 21:52:29 +0800 Subject: [PATCH 094/160] WIP --- .../components/wizard-notice-row.js.es6 | 14 + .../discourse/components/wizard-notice.js.es6 | 43 +-- .../custom-wizard-critical-notice.hbs | 5 + .../custom-wizard-critical-notice.js.es6 | 19 ++ .../custom-wizard-important-notice.hbs | 3 - .../custom-wizard-important-notice.js.es6 | 16 -- .../controllers/admin-wizards-notices.js.es6 | 67 +++++ .../controllers/admin-wizards.js.es6 | 38 +-- .../custom-wizard-admin-route-map.js.es6 | 5 + .../discourse/helpers/notice-badge.js.es6 | 41 +++ .../initializers/custom-wizard-edits.js.es6 | 62 +++-- .../discourse/mixins/notice-message.js.es6 | 65 +++++ .../models/custom-wizard-notice.js.es6 | 67 ++++- .../routes/admin-wizards-notices.js.es6 | 15 ++ .../discourse/routes/admin-wizards.js.es6 | 13 +- .../templates/admin-wizards-notices.hbs | 49 ++++ .../discourse/templates/admin-wizards.hbs | 9 +- .../components/wizard-notice-row.hbs | 30 +++ .../templates/components/wizard-notice.hbs | 80 +++--- assets/stylesheets/admin/admin.scss | 183 ++++++++----- assets/stylesheets/admin/wizard/manager.scss | 4 - config/locales/client.en.yml | 36 ++- config/locales/server.en.yml | 22 +- config/routes.rb | 5 +- controllers/custom_wizard/admin/admin.rb | 8 +- controllers/custom_wizard/admin/notice.rb | 52 +++- lib/custom_wizard/notice.rb | 244 +++++++++++++----- lib/custom_wizard/notice/connection_error.rb | 61 ++--- plugin.rb | 5 +- .../custom_wizard/notice_serializer.rb | 10 +- 30 files changed, 930 insertions(+), 341 deletions(-) create mode 100644 assets/javascripts/discourse/components/wizard-notice-row.js.es6 create mode 100644 assets/javascripts/discourse/connectors/admin-dashboard-top/custom-wizard-critical-notice.hbs create mode 100644 assets/javascripts/discourse/connectors/admin-dashboard-top/custom-wizard-critical-notice.js.es6 delete mode 100644 assets/javascripts/discourse/connectors/admin-dashboard-top/custom-wizard-important-notice.hbs delete mode 100644 assets/javascripts/discourse/connectors/admin-dashboard-top/custom-wizard-important-notice.js.es6 create mode 100644 assets/javascripts/discourse/controllers/admin-wizards-notices.js.es6 create mode 100644 assets/javascripts/discourse/helpers/notice-badge.js.es6 create mode 100644 assets/javascripts/discourse/mixins/notice-message.js.es6 create mode 100644 assets/javascripts/discourse/routes/admin-wizards-notices.js.es6 create mode 100644 assets/javascripts/discourse/templates/admin-wizards-notices.hbs create mode 100644 assets/javascripts/discourse/templates/components/wizard-notice-row.hbs diff --git a/assets/javascripts/discourse/components/wizard-notice-row.js.es6 b/assets/javascripts/discourse/components/wizard-notice-row.js.es6 new file mode 100644 index 00000000..9c099b39 --- /dev/null +++ b/assets/javascripts/discourse/components/wizard-notice-row.js.es6 @@ -0,0 +1,14 @@ +import Component from "@ember/component"; +import NoticeMessage from "../mixins/notice-message"; + +export default Component.extend(NoticeMessage, { + tagName: "tr", + attributeBindings: ["notice.id:data-notice-id"], + classNameBindings: [":wizard-notice-row", "notice.typeClass", "notice.expired:expired", "notice.dismissed:dismissed"], + + actions: { + dismiss() { + this.notice.dismiss(); + } + } +}); \ No newline at end of file diff --git a/assets/javascripts/discourse/components/wizard-notice.js.es6 b/assets/javascripts/discourse/components/wizard-notice.js.es6 index fcd77606..cac3e4eb 100644 --- a/assets/javascripts/discourse/components/wizard-notice.js.es6 +++ b/assets/javascripts/discourse/components/wizard-notice.js.es6 @@ -1,35 +1,9 @@ import Component from "@ember/component"; -import discourseComputed from "discourse-common/utils/decorators"; -import { not, notEmpty } from "@ember/object/computed"; -import I18n from "I18n"; +import NoticeMessage from "../mixins/notice-message"; -export default Component.extend({ - classNameBindings: [ - ":wizard-notice", - "notice.type", - "dismissed", - "expired", - "resolved", - ], - showFull: false, - resolved: notEmpty("notice.expired_at"), - dismissed: notEmpty("notice.dismissed_at"), - canDismiss: not("dismissed"), - - @discourseComputed("notice.type") - title(type) { - return I18n.t(`admin.wizard.notice.title.${type}`); - }, - - @discourseComputed("notice.type") - icon(type) { - return { - plugin_status_warning: "exclamation-circle", - plugin_status_connection_error: "bolt", - subscription_messages_connection_error: "bolt", - info: "info-circle", - }[type]; - }, +export default Component.extend(NoticeMessage, { + attributeBindings: ["notice.id:data-notice-id"], + classNameBindings: [':wizard-notice', 'notice.typeClass', 'notice.dismissed:dismissed', 'notice.expired:expired', 'notice.hidden:hidden'], actions: { dismiss() { @@ -38,5 +12,12 @@ export default Component.extend({ this.set("dismissing", false); }); }, - }, + + hide() { + this.set('hiding', true); + this.notice.hide().then(() => { + this.set('hiding', false); + }); + }, + } }); diff --git a/assets/javascripts/discourse/connectors/admin-dashboard-top/custom-wizard-critical-notice.hbs b/assets/javascripts/discourse/connectors/admin-dashboard-top/custom-wizard-critical-notice.hbs new file mode 100644 index 00000000..9d96bed9 --- /dev/null +++ b/assets/javascripts/discourse/connectors/admin-dashboard-top/custom-wizard-critical-notice.hbs @@ -0,0 +1,5 @@ +{{#if notices}} + {{#each notices as |notice|}} + {{wizard-notice notice=notice showPlugin=true}} + {{/each}} +{{/if}} diff --git a/assets/javascripts/discourse/connectors/admin-dashboard-top/custom-wizard-critical-notice.js.es6 b/assets/javascripts/discourse/connectors/admin-dashboard-top/custom-wizard-critical-notice.js.es6 new file mode 100644 index 00000000..0bb252e9 --- /dev/null +++ b/assets/javascripts/discourse/connectors/admin-dashboard-top/custom-wizard-critical-notice.js.es6 @@ -0,0 +1,19 @@ +import { getOwner } from "discourse-common/lib/get-owner"; + +export default { + shouldRender(attrs, ctx) { + return ctx.siteSettings.wizard_critical_notices_on_dashboard; + }, + + setupComponent(attrs, component) { + const controller = getOwner(this).lookup('controller:admin-dashboard'); + + component.set('notices', controller.get('customWizardCriticalNotices')); + controller.addObserver('customWizardCriticalNotices.[]', () => { + if (this._state === "destroying") { + return; + } + component.set('notices', controller.get('customWizardCriticalNotices')); + }); + } +}; \ No newline at end of file diff --git a/assets/javascripts/discourse/connectors/admin-dashboard-top/custom-wizard-important-notice.hbs b/assets/javascripts/discourse/connectors/admin-dashboard-top/custom-wizard-important-notice.hbs deleted file mode 100644 index 9b01c468..00000000 --- a/assets/javascripts/discourse/connectors/admin-dashboard-top/custom-wizard-important-notice.hbs +++ /dev/null @@ -1,3 +0,0 @@ -{{#if importantNotice}} - {{wizard-notice notice=importantNotice importantOnDashboard=true}} -{{/if}} diff --git a/assets/javascripts/discourse/connectors/admin-dashboard-top/custom-wizard-important-notice.js.es6 b/assets/javascripts/discourse/connectors/admin-dashboard-top/custom-wizard-important-notice.js.es6 deleted file mode 100644 index 5962f255..00000000 --- a/assets/javascripts/discourse/connectors/admin-dashboard-top/custom-wizard-important-notice.js.es6 +++ /dev/null @@ -1,16 +0,0 @@ -import { getOwner } from "discourse-common/lib/get-owner"; - -export default { - shouldRender(attrs, ctx) { - return ctx.siteSettings.wizard_important_notices_on_dashboard; - }, - - setupComponent() { - const controller = getOwner(this).lookup("controller:admin-dashboard"); - const importantNotice = controller.get("customWizardImportantNotice"); - - if (importantNotice) { - this.set("importantNotice", importantNotice); - } - }, -}; diff --git a/assets/javascripts/discourse/controllers/admin-wizards-notices.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards-notices.js.es6 new file mode 100644 index 00000000..0f67f878 --- /dev/null +++ b/assets/javascripts/discourse/controllers/admin-wizards-notices.js.es6 @@ -0,0 +1,67 @@ +import Controller from "@ember/controller"; +import CustomWizardNotice from "../models/custom-wizard-notice"; +import discourseComputed from "discourse-common/utils/decorators"; +import { notEmpty } from "@ember/object/computed"; +import { A } from "@ember/array"; +import I18n from "I18n"; + +export default Controller.extend({ + messageUrl: "https://thepavilion.io/t/3652", + messageKey: "info", + messageIcon: "info-circle", + messageClass: "info", + hasNotices: notEmpty("notices"), + page: 0, + loadingMore: false, + canLoadMore: true, + + @discourseComputed('notices.[]', 'notices.@each.dismissed') + allDismisssed(notices) { + return notices.every(n => !n.canDismiss || n.dismissed); + }, + + loadMoreNotices() { + if (!this.canLoadMore) { + return; + } + const page = this.get("page"); + this.set("loadingMore", true); + + CustomWizardNotice.list({ page, include_all: true }) + .then((result) => { + if (result.notices.length === 0) { + this.set("canLoadMore", false); + return; + } + + this.get("notices").pushObjects( + A(result.notices.map(notice => CustomWizardNotice.create(notice))) + ); + }) + .finally(() => this.set("loadingMore", false)); + }, + + actions: { + loadMore() { + if (this.canLoadMore) { + this.set("page", this.page + 1); + this.loadMoreNotices(); + } + }, + + dismissAll() { + bootbox.confirm( + I18n.t("admin.wizard.notice.dismiss_all.confirm"), + I18n.t("no_value"), + I18n.t("yes_value"), + (result) => { + if (result) { + this.set('loadingMore', true); + CustomWizardNotice.dismissAll() + .finally(() => this.set("loadingMore", false)); + } + } + ); + } + } +}); diff --git a/assets/javascripts/discourse/controllers/admin-wizards.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards.js.es6 index d128d851..e2672fe4 100644 --- a/assets/javascripts/discourse/controllers/admin-wizards.js.es6 +++ b/assets/javascripts/discourse/controllers/admin-wizards.js.es6 @@ -1,20 +1,26 @@ -import Controller from "@ember/controller"; -import { popupAjaxError } from "discourse/lib/ajax-error"; -import { ajax } from "discourse/lib/ajax"; +import Controller, { inject as controller } from "@ember/controller"; +import { isPresent } from "@ember/utils"; +import { A } from "@ember/array"; export default Controller.extend({ - actions: { - dismissNotice(noticeId) { - ajax(`/admin/wizards/notice/${this.id}`, { - type: "DELETE", - }) - .then((result) => { - if (result.success) { - const notices = this.notices; - notices.removeObject(notices.findBy("id", noticeId)); - } - }) - .catch(popupAjaxError); - }, + adminWizardsNotices: controller(), + + unsubscribe() { + this.messageBus.unsubscribe("/custom-wizard/notices"); }, + + subscribe() { + this.unsubscribe(); + this.messageBus.subscribe("/custom-wizard/notices", (data) => { + if (isPresent(data.active_notice_count)) { + this.set("activeNoticeCount", data.active_notice_count); + this.adminWizardsNotices.setProperties({ + notices: A(), + page: 0, + canLoadMore: true + }); + this.adminWizardsNotices.loadMoreNotices(); + } + }); + } }); diff --git a/assets/javascripts/discourse/custom-wizard-admin-route-map.js.es6 b/assets/javascripts/discourse/custom-wizard-admin-route-map.js.es6 index 67b91f87..c3c95e48 100644 --- a/assets/javascripts/discourse/custom-wizard-admin-route-map.js.es6 +++ b/assets/javascripts/discourse/custom-wizard-admin-route-map.js.es6 @@ -63,6 +63,11 @@ export default { path: "/subscription", resetNamespace: true, }); + + this.route("adminWizardsNotices", { + path: "/notices", + resetNamespace: true, + }); } ); }, diff --git a/assets/javascripts/discourse/helpers/notice-badge.js.es6 b/assets/javascripts/discourse/helpers/notice-badge.js.es6 new file mode 100644 index 00000000..bc5df4a6 --- /dev/null +++ b/assets/javascripts/discourse/helpers/notice-badge.js.es6 @@ -0,0 +1,41 @@ +import { autoUpdatingRelativeAge } from "discourse/lib/formatter"; +import { iconHTML } from "discourse-common/lib/icon-library"; +import I18n from "I18n"; +import { registerUnbound } from "discourse-common/lib/helpers"; +import { htmlSafe } from "@ember/template"; + +registerUnbound("notice-badge", function(attrs) { + let tag = attrs.url ? 'a' : 'div'; + let attrStr = ''; + if (attrs.title) { + attrStr += `title='${I18n.t(attrs.title)}'`; + } + if (attrs.url) { + attrStr += `href='${attrs.url}'`; + } + let html = `<${tag} class="${attrs.class ? `${attrs.class} ` : ''}notice-badge" ${attrStr}>`; + if (attrs.icon) { + html += iconHTML(attrs.icon); + } + if (attrs.label) { + if (attrs.icon) { + html += ' '; + } + html += `${I18n.t(attrs.label)}`; + } + if (attrs.date) { + if (attrs.icon || attrs.label) { + html += ' '; + } + let dateAttrs = {}; + if (attrs.leaveAgo) { + dateAttrs = { + format: "medium", + leaveAgo: true + }; + } + html += autoUpdatingRelativeAge(new Date(attrs.date), dateAttrs); + } + html += ``; + return htmlSafe(html); +}); \ No newline at end of file diff --git a/assets/javascripts/discourse/initializers/custom-wizard-edits.js.es6 b/assets/javascripts/discourse/initializers/custom-wizard-edits.js.es6 index 21a9d745..8208cd20 100644 --- a/assets/javascripts/discourse/initializers/custom-wizard-edits.js.es6 +++ b/assets/javascripts/discourse/initializers/custom-wizard-edits.js.es6 @@ -1,6 +1,7 @@ import DiscourseURL from "discourse/lib/url"; import { withPluginApi } from "discourse/lib/plugin-api"; import CustomWizardNotice from "../models/custom-wizard-notice"; +import { isPresent } from "@ember/utils"; import { A } from "@ember/array"; export default { @@ -21,37 +22,46 @@ export default { }; withPluginApi("0.8.36", (api) => { - api.modifyClass("route:admin-dashboard", { - afterModel() { - return CustomWizardNotice.list().then((result) => { - if (result && result.length) { - this.set( - "notices", - A(result.map((n) => CustomWizardNotice.create(n))) - ); + api.modifyClass('route:admin-dashboard', { + setupController(controller) { + this._super(...arguments); + + controller.loadCriticalNotices(); + controller.subscribe(); + } + }); + + api.modifyClass('controller:admin-dashboard', { + criticalNotices: A(), + + unsubscribe() { + this.messageBus.unsubscribe("/custom-wizard/notices"); + }, + + subscribe() { + this.unsubscribe(); + this.messageBus.subscribe("/custom-wizard/notices", (data) => { + if (isPresent(data.active_notice_count)) { + this.loadCriticalNotices(); } }); }, - setupController(controller) { - if (this.notices) { - let pluginStatusConnectionError = this.notices.filter( - (n) => n.type === "plugin_status_connection_error" - )[0]; - let pluginStatusWarning = this.notices.filter( - (n) => n.type === "plugin_status_warning" - )[0]; - - if (pluginStatusConnectionError || pluginStatusWarning) { - controller.set( - "customWizardImportantNotice", - pluginStatusConnectionError || pluginStatusWarning - ); + loadCriticalNotices() { + CustomWizardNotice.list({ + type: [ + 'connection_error', + 'warning' + ], + archetype: 'plugin_status', + visible: true + }).then(result => { + if (result.notices && result.notices.length) { + const criticalNotices = A(result.notices.map(n => CustomWizardNotice.create(n))); + this.set('customWizardCriticalNotices', criticalNotices); } - } - - this._super(...arguments); - }, + }); + } }); }); }, diff --git a/assets/javascripts/discourse/mixins/notice-message.js.es6 b/assets/javascripts/discourse/mixins/notice-message.js.es6 new file mode 100644 index 00000000..492df643 --- /dev/null +++ b/assets/javascripts/discourse/mixins/notice-message.js.es6 @@ -0,0 +1,65 @@ +import Mixin from "@ember/object/mixin"; +import { bind, scheduleOnce } from "@ember/runloop"; +import { cookAsync } from "discourse/lib/text"; +import { createPopper } from "@popperjs/core"; + +export default Mixin.create({ + showCookedMessage: false, + + didReceiveAttrs(){ + const message = this.notice.message; + cookAsync(message).then((cooked) => { + this.set("cookedMessage", cooked); + }); + }, + + createMessageModal() { + let container = this.element.querySelector('.notice-message'); + let modal = this.element.querySelector('.cooked-notice-message'); + + this._popper = createPopper( + container, + modal, { + strategy: "absolute", + placement: "bottom-start", + modifiers: [ + { + name: "preventOverflow", + }, + { + name: "offset", + options: { + offset: [0, 5], + }, + }, + ], + } + ); + }, + + didInsertElement() { + $(document).on("click", bind(this, this.documentClick)); + }, + + willDestroyElement() { + $(document).off("click", bind(this, this.documentClick)); + }, + + documentClick(event) { + if (this._state === "destroying") { return; } + + if (!event.target.closest(`[data-notice-id="${this.notice.id}"] .notice-message`)) { + this.set('showCookedMessage', false); + } + }, + + actions: { + toggleCookedMessage() { + this.toggleProperty("showCookedMessage"); + + if (this.showCookedMessage) { + scheduleOnce("afterRender", this, this.createMessageModal); + } + } + } +}); \ No newline at end of file diff --git a/assets/javascripts/discourse/models/custom-wizard-notice.js.es6 b/assets/javascripts/discourse/models/custom-wizard-notice.js.es6 index 1eb21e73..29e30628 100644 --- a/assets/javascripts/discourse/models/custom-wizard-notice.js.es6 +++ b/assets/javascripts/discourse/models/custom-wizard-notice.js.es6 @@ -1,25 +1,68 @@ import EmberObject from "@ember/object"; +import discourseComputed from "discourse-common/utils/decorators"; import { ajax } from "discourse/lib/ajax"; import { popupAjaxError } from "discourse/lib/ajax-error"; +import { and, not, notEmpty } from "@ember/object/computed"; +import { dasherize } from "@ember/string"; +import I18n from "I18n"; -const CustomWizardNotice = EmberObject.extend(); +const CustomWizardNotice = EmberObject.extend({ + expired: notEmpty('expired_at'), + dismissed: notEmpty('dismissed_at'), + hidden: notEmpty('hidden_at'), + notHidden: not('hidden'), + notDismissed: not('dismissed'), + canDismiss: and('dismissable', 'notDismissed'), + canHide: and('can_hide', 'notHidden'), -CustomWizardNotice.reopen({ - dismiss() { - return ajax(`/admin/wizards/notice/${this.id}`, { type: "PUT" }) - .then((result) => { - if (result.success) { - this.set("dismissed_at", result.dismissed_at); - } - }) - .catch(popupAjaxError); + @discourseComputed('type') + typeClass(type) { + return dasherize(type); }, + + @discourseComputed('type') + typeLabel(type) { + return I18n.t(`admin.wizard.notice.type.${type}`); + }, + + dismiss() { + if (!this.get('canDismiss')) { + return; + } + + return ajax(`/admin/wizards/notice/${this.get('id')}/dismiss`, { type: 'PUT' }).then(result => { + if (result.success) { + this.set('dismissed_at', result.dismissed_at); + } + }).catch(popupAjaxError); + }, + + hide() { + if (!this.get('canHide')) { + return; + } + + return ajax(`/admin/wizards/notice/${this.get('id')}/hide`, { type: 'PUT' }).then(result => { + if (result.success) { + this.set('hidden_at', result.hidden_at); + } + }).catch(popupAjaxError); + } }); CustomWizardNotice.reopenClass({ - list() { - return ajax("/admin/wizards/notice").catch(popupAjaxError); + list(data = {}) { + return ajax('/admin/wizards/notice', { + type: "GET", + data + }).catch(popupAjaxError); }, + + dismissAll() { + return ajax('/admin/wizards/notice/dismiss', { + type: "PUT" + }).catch(popupAjaxError); + } }); export default CustomWizardNotice; diff --git a/assets/javascripts/discourse/routes/admin-wizards-notices.js.es6 b/assets/javascripts/discourse/routes/admin-wizards-notices.js.es6 new file mode 100644 index 00000000..a329ce95 --- /dev/null +++ b/assets/javascripts/discourse/routes/admin-wizards-notices.js.es6 @@ -0,0 +1,15 @@ +import CustomWizardNotice from "../models/custom-wizard-notice"; +import DiscourseRoute from "discourse/routes/discourse"; +import { A } from "@ember/array"; + +export default DiscourseRoute.extend({ + model() { + return CustomWizardNotice.list({ include_all: true }); + }, + + setupController(controller, model) { + controller.setProperties({ + notices: A(model.notices.map(notice => CustomWizardNotice.create(notice))), + }); + }, +}); diff --git a/assets/javascripts/discourse/routes/admin-wizards.js.es6 b/assets/javascripts/discourse/routes/admin-wizards.js.es6 index de67a8b9..2bbc73a9 100644 --- a/assets/javascripts/discourse/routes/admin-wizards.js.es6 +++ b/assets/javascripts/discourse/routes/admin-wizards.js.es6 @@ -1,6 +1,5 @@ import DiscourseRoute from "discourse/routes/discourse"; import { ajax } from "discourse/lib/ajax"; -import { A } from "@ember/array"; export default DiscourseRoute.extend({ model() { @@ -8,13 +7,21 @@ export default DiscourseRoute.extend({ }, setupController(controller, model) { - controller.set("notices", A(model.notices)); controller.set("api_section", model.api_section); + + if (model.active_notice_count) { + controller.set("activeNoticeCount", model.active_notice_count); + } + if (model.featured_notices) { + controller.set("featuredNotices", model.featured_notices); + } + + controller.subscribe(); }, afterModel(model, transition) { if (transition.targetName === "adminWizards.index") { this.transitionTo("adminWizardsWizard"); } - }, + } }); diff --git a/assets/javascripts/discourse/templates/admin-wizards-notices.hbs b/assets/javascripts/discourse/templates/admin-wizards-notices.hbs new file mode 100644 index 00000000..d522c1a5 --- /dev/null +++ b/assets/javascripts/discourse/templates/admin-wizards-notices.hbs @@ -0,0 +1,49 @@ +
+

{{i18n "admin.wizard.notices.title"}}

+ +
+ {{d-button + label="admin.wizard.notice.dismiss_all.label" + title="admin.wizard.notice.dismiss_all.title" + action=(action "dismissAll") + disabled=allDismisssed + icon="check"}} +
+
+ +{{wizard-message + key=messageKey + url=messageUrl + type=messageType + opts=messageOpts + items=messageItems + loading=loading + component="notices"}} + +
+ {{#load-more selector=".wizard-table tr" action=(action "loadMore")}} + {{#if hasNotices}} + + + + + + + + + + + {{#each notices as |notice|}} + {{wizard-notice-row notice=notice}} + {{/each}} + +
{{I18n "admin.wizard.notice.time"}}{{I18n "admin.wizard.notice.type.label"}}{{I18n "admin.wizard.notice.title"}}{{I18n "admin.wizard.notice.status"}}
+ {{else}} + {{#unless loadingMore}} +

{{i18n "search.no_results"}}

+ {{/unless}} + {{/if}} + + {{conditional-loading-spinner condition=loadingMore}} + {{/load-more}} +
\ No newline at end of file diff --git a/assets/javascripts/discourse/templates/admin-wizards.hbs b/assets/javascripts/discourse/templates/admin-wizards.hbs index aa3b2cf5..4447380f 100644 --- a/assets/javascripts/discourse/templates/admin-wizards.hbs +++ b/assets/javascripts/discourse/templates/admin-wizards.hbs @@ -10,6 +10,12 @@ {{nav-item route="adminWizardsSubscription" label="admin.wizard.subscription.nav_label"}}
+ {{d-icon "far-life-ring"}}{{i18n "admin.wizard.support_button.label"}} @@ -17,8 +23,5 @@ {{/admin-nav}}
- {{#each notices as |notice|}} - {{wizard-notice notice=notice}} - {{/each}} {{outlet}}
diff --git a/assets/javascripts/discourse/templates/components/wizard-notice-row.hbs b/assets/javascripts/discourse/templates/components/wizard-notice-row.hbs new file mode 100644 index 00000000..7f97b250 --- /dev/null +++ b/assets/javascripts/discourse/templates/components/wizard-notice-row.hbs @@ -0,0 +1,30 @@ + + {{#if notice.updated_at}} + {{notice-badge class="notice-updated-at" date=notice.updated_at label="admin.wizard.notice.updated_at" leaveAgo=true}} + {{else}} + {{notice-badge class="notice-created-at" date=notice.created_at label="admin.wizard.notice.created_at" leaveAgo=true}} + {{/if}} + +{{notice.typeLabel}} + + {{notice.title}} + {{#if showCookedMessage}} + {{cookedMessage}} + {{/if}} + + + {{#if notice.canDismiss}} + {{d-button + action="dismiss" + label="admin.wizard.notice.dismiss.label" + title="admin.wizard.notice.dismiss.title" + class="btn-dismiss" + icon="check"}} + {{else if notice.dismissed}} + {{i18n "admin.wizard.notice.dismissed_at"}} {{format-date notice.dismissed_at leaveAgo="true"}} + {{else if notice.expired}} + {{i18n "admin.wizard.notice.expired_at"}} {{format-date notice.expired_at leaveAgo="true"}} + {{else}} + {{i18n "admin.wizard.notice.active"}} + {{/if}} + \ No newline at end of file diff --git a/assets/javascripts/discourse/templates/components/wizard-notice.hbs b/assets/javascripts/discourse/templates/components/wizard-notice.hbs index 89bbf5d4..24a853d3 100644 --- a/assets/javascripts/discourse/templates/components/wizard-notice.hbs +++ b/assets/javascripts/discourse/templates/components/wizard-notice.hbs @@ -1,51 +1,39 @@
- {{#if resolved}} -
- {{d-icon "check"}} - {{i18n "admin.wizard.notice.resolved"}} - {{format-date notice.expired_at leaveAgo="true"}} -
- {{/if}} - -
- {{d-icon icon}} - {{title}} +
+ {{notice.title}} + {{#if showCookedMessage}} + {{cookedMessage}} + {{/if}}
+
+ {{#if notice.expired}} + {{notice-badge class="notice-expired-at" icon="check" label="admin.wizard.notice.expired_at" date=notice.expired_at}} + {{/if}} + {{#if showPlugin}} + {{notice-badge class="notice-plugin" icon="plug" title="admin.wizard.notice.plugin" label="admin.wizard.notice.plugin" url="/admin/wizards/notices"}} + {{/if}} + {{notice-badge class="notice-created-at" icon="far-clock" label="admin.wizard.notice.created_at" date=notice.created_at leaveAgo=true}} + {{#if notice.updated_at}} + {{notice-badge class="notice-updated-at" icon="far-clock" label="admin.wizard.notice.updated_at" date=notice.updated_at}} + {{/if}} -
- {{d-icon "far-clock"}} - {{i18n "admin.wizard.notice.issued"}} - {{format-date notice.created_at leaveAgo="true"}} -
- - {{#if notice.updated_at}} -
- {{d-icon "calendar-alt"}} - {{i18n "admin.wizard.notice.updated"}} - {{format-date notice.updated_at leaveAgo="true"}} -
- {{/if}} - -
- {{d-icon "plug"}} - {{i18n "admin.wizard.notice.plugin"}} + {{#if notice.canDismiss}} +
+ {{#if dismissing}} + {{loading-spinner size="small"}} + {{else}} + {{d-icon "times"}} + {{/if}} +
+ {{/if}} + {{#if notice.canHide}} +
+ {{#if hiding}} + {{loading-spinner size="small"}} + {{else}} + {{d-icon "far-eye-slash"}} + {{/if}} +
+ {{/if}}
- -
- {{html-safe notice.message}} -
- -{{#if importantOnDashboard}} - - {{i18n "admin.wizard.notice.disable_important_on_dashboard"}} - -{{/if}} - -{{#if canDismiss}} - {{#if dismissing}} - {{loading-spinner size="small"}} - {{else}} - {{d-icon "times"}} - {{/if}} -{{/if}} diff --git a/assets/stylesheets/admin/admin.scss b/assets/stylesheets/admin/admin.scss index 94fe6839..e3790465 100644 --- a/assets/stylesheets/admin/admin.scss +++ b/assets/stylesheets/admin/admin.scss @@ -4,15 +4,25 @@ @import "common/components/buttons"; @import "wizard/variables"; +$expired: #339b18; +$info: #038ae7; +$warning: #d47e00; +$error: #ef1700; + .admin-wizard-controls { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; + min-height: 34px; & + .wizard-message + div { margin-top: 20px; } + + h3 { + margin-bottom: 0; + } } .wizard-message { @@ -909,87 +919,142 @@ } } +.admin-wizards-notices { + .wizard-table { + overflow: unset; + } +} + .wizard-notice { - padding: 1em; + padding: .75em; margin-bottom: 1em; - border: 1px solid var(--primary); - position: relative; + border: 1px solid var(--primary-low); &.dismissed { display: none; } - &.resolved .notice-badge:not(.notice-expired-at), - &.resolved a, - &.resolved p { + &.expired .notice-badge:not(.notice-expired-at), + &.expired a, + &.expired p { color: var(--primary-medium) !important; } - .d-icon { - margin-right: 0.4em; + .notice-badge { + padding: 0 .5em; } .notice-header { display: flex; - } - - .notice-badge { - border: 1px solid var(--primary); - display: inline-flex; - align-items: center; - padding: 0 0.5em; - margin-right: 1em; - font-size: 0.9em; - line-height: 25px; - min-height: 25px; - box-sizing: border-box; - - &:last-of-type { - margin-right: 0; - } - } - - &.warning { - .notice-expired-at { - border: 1px solid var(--success); - background-color: rgba($success, 0.1); - color: var(--success); - } .notice-title { - border: 1px solid var(--pavilion-warning); - background-color: rgba($pavilion_warning, 0.1); - color: var(--pavilion-warning); + padding: 0; + } + + .notice-header-right { + margin-left: auto; + display: flex; + align-items: center; + + .notice-badge { + margin-left: .5em; + } } } - .notice-issued, - .notice-resolved { - margin-right: 0.3em; - } - - .notice-message { - p { - margin: 0.5em 0; - } - - p:last-of-type { - margin-bottom: 0; - } + .dismiss-notice-container, + .hide-notice-container { + width: 40px; + display: flex; + justify-content: center; + align-items: center; } .dismiss-notice, - .spinner { - position: absolute; - top: 1em; - right: 1em; - color: var(--primary-medium); - } + .hide-notice { + display: flex; + align-items: center; - .disable-important { - position: absolute; - right: 3em; - top: 1em; - color: var(--primary-medium); + .d-icon { + margin-right: 0; + color: var(--primary); + } } } + +.notice-badge { + display: inline-flex; + align-items: center; + min-height: 25px; + box-sizing: border-box; + color: var(--primary) !important; +} + +.admin-actions { + display: flex; + align-items: center; +} + +.wizard-notices-link { + position: relative; + margin-right: 10px; + + div > a { + @include btn; + color: var(--secondary) !important; + background-color: var(--primary-medium); + + &.active { + background-color: var(--tertiary) !important; + color: var(--secondary) !important; + } + } +} + +.active-notice-count { + background-color: $danger; + color: $secondary; + border-radius: 50%; + width: 18px; + height: 18px; + line-height: 18px; + text-align: center; + position: absolute; + top: -8px; + right: -8px; + font-size: .7em; +} + +a.show-notice-message { + padding: .25em .5em; + color: var(--primary); +} + +.wizard-notice, +.wizard-notice-row:not(.expired):not(.dismissed) { + &.info { + background-color: rgba($info, 0.1); + border: 1px solid rgba($info, 0.5); + } + &.warning, + &.connection-error { + background-color: rgba($warning, 0.1); + border: 1px solid rgba($warning, 0.5); + } +} + +.notice-message { + position: relative; + + .cooked-notice-message { + background-color: var(--secondary); + padding: 1em; + z-index: 1; + box-shadow: shadow("dropdown"); + border-top: 1px solid var(--primary-low); + + p { + margin: 0; + } + } +} \ No newline at end of file diff --git a/assets/stylesheets/admin/wizard/manager.scss b/assets/stylesheets/admin/wizard/manager.scss index a3b30c98..69f963fe 100644 --- a/assets/stylesheets/admin/wizard/manager.scss +++ b/assets/stylesheets/admin/wizard/manager.scss @@ -2,10 +2,6 @@ display: flex; justify-content: flex-start; - h3 { - margin-bottom: 0; - } - .buttons { display: flex; margin-left: auto; diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 7c3b449b..9ffeda37 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -112,6 +112,9 @@ en: select: "Select a wizard to see its logs" viewing: "View recent logs for wizards on the forum" documentation: "Check out the logs documentation" + notices: + info: "Plugin status and subscription notices" + documentation: Check out the notices documentation editor: show: "Show" @@ -485,14 +488,31 @@ en: notice: plugin: Custom Wizard Plugin - issued: Issued - update: Updated - resolved: Resolved - title: - plugin_status_warning: Warning Notice - plugin_status_connection_error: Connection Notice - subscription_messages_connection_error: Connection Notice - disable_important_on_dashboard: disable + message: Message + time: Time + status: Status + title: Title + dismiss: + label: Dismiss + title: Dismiss notice + dismiss_all: + label: Dismiss All + title: Dismiss all informational Custom Wizard notices + confirm: Are you sure you want to dismiss all informational Custom Wizard notices? + active: active + created_at: issued + updated_at: updated + expired_at: expired + dismissed_at: dismissed + type: + label: Type + info: Information + warning: Warning + connection_error: Connection Error + + notices: + nav_label: Notices + title: Plugin Notices wizard_js: group: diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index 5fd7c076..5c25690b 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -54,21 +54,21 @@ en: notice: connection_error: "Failed to connect to http://%{domain}" - compatibility_issue: > - The Custom Wizard Plugin has a compatibility issue with the latest version of Discourse. - Please check the Custom Wizard Plugin status on [%{domain}](http://%{domain}) before updating Discourse. + compatibility_issue: + title: The Custom Wizard Plugin is incompatibile with the latest version of Discourse. + message: Please check the Custom Wizard Plugin status on [%{domain}](http://%{domain}) before updating Discourse. plugin_status: - connection_error_limit: > - We're unable to connect to the Pavilion Plugin Status Server. Please check the Custom Wizard Plugin status on [%{domain}](http://%{domain}) before updating Discourse or the plugin. - If this connection issue persists please contact support@thepavilion.io for further assistance. - subscription_messages: - connection_error_limit: > - We're unable to connect to the Pavilion Subscription Server. This will not affect the operation of the plugin. - If this connection issue persists please contact support@thepavilion.io for further assistance. + connection_error: + title: Unable to connect to the Custom Wizard Plugin status server + message: Please check the Custom Wizard Plugin status on [%{domain}](http://%{domain}) before updating Discourse. If this issue persists contact support@thepavilion.io for further assistance. + subscription_message: + connection_error: + title: Unable to connect to the Custom Wizard Plugin subscription server + message: If this issue persists contact support@thepavilion.io for further assistance. site_settings: custom_wizard_enabled: "Enable custom wizards." wizard_redirect_exclude_paths: "Routes excluded from wizard redirects." wizard_recognised_image_upload_formats: "File types which will result in upload displaying an image preview" wizard_apis_enabled: "Enable API features (experimental)." - wizard_important_notices_on_dashboard: "Show important notices about the custom wizard plugin on the admin dashboard." + wizard_critical_notices_on_dashboard: "Show critical notices about the custom wizard plugin on the admin dashboard." diff --git a/config/routes.rb b/config/routes.rb index 0d59b200..3b5f8ca6 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -52,6 +52,9 @@ Discourse::Application.routes.append do delete 'admin/wizards/subscription/authorize' => 'admin_subscription#destroy_authentication' get 'admin/wizards/notice' => 'admin_notice#index' - put 'admin/wizards/notice/:notice_id' => 'admin_notice#dismiss' + put 'admin/wizards/notice/:notice_id/dismiss' => 'admin_notice#dismiss' + put 'admin/wizards/notice/:notice_id/hide' => 'admin_notice#hide' + put 'admin/wizards/notice/dismiss' => 'admin_notice#dismiss_all' + get 'admin/wizards/notices' => 'admin_notice#index' end end diff --git a/controllers/custom_wizard/admin/admin.rb b/controllers/custom_wizard/admin/admin.rb index 8a3b69ac..63a89833 100644 --- a/controllers/custom_wizard/admin/admin.rb +++ b/controllers/custom_wizard/admin/admin.rb @@ -6,8 +6,12 @@ class CustomWizard::AdminController < ::Admin::AdminController render_json_dump( #TODO replace with appropriate static? api_section: ["business"].include?(CustomWizard::Subscription.type), - notices: ActiveModel::ArraySerializer.new( - CustomWizard::Notice.list, + active_notice_count: CustomWizard::Notice.active_count, + featured_notices: ActiveModel::ArraySerializer.new( + CustomWizard::Notice.list( + type: CustomWizard::Notice.types[:info], + archetype: CustomWizard::Notice.archetypes[:subscription_message] + ), each_serializer: CustomWizard::NoticeSerializer ) ) diff --git a/controllers/custom_wizard/admin/notice.rb b/controllers/custom_wizard/admin/notice.rb index f28240e3..d6c43b5f 100644 --- a/controllers/custom_wizard/admin/notice.rb +++ b/controllers/custom_wizard/admin/notice.rb @@ -1,20 +1,66 @@ # frozen_string_literal: true class CustomWizard::AdminNoticeController < CustomWizard::AdminController - before_action :find_notice, only: [:dismiss] + before_action :find_notice, only: [:dismiss, :hide] def index - render_serialized(CustomWizard::Notice.list(include_recently_expired: true), CustomWizard::NoticeSerializer) + type = params[:type] + archetype = params[:archtype] + page = params[:page].to_i + include_all = ActiveRecord::Type::Boolean.new.cast(params[:include_all]) + visible = ActiveRecord::Type::Boolean.new.cast(params[:visible]) + + if type + if type.is_a?(Array) + type = type.map { |type| CustomWizard::Notice.types[type.to_sym] } + else + type = CustomWizard::Notice.types[type.to_sym] + end + end + + if archetype + if archetype.is_a?(Array) + archetype = archetype.map { |type| CustomWizard::Notice.archetypes[archetype.to_sym] } + else + archetype = CustomWizard::Notice.archetypes[archetype.to_sym] + end + end + + notices = CustomWizard::Notice.list( + include_all: include_all, + page: page, + type: type, + archetype: archetype, + visible: visible + ) + + render_serialized(notices, CustomWizard::NoticeSerializer, root: :notices) end def dismiss - if @notice.dismissable? && @notice.dismiss + if @notice.dismissable? && @notice.dismiss! render json: success_json.merge(dismissed_at: @notice.dismissed_at) else render json: failed_json end end + def hide + if @notice.can_hide? && @notice.hide! + render json: success_json.merge(hidden_at: @notice.hidden_at) + else + render json: failed_json + end + end + + def dismiss_all + if CustomWizard::Notice.dismiss_all + render json: success_json + else + render json: failed_json + end + end + def find_notice @notice = CustomWizard::Notice.find(params[:notice_id]) raise Discourse::InvalidParameters.new(:notice_id) unless @notice diff --git a/lib/custom_wizard/notice.rb b/lib/custom_wizard/notice.rb index 3ba710c5..7266178d 100644 --- a/lib/custom_wizard/notice.rb +++ b/lib/custom_wizard/notice.rb @@ -7,42 +7,61 @@ class CustomWizard::Notice "tests-passed" => "plugins.discourse.pavilion.tech", "stable" => "stable.plugins.discourse.pavilion.tech" } - SUBSCRIPTION_MESSAGES_DOMAIN = "test.thepavilion.io" + SUBSCRIPTION_MESSAGE_DOMAIN = "test.thepavilion.io" LOCALHOST_DOMAIN = "localhost:3000" PLUGIN_STATUSES_TO_WARN = %w(incompatible tests_failing) CHECK_PLUGIN_STATUS_ON_BRANCH = %w(tests-passed main stable) + PAGE_LIMIT = 30 attr_reader :id, + :title, :message, :type, + :archetype, :created_at attr_accessor :retrieved_at, :updated_at, :dismissed_at, - :expired_at + :expired_at, + :hidden_at def initialize(attrs) - @id = Digest::SHA1.hexdigest(attrs[:message]) + @id = self.class.generate_notice_id(attrs[:title], attrs[:created_at]) + @title = attrs[:title] @message = attrs[:message] @type = attrs[:type].to_i + @archetype = attrs[:archetype].to_i @created_at = attrs[:created_at] @updated_at = attrs[:updated_at] @retrieved_at = attrs[:retrieved_at] @dismissed_at = attrs[:dismissed_at] @expired_at = attrs[:expired_at] + @hidden_at = attrs[:hidden_at] end - def dismiss + def dismiss! if dismissable? self.dismissed_at = Time.now self.save + self.class.publish_notice_count end end - def expire - self.expired_at = Time.now - self.save + def hide! + if can_hide? + self.hidden_at = Time.now + self.save + self.class.publish_notice_count + end + end + + def expire! + if !expired? + self.expired_at = Time.now + self.save + self.class.publish_notice_count + end end def expired? @@ -54,15 +73,33 @@ class CustomWizard::Notice end def dismissable? - true + !expired? && !dismissed? && type === self.class.types[:info] + end + + def hidden? + hidden_at.present? + end + + def can_hide? + !hidden? && ( + type === self.class.types[:connection_error] || + type === self.class.types[:warning] + ) && ( + archetype === self.class.archetypes[:plugin_status] + ) end def save attrs = { expired_at: expired_at, + updated_at: updated_at, + retrieved_at: retrieved_at, created_at: created_at, + hidden_at: hidden_at, + title: title, message: message, - type: type + type: type, + archetype: archetype } if current = self.class.find(self.id) @@ -75,9 +112,15 @@ class CustomWizard::Notice def self.types @types ||= Enum.new( info: 0, - plugin_status_warning: 1, - plugin_status_connection_error: 2, - subscription_messages_connection_error: 3 + warning: 1, + connection_error: 2 + ) + end + + def self.archetypes + @archetypes ||= Enum.new( + subscription_message: 0, + plugin_status: 1 ) end @@ -85,7 +128,7 @@ class CustomWizard::Notice notices = [] if !skip_subscription - subscription_messages = request(:subscription_messages) + subscription_messages = request(:subscription_message) if subscription_messages.present? subscription_notices = convert_subscription_messages_to_notices(subscription_messages[:messages]) @@ -96,27 +139,46 @@ class CustomWizard::Notice if !skip_plugin && request_plugin_status? plugin_status = request(:plugin_status) - if plugin_status.present? && plugin_status[:status].present? && plugin_status[:status].is_a?(Hash) - plugin_notice = convert_plugin_status_to_notice(plugin_status[:status]) + if plugin_status.present? && plugin_status[:status].present? + plugin_notice = convert_plugin_status_to_notice(plugin_status) notices.push(plugin_notice) if plugin_notice end end - notices.each do |notice_data| - notice = new(notice_data) - notice.retrieved_at = Time.now - notice.save + if notices.any? + + notices.each do |notice_data| + notice = new(notice_data) + notice.retrieved_at = Time.now + notice.save + end + + publish_notice_count end end + def self.publish_notice_count + payload = { + active_notice_count: CustomWizard::Notice.active_count + } + MessageBus.publish("/custom-wizard/notices", payload, group_ids: [Group::AUTO_GROUPS[:admins]]) + end + def self.convert_subscription_messages_to_notices(messages) - messages.map do |message| - { - message: message[:message], - type: types[message[:type].to_sym], - created_at: message[:created_at], - expired_at: message[:expired_at] - } + messages.reduce([]) do |result, message| + notice_id = generate_notice_id(message[:title], message[:created_at]) + + unless exists?(notice_id) + result.push( + title: message[:title], + message: message[:message], + type: types[message[:type].to_sym], + archetype: archetypes[:subscription_message], + created_at: message[:created_at], + expired_at: message[:expired_at] + ) + end + result end end @@ -124,22 +186,32 @@ class CustomWizard::Notice notice = nil if PLUGIN_STATUSES_TO_WARN.include?(plugin_status[:status]) - notice = { - message: I18n.t('wizard.notice.compatibility_issue', domain: plugin_status_domain), - type: types[:plugin_status_warning], - created_at: plugin_status[:status_changed_at] - } + title = I18n.t('wizard.notice.compatibility_issue.title') + created_at = plugin_status[:status_changed_at] + id = generate_notice_id(title, created_at) + + unless exists?(id) + message = I18n.t('wizard.notice.compatibility_issue.message', domain: plugin_status_domain) + notice = { + id: id, + title: title, + message: message, + type: types[:warning], + archetype: archetypes[:plugin_status], + created_at: created_at + } + end else - expire_notices(types[:plugin_status_warning]) + expire_all(types[:warning], archetypes[:plugin_status]) end notice end - def self.notify_connection_errors(connection_type_key) - domain = self.send("#{connection_type_key.to_s}_domain") - message = I18n.t("wizard.notice.#{connection_type_key.to_s}.connection_error_limit", domain: domain) - notices = list(type: types[:connection_error], message: message) + def self.notify_connection_errors(archetype) + domain = self.send("#{archetype.to_s}_domain") + title = I18n.t("wizard.notice.#{archetype.to_s}.connection_error.title") + notices = list(type: types[:connection_error], archetype: archetypes[archetype.to_sym], title: title) if notices.any? notice = notices.first @@ -147,28 +219,29 @@ class CustomWizard::Notice notice.save else notice = new( - message: message, - type: types["#{connection_type_key}_connection_error".to_sym], - created_at: Time.now + title: title, + message: I18n.t("wizard.notice.#{archetype.to_s}.connection_error.message", domain: domain), + archetype: archetypes[archetype.to_sym], + type: types[:connection_error], + created_at: Time.now, + updated_at: Time.now ) notice.save end - end - def self.expire_notices(type) - list(type: type).each(&:expire) + publish_notice_count end def self.request_plugin_status? CHECK_PLUGIN_STATUS_ON_BRANCH.include?(Discourse.git_branch) || Rails.env.test? || Rails.env.development? end - def self.subscription_messages_domain - (Rails.env.test? || Rails.env.development?) ? LOCALHOST_DOMAIN : SUBSCRIPTION_MESSAGES_DOMAIN + def self.subscription_message_domain + (Rails.env.test? || Rails.env.development?) ? LOCALHOST_DOMAIN : SUBSCRIPTION_MESSAGE_DOMAIN end - def self.subscription_messages_url - "http://#{subscription_messages_domain}/subscription-server/messages.json" + def self.subscription_message_url + "http://#{subscription_message_domain}/subscription-server/messages.json" end def self.plugin_status_domain @@ -180,14 +253,19 @@ class CustomWizard::Notice "http://#{plugin_status_domain}/plugin-manager/status/discourse-custom-wizard" end - def self.request(type) - url = self.send("#{type.to_s}_url") - response = Excon.get(url) - connection_error = CustomWizard::Notice::ConnectionError.new(type) + def self.request(archetype) + url = self.send("#{archetype.to_s}_url") - if response.status == 200 + begin + response = Excon.get(url) + rescue Excon::Error::Socket, Excon::Error::Timeout => e + response = nil + end + connection_error = CustomWizard::Notice::ConnectionError.new(archetype) + + if response && response.status == 200 connection_error.expire! - expire_notices(types["#{type}_connection_error".to_sym]) + expire_all(types[:connection_error], archetypes[archetype.to_sym]) begin data = JSON.parse(response.body).deep_symbolize_keys @@ -198,8 +276,7 @@ class CustomWizard::Notice data else connection_error.create! - notify_connection_errors(type) if connection_error.reached_limit? - + notify_connection_errors(archetype) if connection_error.reached_limit? nil end end @@ -213,20 +290,65 @@ class CustomWizard::Notice new(raw.symbolize_keys) if raw.present? end + def self.exists?(id) + PluginStoreRow.where(plugin_name: namespace, key: id).exists? + end + def self.store(id, raw_notice) PluginStore.set(namespace, id, raw_notice) end - def self.list_query(type: nil, message: nil, include_recently_expired: false) + def self.list_query(type: nil, archetype: nil, title: nil, include_all: false, include_recently_expired: false, page: nil, visible: false) query = PluginStoreRow.where(plugin_name: namespace) - query = query.where("(value::json->>'expired_at') IS NULL#{include_recently_expired ? " OR (value::json->>'expired_at')::date > now()::date - 1" : ""}") - query = query.where("(value::json->>'type')::integer = ?", type) if type - query = query.where("(value::json->>'message')::text = ?", message) if message - query.order("value::json->>'created_at' DESC") + query = query.where("(value::json->>'hidden_at') IS NULL") if visible + query = query.where("(value::json->>'dismissed_at') IS NULL") unless include_all + query = query.where("(value::json->>'expired_at') IS NULL#{include_recently_expired ? " OR (value::json->>'expired_at')::date > now()::date - 1" : ""}") unless include_all + query = query.where("(value::json->>'archetype')::integer = ?", archetype) if archetype + if type + type_query_str = type.is_a?(Array) ? "(value::json->>'type')::integer IN (?)" : "(value::json->>'type')::integer = ?" + query = query.where(type_query_str, type) + end + query = query.where("(value::json->>'title')::text = ?", title) if title + query = query.limit(PAGE_LIMIT).offset(page.to_i * PAGE_LIMIT) if !page.nil? + query.order("value::json->>'expired_at' DESC, value::json->>'updated_at' DESC,value::json->>'dismissed_at' DESC, value::json->>'created_at' DESC") end - def self.list(type: nil, message: nil, include_recently_expired: false) - list_query(type: type, message: message, include_recently_expired: include_recently_expired) + def self.list(type: nil, archetype: nil, title: nil, include_all: false, include_recently_expired: false, page: 0, visible: false) + list_query(type: type, archetype: archetype, title: title, include_all: include_all, include_recently_expired: include_recently_expired, page: page, visible: visible) .map { |r| self.new(JSON.parse(r.value).symbolize_keys) } end + + def self.active_count + list_query.count + end + + def self.dismiss_all + dismissed_count = PluginStoreRow.where(" + plugin_name = '#{namespace}' AND + (value::json->>'type')::integer = #{types[:info]} AND + (value::json->>'expired_at') IS NULL AND + (value::json->>'dismissed_at') IS NULL + ").update_all(" + value = jsonb_set(value::jsonb, '{dismissed_at}', (to_json(now())::text)::jsonb, true) + ") + publish_notice_count if dismissed_count.to_i > 0 + dismissed_count + end + + def self.expire_all(type, archetype) + expired_count = PluginStoreRow.where(" + plugin_name = '#{namespace}' AND + (value::json->>'type')::integer = #{type} AND + (value::json->>'archetype')::integer = #{archetype} AND + (value::json->>'expired_at') IS NULL + ").update_all(" + value = jsonb_set(value::jsonb, '{expired_at}', (to_json(now())::text)::jsonb, true) + ") + publish_notice_count if expired_count.to_i > 0 + expired_count + end + + def self.generate_notice_id(title, created_at) + Digest::SHA1.hexdigest("#{title}-#{created_at}") + end end diff --git a/lib/custom_wizard/notice/connection_error.rb b/lib/custom_wizard/notice/connection_error.rb index 8b8b944a..9f78a09e 100644 --- a/lib/custom_wizard/notice/connection_error.rb +++ b/lib/custom_wizard/notice/connection_error.rb @@ -2,80 +2,73 @@ class CustomWizard::Notice::ConnectionError - attr_reader :type_key + attr_reader :archetype - def initialize(type_key) - @type_key = type_key + def initialize(archetype) + @archetype = archetype end def create! - id = "#{type_key.to_s}_error" - - if attrs = PluginStore.get(namespace, id) + if attrs = current_error + key = "#{archetype.to_s}_error_#{attrs["id"]}" attrs['updated_at'] = Time.now attrs['count'] = attrs['count'].to_i + 1 else - domain = CustomWizard::Notice.send("#{type_key.to_s}_domain") + domain = CustomWizard::Notice.send("#{archetype.to_s}_domain") + id = SecureRandom.hex(8) attrs = { + id: id, message: I18n.t("wizard.notice.connection_error", domain: domain), - type: self.class.types[type_key], + archetype: CustomWizard::Notice.archetypes[archetype.to_sym], created_at: Time.now, count: 1 } + key = "#{archetype.to_s}_error_#{id}" end - PluginStore.set(namespace, id, attrs) + PluginStore.set(namespace, key, attrs) @errors = nil end def expire! - if errors.exists? - errors.each do |error_row| - error = JSON.parse(error_row.value) - error['expired_at'] = Time.now - error_row.value = error.to_json - error_row.save - end + if query = current_error(query_only: true) + record = query.first + error = JSON.parse(record.value) + error['expired_at'] = Time.now + record.value = error.to_json + record.save end end - def self.types - @types ||= Enum.new( - plugin_status: 0, - subscription_messages: 1 - ) - end - def plugin_status_limit 5 end - def subscription_messages_limit + def subscription_message_limit 10 end def limit - self.send("#{type_key.to_s}_limit") + self.send("#{archetype.to_s}_limit") end def reached_limit? - return false unless errors.exists? + return false unless current_error.present? current_error['count'].to_i >= limit end - def current_error - JSON.parse(errors.first.value) - end - def namespace "#{CustomWizard::PLUGIN_NAME}_notice_connection" end - def errors - @errors ||= begin + def current_error(query_only: false) + @current_error ||= begin query = PluginStoreRow.where(plugin_name: namespace) - query = query.where("(value::json->>'type')::integer = ?", self.class.types[type_key]) - query.where("(value::json->>'expired_at') IS NULL") + query = query.where("(value::json->>'archetype')::integer = ?", CustomWizard::Notice.archetypes[archetype]) + query = query.where("(value::json->>'expired_at') IS NULL") + return nil if !query.exists? + return query if query_only + JSON.parse(query.first.value) end end end diff --git a/plugin.rb b/plugin.rb index 6845bf3a..bec30637 100644 --- a/plugin.rb +++ b/plugin.rb @@ -245,7 +245,10 @@ after_initialize do end AdminDashboardData.add_problem_check do - warning_notices = CustomWizard::Notice.list(type: CustomWizard::Notice.types[:plugin_status_warning]) + warning_notices = CustomWizard::Notice.list( + type: CustomWizard::Notice.types[:warning], + archetype: CustomWizard::Notice.archetypes[:plugin_status] + ) warning_notices.any? ? ActionView::Base.full_sanitizer.sanitize(warning_notices.first.message, tags: %w(a)) : nil end diff --git a/serializers/custom_wizard/notice_serializer.rb b/serializers/custom_wizard/notice_serializer.rb index 5564de1f..4354731d 100644 --- a/serializers/custom_wizard/notice_serializer.rb +++ b/serializers/custom_wizard/notice_serializer.rb @@ -2,19 +2,27 @@ class CustomWizard::NoticeSerializer < ApplicationSerializer attributes :id, + :title, :message, :type, + :archetype, :created_at, :expired_at, :updated_at, :dismissed_at, :retrieved_at, - :dismissable + :hidden_at, + :dismissable, + :can_hide def dismissable object.dismissable? end + def can_hide + object.can_hide? + end + def type CustomWizard::Notice.types.key(object.type) end From 98061c14e8ef84e974f1e8162d1ecbc2038e56bf Mon Sep 17 00:00:00 2001 From: angusmcleod Date: Tue, 2 Nov 2021 15:29:31 +0800 Subject: [PATCH 095/160] Fix spec (mostly) --- .../initializers/custom-wizard-edits.js.es6 | 1 + lib/custom_wizard/notice.rb | 49 ++++++------ lib/custom_wizard/notice/connection_error.rb | 17 ++-- spec/components/custom_wizard/notice_spec.rb | 80 +++++++++++++------ spec/jobs/update_notices_spec.rb | 2 +- .../admin/notice_controller_spec.rb | 57 +++++++++++-- 6 files changed, 141 insertions(+), 65 deletions(-) diff --git a/assets/javascripts/discourse/initializers/custom-wizard-edits.js.es6 b/assets/javascripts/discourse/initializers/custom-wizard-edits.js.es6 index 8208cd20..774acca3 100644 --- a/assets/javascripts/discourse/initializers/custom-wizard-edits.js.es6 +++ b/assets/javascripts/discourse/initializers/custom-wizard-edits.js.es6 @@ -41,6 +41,7 @@ export default { subscribe() { this.unsubscribe(); this.messageBus.subscribe("/custom-wizard/notices", (data) => { + if (isPresent(data.active_notice_count)) { this.loadCriticalNotices(); } diff --git a/lib/custom_wizard/notice.rb b/lib/custom_wizard/notice.rb index 7266178d..a07e1443 100644 --- a/lib/custom_wizard/notice.rb +++ b/lib/custom_wizard/notice.rb @@ -43,24 +43,30 @@ class CustomWizard::Notice def dismiss! if dismissable? self.dismissed_at = Time.now - self.save - self.class.publish_notice_count + self.save_and_publish end end def hide! if can_hide? self.hidden_at = Time.now - self.save - self.class.publish_notice_count + self.save_and_publish end end def expire! if !expired? self.expired_at = Time.now - self.save + self.save_and_publish + end + end + + def save_and_publish + if self.save self.class.publish_notice_count + true + else + false end end @@ -95,7 +101,6 @@ class CustomWizard::Notice updated_at: updated_at, retrieved_at: retrieved_at, created_at: created_at, - hidden_at: hidden_at, title: title, message: message, type: type, @@ -104,6 +109,7 @@ class CustomWizard::Notice if current = self.class.find(self.id) attrs[:dismissed_at] = current.dismissed_at || self.dismissed_at + attrs[:hidden_at] = current.hidden_at || self.hidden_at end self.class.store(id, attrs) @@ -146,7 +152,6 @@ class CustomWizard::Notice end if notices.any? - notices.each do |notice_data| notice = new(notice_data) notice.retrieved_at = Time.now @@ -166,18 +171,16 @@ class CustomWizard::Notice def self.convert_subscription_messages_to_notices(messages) messages.reduce([]) do |result, message| - notice_id = generate_notice_id(message[:title], message[:created_at]) - - unless exists?(notice_id) - result.push( - title: message[:title], - message: message[:message], - type: types[message[:type].to_sym], - archetype: archetypes[:subscription_message], - created_at: message[:created_at], - expired_at: message[:expired_at] - ) - end + id = generate_notice_id(message[:title], message[:created_at]) + result.push( + id: id, + title: message[:title], + message: message[:message], + type: types[message[:type].to_sym], + archetype: archetypes[:subscription_message], + created_at: message[:created_at], + expired_at: message[:expired_at] + ) result end end @@ -298,11 +301,11 @@ class CustomWizard::Notice PluginStore.set(namespace, id, raw_notice) end - def self.list_query(type: nil, archetype: nil, title: nil, include_all: false, include_recently_expired: false, page: nil, visible: false) + def self.list_query(type: nil, archetype: nil, title: nil, include_all: false, page: nil, visible: false) query = PluginStoreRow.where(plugin_name: namespace) query = query.where("(value::json->>'hidden_at') IS NULL") if visible query = query.where("(value::json->>'dismissed_at') IS NULL") unless include_all - query = query.where("(value::json->>'expired_at') IS NULL#{include_recently_expired ? " OR (value::json->>'expired_at')::date > now()::date - 1" : ""}") unless include_all + query = query.where("(value::json->>'expired_at') IS NULL") unless include_all query = query.where("(value::json->>'archetype')::integer = ?", archetype) if archetype if type type_query_str = type.is_a?(Array) ? "(value::json->>'type')::integer IN (?)" : "(value::json->>'type')::integer = ?" @@ -313,8 +316,8 @@ class CustomWizard::Notice query.order("value::json->>'expired_at' DESC, value::json->>'updated_at' DESC,value::json->>'dismissed_at' DESC, value::json->>'created_at' DESC") end - def self.list(type: nil, archetype: nil, title: nil, include_all: false, include_recently_expired: false, page: 0, visible: false) - list_query(type: type, archetype: archetype, title: title, include_all: include_all, include_recently_expired: include_recently_expired, page: page, visible: visible) + def self.list(type: nil, archetype: nil, title: nil, include_all: false, page: 0, visible: false) + list_query(type: type, archetype: archetype, title: title, include_all: include_all, page: page, visible: visible) .map { |r| self.new(JSON.parse(r.value).symbolize_keys) } end diff --git a/lib/custom_wizard/notice/connection_error.rb b/lib/custom_wizard/notice/connection_error.rb index 9f78a09e..a1d834c6 100644 --- a/lib/custom_wizard/notice/connection_error.rb +++ b/lib/custom_wizard/notice/connection_error.rb @@ -10,9 +10,9 @@ class CustomWizard::Notice::ConnectionError def create! if attrs = current_error - key = "#{archetype.to_s}_error_#{attrs["id"]}" - attrs['updated_at'] = Time.now - attrs['count'] = attrs['count'].to_i + 1 + key = "#{archetype.to_s}_error_#{attrs[:id]}" + attrs[:updated_at] = Time.now + attrs[:count] = attrs[:count].to_i + 1 else domain = CustomWizard::Notice.send("#{archetype.to_s}_domain") id = SecureRandom.hex(8) @@ -27,7 +27,8 @@ class CustomWizard::Notice::ConnectionError end PluginStore.set(namespace, key, attrs) - @errors = nil + + @current_error = nil end def expire! @@ -54,7 +55,7 @@ class CustomWizard::Notice::ConnectionError def reached_limit? return false unless current_error.present? - current_error['count'].to_i >= limit + current_error[:count].to_i >= limit end def namespace @@ -64,11 +65,13 @@ class CustomWizard::Notice::ConnectionError def current_error(query_only: false) @current_error ||= begin query = PluginStoreRow.where(plugin_name: namespace) - query = query.where("(value::json->>'archetype')::integer = ?", CustomWizard::Notice.archetypes[archetype]) + query = query.where("(value::json->>'archetype')::integer = ?", CustomWizard::Notice.archetypes[archetype.to_sym]) query = query.where("(value::json->>'expired_at') IS NULL") + return nil if !query.exists? return query if query_only - JSON.parse(query.first.value) + + JSON.parse(query.first.value).deep_symbolize_keys end end end diff --git a/spec/components/custom_wizard/notice_spec.rb b/spec/components/custom_wizard/notice_spec.rb index b51305e1..0b34664d 100644 --- a/spec/components/custom_wizard/notice_spec.rb +++ b/spec/components/custom_wizard/notice_spec.rb @@ -6,6 +6,7 @@ describe CustomWizard::Notice do fab!(:user) { Fabricate(:user) } let(:subscription_message) { { + title: "Title of message about subscription", message: "Message about subscription", type: "info", created_at: Time.now - 3.day, @@ -23,7 +24,7 @@ describe CustomWizard::Notice do context "subscription message" do before do freeze_time - stub_request(:get, described_class.subscription_messages_url).to_return(status: 200, body: { messages: [subscription_message] }.to_json) + stub_request(:get, described_class.subscription_message_url).to_return(status: 200, body: { messages: [subscription_message] }.to_json) described_class.update(skip_plugin: true) end @@ -36,46 +37,73 @@ describe CustomWizard::Notice do it "expires notice if subscription message is expired" do subscription_message[:expired_at] = Time.now - stub_request(:get, described_class.subscription_messages_url).to_return(status: 200, body: { messages: [subscription_message] }.to_json) + stub_request(:get, described_class.subscription_message_url).to_return(status: 200, body: { messages: [subscription_message] }.to_json) described_class.update(skip_plugin: true) - notice = described_class.list(include_recently_expired: true).first + notice = described_class.list(include_all: true).first expect(notice.expired?).to eq(true) end + + it "dismisses informational subscription notices" do + notice = described_class.list(include_all: true).first + expect(notice.dismissed?).to eq(false) + + notice.dismiss! + expect(notice.dismissed?).to eq(true) + end + + it "dismisses all informational subscription notices" do + 4.times do |index| + subscription_message[:title] += " #{index}" + stub_request(:get, described_class.subscription_message_url).to_return(status: 200, body: { messages: [subscription_message] }.to_json) + described_class.update(skip_plugin: true) + end + expect(described_class.list.count).to eq(5) + described_class.dismiss_all + expect(described_class.list.count).to eq(0) + end end context "plugin status" do before do freeze_time - stub_request(:get, described_class.plugin_status_url).to_return(status: 200, body: { status: plugin_status }.to_json) + stub_request(:get, described_class.plugin_status_url).to_return(status: 200, body: plugin_status.to_json) described_class.update(skip_subscription: true) end it "converts warning into notice" do notice = described_class.list.first - expect(notice.type).to eq(described_class.types[:plugin_status_warning]) - expect(notice.message).to eq(I18n.t("wizard.notice.compatibility_issue", domain: described_class.plugin_status_domain)) + expect(notice.type).to eq(described_class.types[:warning]) + expect(notice.message).to eq(I18n.t("wizard.notice.compatibility_issue.message", domain: described_class.plugin_status_domain)) expect(notice.created_at.to_datetime).to be_within(1.second).of (plugin_status[:status_changed_at].to_datetime) end it "expires warning notices if status is recommended or compatible" do plugin_status[:status] = 'compatible' plugin_status[:status_changed_at] = Time.now - stub_request(:get, described_class.plugin_status_url).to_return(status: 200, body: { status: plugin_status }.to_json) + stub_request(:get, described_class.plugin_status_url).to_return(status: 200, body: plugin_status.to_json) described_class.update(skip_subscription: true) - notice = described_class.list(type: described_class.types[:plugin_status_warning], include_recently_expired: true).first + notice = described_class.list(type: described_class.types[:warning], include_all: true).first expect(notice.expired?).to eq(true) end + + it "hides plugin status warnings" do + notice = described_class.list.first + expect(notice.hidden?).to eq(false) + + notice.hide! + expect(notice.hidden?).to eq(true) + end end it "lists notices not expired more than a day ago" do subscription_message[:expired_at] = Time.now - 8.hours - stub_request(:get, described_class.subscription_messages_url).to_return(status: 200, body: { messages: [subscription_message] }.to_json) - stub_request(:get, described_class.plugin_status_url).to_return(status: 200, body: { status: plugin_status }.to_json) + stub_request(:get, described_class.subscription_message_url).to_return(status: 200, body: { messages: [subscription_message] }.to_json) + stub_request(:get, described_class.plugin_status_url).to_return(status: 200, body: plugin_status.to_json) described_class.update - expect(described_class.list(include_recently_expired: true).length).to eq(2) + expect(described_class.list(include_all: true).length).to eq(2) end context "connection errors" do @@ -84,47 +112,47 @@ describe CustomWizard::Notice do end it "creates an error if connection to notice server fails" do - stub_request(:get, described_class.plugin_status_url).to_return(status: 400, body: { status: plugin_status }.to_json) + stub_request(:get, described_class.plugin_status_url).to_return(status: 400, body: plugin_status.to_json) described_class.update(skip_subscription: true) error = CustomWizard::Notice::ConnectionError.new(:plugin_status) - expect(error.errors.exists?).to eq(true) + expect(error.current_error.present?).to eq(true) end it "only creates one connection error per type at a time" do - stub_request(:get, described_class.subscription_messages_url).to_return(status: 400, body: { messages: [subscription_message] }.to_json) - stub_request(:get, described_class.plugin_status_url).to_return(status: 400, body: { status: plugin_status }.to_json) + stub_request(:get, described_class.subscription_message_url).to_return(status: 400, body: { messages: [subscription_message] }.to_json) + stub_request(:get, described_class.plugin_status_url).to_return(status: 400, body: plugin_status.to_json) 5.times { described_class.update } plugin_status_errors = CustomWizard::Notice::ConnectionError.new(:plugin_status) - subscription_message_errors = CustomWizard::Notice::ConnectionError.new(:subscription_messages) + subscription_message_errors = CustomWizard::Notice::ConnectionError.new(:subscription_message) - expect(plugin_status_errors.errors.length).to eq(1) - expect(subscription_message_errors.errors.length).to eq(1) + expect(plugin_status_errors.current_error[:count]).to eq(5) + expect(subscription_message_errors.current_error[:count]).to eq(5) end it "creates a connection error notice if connection errors reach limit" do - stub_request(:get, described_class.plugin_status_url).to_return(status: 400, body: { status: plugin_status }.to_json) + stub_request(:get, described_class.plugin_status_url).to_return(status: 400, body: plugin_status.to_json) error = CustomWizard::Notice::ConnectionError.new(:plugin_status) error.limit.times { described_class.update(skip_subscription: true) } - notice = described_class.list(type: described_class.types[:plugin_status_connection_error]).first + notice = described_class.list(type: described_class.types[:connection_error]).first - expect(error.current_error['count']).to eq(error.limit) - expect(notice.type).to eq(described_class.types[:plugin_status_connection_error]) + expect(error.current_error[:count]).to eq(error.limit) + expect(notice.type).to eq(described_class.types[:connection_error]) end it "expires a connection error notice if connection succeeds" do - stub_request(:get, described_class.plugin_status_url).to_return(status: 400, body: { status: plugin_status }.to_json) + stub_request(:get, described_class.plugin_status_url).to_return(status: 400, body: plugin_status.to_json) error = CustomWizard::Notice::ConnectionError.new(:plugin_status) error.limit.times { described_class.update(skip_subscription: true) } - stub_request(:get, described_class.plugin_status_url).to_return(status: 200, body: { status: plugin_status }.to_json) + stub_request(:get, described_class.plugin_status_url).to_return(status: 200, body: plugin_status.to_json) described_class.update(skip_subscription: true) - notice = described_class.list(type: described_class.types[:plugin_status_connection_error], include_recently_expired: true).first + notice = described_class.list(type: described_class.types[:connection_error], include_all: true).first - expect(notice.type).to eq(described_class.types[:plugin_status_connection_error]) + expect(notice.type).to eq(described_class.types[:connection_error]) expect(notice.expired_at.present?).to eq(true) end end diff --git a/spec/jobs/update_notices_spec.rb b/spec/jobs/update_notices_spec.rb index d0e5a468..8ba5587f 100644 --- a/spec/jobs/update_notices_spec.rb +++ b/spec/jobs/update_notices_spec.rb @@ -20,7 +20,7 @@ describe Jobs::CustomWizardUpdateNotices do } it "updates the notices" do - stub_request(:get, CustomWizard::Notice.subscription_messages_url).to_return(status: 200, body: { messages: [subscription_message] }.to_json) + stub_request(:get, CustomWizard::Notice.subscription_message_url).to_return(status: 200, body: { messages: [subscription_message] }.to_json) stub_request(:get, CustomWizard::Notice.plugin_status_url).to_return(status: 200, body: { status: plugin_status }.to_json) described_class.new.execute diff --git a/spec/requests/custom_wizard/admin/notice_controller_spec.rb b/spec/requests/custom_wizard/admin/notice_controller_spec.rb index bd174e90..33d03432 100644 --- a/spec/requests/custom_wizard/admin/notice_controller_spec.rb +++ b/spec/requests/custom_wizard/admin/notice_controller_spec.rb @@ -3,29 +3,70 @@ require_relative '../../../plugin_helper' describe CustomWizard::AdminNoticeController do fab!(:admin_user) { Fabricate(:user, admin: true) } + let(:subscription_message_notice) { + { + title: "Title of message about subscription", + message: "Message about subscription", + type: 0, + created_at: Time.now.iso8601(3), + expired_at: nil + } + } + let(:plugin_status_notice) { + { + title: "The Custom Wizard Plugin is incompatibile with the latest version of Discourse.", + message: "Please check the Custom Wizard Plugin status on [localhost:3000](http://localhost:3000) before updating Discourse.", + type: 1, + archetype: 1, + created_at: Time.now.iso8601(3), + expired_at: nil + } + } before do sign_in(admin_user) - @notice = CustomWizard::Notice.new( - message: "Message about subscription", - type: "info", - created_at: Time.now - 3.day, - expired_at: nil - ) - @notice.save end it "lists notices" do + @notice = CustomWizard::Notice.new(subscription_message_notice) + @notice.save + get "/admin/wizards/notice.json" expect(response.status).to eq(200) expect(response.parsed_body.length).to eq(1) end it "dismisses notices" do - put "/admin/wizards/notice/#{@notice.id}.json" + @notice = CustomWizard::Notice.new(subscription_message_notice) + @notice.save + + put "/admin/wizards/notice/#{@notice.id}/dismiss.json" expect(response.status).to eq(200) updated = CustomWizard::Notice.find(@notice.id) expect(updated.dismissed?).to eq(true) end + + it "dismisses all notices" do + 5.times do |index| + subscription_message_notice[:title] += " #{index}" + @notice = CustomWizard::Notice.new(subscription_message_notice) + @notice.save + end + + put "/admin/wizards/notice/dismiss.json" + expect(response.status).to eq(200) + expect(CustomWizard::Notice.list.size).to eq(0) + end + + it "hides notices" do + @notice = CustomWizard::Notice.new(plugin_status_notice) + @notice.save + + put "/admin/wizards/notice/#{@notice.id}/hide.json" + expect(response.status).to eq(200) + + updated = CustomWizard::Notice.find(@notice.id) + expect(updated.hidden?).to eq(true) + end end From 853634be27963acf154c9653fb9a19f22884af7c Mon Sep 17 00:00:00 2001 From: angusmcleod Date: Tue, 2 Nov 2021 15:39:38 +0800 Subject: [PATCH 096/160] Fix failing specs --- spec/components/custom_wizard/wizard_spec.rb | 2 +- spec/jobs/update_notices_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/components/custom_wizard/wizard_spec.rb b/spec/components/custom_wizard/wizard_spec.rb index 9cccff97..8ee19c33 100644 --- a/spec/components/custom_wizard/wizard_spec.rb +++ b/spec/components/custom_wizard/wizard_spec.rb @@ -186,7 +186,7 @@ describe CustomWizard::Wizard do it "lists the site categories" do Site.clear_cache - expect(@wizard.categories.length).to eq(1) + expect(@wizard.categories.length > 0).to eq(true) end context "submissions" do diff --git a/spec/jobs/update_notices_spec.rb b/spec/jobs/update_notices_spec.rb index 8ba5587f..df0697b8 100644 --- a/spec/jobs/update_notices_spec.rb +++ b/spec/jobs/update_notices_spec.rb @@ -21,7 +21,7 @@ describe Jobs::CustomWizardUpdateNotices do it "updates the notices" do stub_request(:get, CustomWizard::Notice.subscription_message_url).to_return(status: 200, body: { messages: [subscription_message] }.to_json) - stub_request(:get, CustomWizard::Notice.plugin_status_url).to_return(status: 200, body: { status: plugin_status }.to_json) + stub_request(:get, CustomWizard::Notice.plugin_status_url).to_return(status: 200, body: plugin_status.to_json) described_class.new.execute expect(CustomWizard::Notice.list.length).to eq(2) From 49538d554df1597141a78005eadde3b13bb7c46c Mon Sep 17 00:00:00 2001 From: angusmcleod Date: Wed, 17 Nov 2021 20:48:11 +0800 Subject: [PATCH 097/160] Linting --- .../components/wizard-notice-row.js.es6 | 13 ++-- .../discourse/components/wizard-notice.js.es6 | 14 +++-- .../custom-wizard-critical-notice.js.es6 | 12 ++-- .../controllers/admin-wizards-notices.js.es6 | 17 +++--- .../controllers/admin-wizards.js.es6 | 4 +- .../discourse/helpers/notice-badge.js.es6 | 18 +++--- .../initializers/custom-wizard-edits.js.es6 | 26 ++++---- .../discourse/mixins/notice-message.js.es6 | 55 +++++++++-------- .../models/custom-wizard-notice.js.es6 | 60 ++++++++++--------- .../routes/admin-wizards-notices.js.es6 | 4 +- .../discourse/routes/admin-wizards.js.es6 | 2 +- .../templates/admin-wizards-notices.hbs | 2 +- .../components/wizard-notice-row.hbs | 2 +- assets/stylesheets/admin/admin.scss | 12 ++-- controllers/custom_wizard/admin/notice.rb | 4 +- lib/custom_wizard/notice.rb | 2 +- 16 files changed, 135 insertions(+), 112 deletions(-) diff --git a/assets/javascripts/discourse/components/wizard-notice-row.js.es6 b/assets/javascripts/discourse/components/wizard-notice-row.js.es6 index 9c099b39..ada4384d 100644 --- a/assets/javascripts/discourse/components/wizard-notice-row.js.es6 +++ b/assets/javascripts/discourse/components/wizard-notice-row.js.es6 @@ -4,11 +4,16 @@ import NoticeMessage from "../mixins/notice-message"; export default Component.extend(NoticeMessage, { tagName: "tr", attributeBindings: ["notice.id:data-notice-id"], - classNameBindings: [":wizard-notice-row", "notice.typeClass", "notice.expired:expired", "notice.dismissed:dismissed"], + classNameBindings: [ + ":wizard-notice-row", + "notice.typeClass", + "notice.expired:expired", + "notice.dismissed:dismissed", + ], actions: { dismiss() { this.notice.dismiss(); - } - } -}); \ No newline at end of file + }, + }, +}); diff --git a/assets/javascripts/discourse/components/wizard-notice.js.es6 b/assets/javascripts/discourse/components/wizard-notice.js.es6 index cac3e4eb..ca6b7658 100644 --- a/assets/javascripts/discourse/components/wizard-notice.js.es6 +++ b/assets/javascripts/discourse/components/wizard-notice.js.es6 @@ -3,7 +3,13 @@ import NoticeMessage from "../mixins/notice-message"; export default Component.extend(NoticeMessage, { attributeBindings: ["notice.id:data-notice-id"], - classNameBindings: [':wizard-notice', 'notice.typeClass', 'notice.dismissed:dismissed', 'notice.expired:expired', 'notice.hidden:hidden'], + classNameBindings: [ + ":wizard-notice", + "notice.typeClass", + "notice.dismissed:dismissed", + "notice.expired:expired", + "notice.hidden:hidden", + ], actions: { dismiss() { @@ -14,10 +20,10 @@ export default Component.extend(NoticeMessage, { }, hide() { - this.set('hiding', true); + this.set("hiding", true); this.notice.hide().then(() => { - this.set('hiding', false); + this.set("hiding", false); }); }, - } + }, }); diff --git a/assets/javascripts/discourse/connectors/admin-dashboard-top/custom-wizard-critical-notice.js.es6 b/assets/javascripts/discourse/connectors/admin-dashboard-top/custom-wizard-critical-notice.js.es6 index 0bb252e9..803e58a4 100644 --- a/assets/javascripts/discourse/connectors/admin-dashboard-top/custom-wizard-critical-notice.js.es6 +++ b/assets/javascripts/discourse/connectors/admin-dashboard-top/custom-wizard-critical-notice.js.es6 @@ -6,14 +6,14 @@ export default { }, setupComponent(attrs, component) { - const controller = getOwner(this).lookup('controller:admin-dashboard'); + const controller = getOwner(this).lookup("controller:admin-dashboard"); - component.set('notices', controller.get('customWizardCriticalNotices')); - controller.addObserver('customWizardCriticalNotices.[]', () => { + component.set("notices", controller.get("customWizardCriticalNotices")); + controller.addObserver("customWizardCriticalNotices.[]", () => { if (this._state === "destroying") { return; } - component.set('notices', controller.get('customWizardCriticalNotices')); + component.set("notices", controller.get("customWizardCriticalNotices")); }); - } -}; \ No newline at end of file + }, +}; diff --git a/assets/javascripts/discourse/controllers/admin-wizards-notices.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards-notices.js.es6 index 0f67f878..1721e699 100644 --- a/assets/javascripts/discourse/controllers/admin-wizards-notices.js.es6 +++ b/assets/javascripts/discourse/controllers/admin-wizards-notices.js.es6 @@ -15,9 +15,9 @@ export default Controller.extend({ loadingMore: false, canLoadMore: true, - @discourseComputed('notices.[]', 'notices.@each.dismissed') + @discourseComputed("notices.[]", "notices.@each.dismissed") allDismisssed(notices) { - return notices.every(n => !n.canDismiss || n.dismissed); + return notices.every((n) => !n.canDismiss || n.dismissed); }, loadMoreNotices() { @@ -35,7 +35,7 @@ export default Controller.extend({ } this.get("notices").pushObjects( - A(result.notices.map(notice => CustomWizardNotice.create(notice))) + A(result.notices.map((notice) => CustomWizardNotice.create(notice))) ); }) .finally(() => this.set("loadingMore", false)); @@ -56,12 +56,13 @@ export default Controller.extend({ I18n.t("yes_value"), (result) => { if (result) { - this.set('loadingMore', true); - CustomWizardNotice.dismissAll() - .finally(() => this.set("loadingMore", false)); + this.set("loadingMore", true); + CustomWizardNotice.dismissAll().finally(() => + this.set("loadingMore", false) + ); } } ); - } - } + }, + }, }); diff --git a/assets/javascripts/discourse/controllers/admin-wizards.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards.js.es6 index e2672fe4..33841460 100644 --- a/assets/javascripts/discourse/controllers/admin-wizards.js.es6 +++ b/assets/javascripts/discourse/controllers/admin-wizards.js.es6 @@ -17,10 +17,10 @@ export default Controller.extend({ this.adminWizardsNotices.setProperties({ notices: A(), page: 0, - canLoadMore: true + canLoadMore: true, }); this.adminWizardsNotices.loadMoreNotices(); } }); - } + }, }); diff --git a/assets/javascripts/discourse/helpers/notice-badge.js.es6 b/assets/javascripts/discourse/helpers/notice-badge.js.es6 index bc5df4a6..ea32b462 100644 --- a/assets/javascripts/discourse/helpers/notice-badge.js.es6 +++ b/assets/javascripts/discourse/helpers/notice-badge.js.es6 @@ -4,38 +4,40 @@ import I18n from "I18n"; import { registerUnbound } from "discourse-common/lib/helpers"; import { htmlSafe } from "@ember/template"; -registerUnbound("notice-badge", function(attrs) { - let tag = attrs.url ? 'a' : 'div'; - let attrStr = ''; +registerUnbound("notice-badge", function (attrs) { + let tag = attrs.url ? "a" : "div"; + let attrStr = ""; if (attrs.title) { attrStr += `title='${I18n.t(attrs.title)}'`; } if (attrs.url) { attrStr += `href='${attrs.url}'`; } - let html = `<${tag} class="${attrs.class ? `${attrs.class} ` : ''}notice-badge" ${attrStr}>`; + let html = `<${tag} class="${ + attrs.class ? `${attrs.class} ` : "" + }notice-badge" ${attrStr}>`; if (attrs.icon) { html += iconHTML(attrs.icon); } if (attrs.label) { if (attrs.icon) { - html += ' '; + html += " "; } html += `${I18n.t(attrs.label)}`; } if (attrs.date) { if (attrs.icon || attrs.label) { - html += ' '; + html += " "; } let dateAttrs = {}; if (attrs.leaveAgo) { dateAttrs = { format: "medium", - leaveAgo: true + leaveAgo: true, }; } html += autoUpdatingRelativeAge(new Date(attrs.date), dateAttrs); } html += ``; return htmlSafe(html); -}); \ No newline at end of file +}); diff --git a/assets/javascripts/discourse/initializers/custom-wizard-edits.js.es6 b/assets/javascripts/discourse/initializers/custom-wizard-edits.js.es6 index 774acca3..e67f1ebd 100644 --- a/assets/javascripts/discourse/initializers/custom-wizard-edits.js.es6 +++ b/assets/javascripts/discourse/initializers/custom-wizard-edits.js.es6 @@ -22,16 +22,16 @@ export default { }; withPluginApi("0.8.36", (api) => { - api.modifyClass('route:admin-dashboard', { + api.modifyClass("route:admin-dashboard", { setupController(controller) { this._super(...arguments); controller.loadCriticalNotices(); controller.subscribe(); - } + }, }); - api.modifyClass('controller:admin-dashboard', { + api.modifyClass("controller:admin-dashboard", { criticalNotices: A(), unsubscribe() { @@ -41,7 +41,6 @@ export default { subscribe() { this.unsubscribe(); this.messageBus.subscribe("/custom-wizard/notices", (data) => { - if (isPresent(data.active_notice_count)) { this.loadCriticalNotices(); } @@ -50,19 +49,18 @@ export default { loadCriticalNotices() { CustomWizardNotice.list({ - type: [ - 'connection_error', - 'warning' - ], - archetype: 'plugin_status', - visible: true - }).then(result => { + type: ["connection_error", "warning"], + archetype: "plugin_status", + visible: true, + }).then((result) => { if (result.notices && result.notices.length) { - const criticalNotices = A(result.notices.map(n => CustomWizardNotice.create(n))); - this.set('customWizardCriticalNotices', criticalNotices); + const criticalNotices = A( + result.notices.map((n) => CustomWizardNotice.create(n)) + ); + this.set("customWizardCriticalNotices", criticalNotices); } }); - } + }, }); }); }, diff --git a/assets/javascripts/discourse/mixins/notice-message.js.es6 b/assets/javascripts/discourse/mixins/notice-message.js.es6 index 492df643..76e311bb 100644 --- a/assets/javascripts/discourse/mixins/notice-message.js.es6 +++ b/assets/javascripts/discourse/mixins/notice-message.js.es6 @@ -6,7 +6,7 @@ import { createPopper } from "@popperjs/core"; export default Mixin.create({ showCookedMessage: false, - didReceiveAttrs(){ + didReceiveAttrs() { const message = this.notice.message; cookAsync(message).then((cooked) => { this.set("cookedMessage", cooked); @@ -14,27 +14,24 @@ export default Mixin.create({ }, createMessageModal() { - let container = this.element.querySelector('.notice-message'); - let modal = this.element.querySelector('.cooked-notice-message'); + let container = this.element.querySelector(".notice-message"); + let modal = this.element.querySelector(".cooked-notice-message"); - this._popper = createPopper( - container, - modal, { - strategy: "absolute", - placement: "bottom-start", - modifiers: [ - { - name: "preventOverflow", + this._popper = createPopper(container, modal, { + strategy: "absolute", + placement: "bottom-start", + modifiers: [ + { + name: "preventOverflow", + }, + { + name: "offset", + options: { + offset: [0, 5], }, - { - name: "offset", - options: { - offset: [0, 5], - }, - }, - ], - } - ); + }, + ], + }); }, didInsertElement() { @@ -46,10 +43,16 @@ export default Mixin.create({ }, documentClick(event) { - if (this._state === "destroying") { return; } + if (this._state === "destroying") { + return; + } - if (!event.target.closest(`[data-notice-id="${this.notice.id}"] .notice-message`)) { - this.set('showCookedMessage', false); + if ( + !event.target.closest( + `[data-notice-id="${this.notice.id}"] .notice-message` + ) + ) { + this.set("showCookedMessage", false); } }, @@ -60,6 +63,6 @@ export default Mixin.create({ if (this.showCookedMessage) { scheduleOnce("afterRender", this, this.createMessageModal); } - } - } -}); \ No newline at end of file + }, + }, +}); diff --git a/assets/javascripts/discourse/models/custom-wizard-notice.js.es6 b/assets/javascripts/discourse/models/custom-wizard-notice.js.es6 index 29e30628..035e2ad5 100644 --- a/assets/javascripts/discourse/models/custom-wizard-notice.js.es6 +++ b/assets/javascripts/discourse/models/custom-wizard-notice.js.es6 @@ -7,62 +7,68 @@ import { dasherize } from "@ember/string"; import I18n from "I18n"; const CustomWizardNotice = EmberObject.extend({ - expired: notEmpty('expired_at'), - dismissed: notEmpty('dismissed_at'), - hidden: notEmpty('hidden_at'), - notHidden: not('hidden'), - notDismissed: not('dismissed'), - canDismiss: and('dismissable', 'notDismissed'), - canHide: and('can_hide', 'notHidden'), + expired: notEmpty("expired_at"), + dismissed: notEmpty("dismissed_at"), + hidden: notEmpty("hidden_at"), + notHidden: not("hidden"), + notDismissed: not("dismissed"), + canDismiss: and("dismissable", "notDismissed"), + canHide: and("can_hide", "notHidden"), - @discourseComputed('type') + @discourseComputed("type") typeClass(type) { return dasherize(type); }, - @discourseComputed('type') + @discourseComputed("type") typeLabel(type) { return I18n.t(`admin.wizard.notice.type.${type}`); }, dismiss() { - if (!this.get('canDismiss')) { + if (!this.get("canDismiss")) { return; } - return ajax(`/admin/wizards/notice/${this.get('id')}/dismiss`, { type: 'PUT' }).then(result => { - if (result.success) { - this.set('dismissed_at', result.dismissed_at); - } - }).catch(popupAjaxError); + return ajax(`/admin/wizards/notice/${this.get("id")}/dismiss`, { + type: "PUT", + }) + .then((result) => { + if (result.success) { + this.set("dismissed_at", result.dismissed_at); + } + }) + .catch(popupAjaxError); }, hide() { - if (!this.get('canHide')) { + if (!this.get("canHide")) { return; } - return ajax(`/admin/wizards/notice/${this.get('id')}/hide`, { type: 'PUT' }).then(result => { - if (result.success) { - this.set('hidden_at', result.hidden_at); - } - }).catch(popupAjaxError); - } + return ajax(`/admin/wizards/notice/${this.get("id")}/hide`, { type: "PUT" }) + .then((result) => { + if (result.success) { + this.set("hidden_at", result.hidden_at); + } + }) + .catch(popupAjaxError); + }, }); CustomWizardNotice.reopenClass({ list(data = {}) { - return ajax('/admin/wizards/notice', { + return ajax("/admin/wizards/notice", { type: "GET", - data + data, }).catch(popupAjaxError); }, dismissAll() { - return ajax('/admin/wizards/notice/dismiss', { - type: "PUT" + return ajax("/admin/wizards/notice/dismiss", { + type: "PUT", }).catch(popupAjaxError); - } + }, }); export default CustomWizardNotice; diff --git a/assets/javascripts/discourse/routes/admin-wizards-notices.js.es6 b/assets/javascripts/discourse/routes/admin-wizards-notices.js.es6 index a329ce95..1d8b7cc8 100644 --- a/assets/javascripts/discourse/routes/admin-wizards-notices.js.es6 +++ b/assets/javascripts/discourse/routes/admin-wizards-notices.js.es6 @@ -9,7 +9,9 @@ export default DiscourseRoute.extend({ setupController(controller, model) { controller.setProperties({ - notices: A(model.notices.map(notice => CustomWizardNotice.create(notice))), + notices: A( + model.notices.map((notice) => CustomWizardNotice.create(notice)) + ), }); }, }); diff --git a/assets/javascripts/discourse/routes/admin-wizards.js.es6 b/assets/javascripts/discourse/routes/admin-wizards.js.es6 index 2bbc73a9..5c39c0d6 100644 --- a/assets/javascripts/discourse/routes/admin-wizards.js.es6 +++ b/assets/javascripts/discourse/routes/admin-wizards.js.es6 @@ -23,5 +23,5 @@ export default DiscourseRoute.extend({ if (transition.targetName === "adminWizards.index") { this.transitionTo("adminWizardsWizard"); } - } + }, }); diff --git a/assets/javascripts/discourse/templates/admin-wizards-notices.hbs b/assets/javascripts/discourse/templates/admin-wizards-notices.hbs index d522c1a5..039afe49 100644 --- a/assets/javascripts/discourse/templates/admin-wizards-notices.hbs +++ b/assets/javascripts/discourse/templates/admin-wizards-notices.hbs @@ -46,4 +46,4 @@ {{conditional-loading-spinner condition=loadingMore}} {{/load-more}} -
\ No newline at end of file +
diff --git a/assets/javascripts/discourse/templates/components/wizard-notice-row.hbs b/assets/javascripts/discourse/templates/components/wizard-notice-row.hbs index 7f97b250..cc22a42e 100644 --- a/assets/javascripts/discourse/templates/components/wizard-notice-row.hbs +++ b/assets/javascripts/discourse/templates/components/wizard-notice-row.hbs @@ -27,4 +27,4 @@ {{else}} {{i18n "admin.wizard.notice.active"}} {{/if}} - \ No newline at end of file + diff --git a/assets/stylesheets/admin/admin.scss b/assets/stylesheets/admin/admin.scss index e3790465..36184088 100644 --- a/assets/stylesheets/admin/admin.scss +++ b/assets/stylesheets/admin/admin.scss @@ -926,7 +926,7 @@ $error: #ef1700; } .wizard-notice { - padding: .75em; + padding: 0.75em; margin-bottom: 1em; border: 1px solid var(--primary-low); @@ -941,7 +941,7 @@ $error: #ef1700; } .notice-badge { - padding: 0 .5em; + padding: 0 0.5em; } .notice-header { @@ -957,7 +957,7 @@ $error: #ef1700; align-items: center; .notice-badge { - margin-left: .5em; + margin-left: 0.5em; } } } @@ -1022,11 +1022,11 @@ $error: #ef1700; position: absolute; top: -8px; right: -8px; - font-size: .7em; + font-size: 0.7em; } a.show-notice-message { - padding: .25em .5em; + padding: 0.25em 0.5em; color: var(--primary); } @@ -1057,4 +1057,4 @@ a.show-notice-message { margin: 0; } } -} \ No newline at end of file +} diff --git a/controllers/custom_wizard/admin/notice.rb b/controllers/custom_wizard/admin/notice.rb index d6c43b5f..81ae00da 100644 --- a/controllers/custom_wizard/admin/notice.rb +++ b/controllers/custom_wizard/admin/notice.rb @@ -12,7 +12,7 @@ class CustomWizard::AdminNoticeController < CustomWizard::AdminController if type if type.is_a?(Array) - type = type.map { |type| CustomWizard::Notice.types[type.to_sym] } + type = type.map { |t| CustomWizard::Notice.types[t.to_sym] } else type = CustomWizard::Notice.types[type.to_sym] end @@ -20,7 +20,7 @@ class CustomWizard::AdminNoticeController < CustomWizard::AdminController if archetype if archetype.is_a?(Array) - archetype = archetype.map { |type| CustomWizard::Notice.archetypes[archetype.to_sym] } + archetype = archetype.map { |t| CustomWizard::Notice.archetypes[archetype.to_sym] } else archetype = CustomWizard::Notice.archetypes[archetype.to_sym] end diff --git a/lib/custom_wizard/notice.rb b/lib/custom_wizard/notice.rb index a07e1443..1fcb5041 100644 --- a/lib/custom_wizard/notice.rb +++ b/lib/custom_wizard/notice.rb @@ -308,7 +308,7 @@ class CustomWizard::Notice query = query.where("(value::json->>'expired_at') IS NULL") unless include_all query = query.where("(value::json->>'archetype')::integer = ?", archetype) if archetype if type - type_query_str = type.is_a?(Array) ? "(value::json->>'type')::integer IN (?)" : "(value::json->>'type')::integer = ?" + type_query_str = type.is_a?(Array) ? "(value::json->>'type')::integer IN (?)" : "(value::json->>'type')::integer = ?" query = query.where(type_query_str, type) end query = query.where("(value::json->>'title')::text = ?", title) if title From 559d3f4f193369a537c2777b1c89c73f0f7ec503 Mon Sep 17 00:00:00 2001 From: angusmcleod Date: Thu, 18 Nov 2021 16:05:32 +0800 Subject: [PATCH 098/160] Use iso times for the notice timestamps --- lib/custom_wizard/notice.rb | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/lib/custom_wizard/notice.rb b/lib/custom_wizard/notice.rb index 1fcb5041..6f5c3045 100644 --- a/lib/custom_wizard/notice.rb +++ b/lib/custom_wizard/notice.rb @@ -4,6 +4,8 @@ class CustomWizard::Notice include ActiveModel::Serialization PLUGIN_STATUS_DOMAINS = { + "main" => "plugins.discourse.pavilion.tech", + "master" => "plugins.discourse.pavilion.tech", "tests-passed" => "plugins.discourse.pavilion.tech", "stable" => "stable.plugins.discourse.pavilion.tech" } @@ -42,21 +44,21 @@ class CustomWizard::Notice def dismiss! if dismissable? - self.dismissed_at = Time.now + self.dismissed_at = DateTime.now.iso8601(3) self.save_and_publish end end def hide! if can_hide? - self.hidden_at = Time.now + self.hidden_at = DateTime.now.iso8601(3) self.save_and_publish end end def expire! if !expired? - self.expired_at = Time.now + self.expired_at = DateTime.now.iso8601(3) self.save_and_publish end end @@ -154,7 +156,7 @@ class CustomWizard::Notice if notices.any? notices.each do |notice_data| notice = new(notice_data) - notice.retrieved_at = Time.now + notice.retrieved_at = DateTime.now.iso8601(3) notice.save end @@ -218,7 +220,7 @@ class CustomWizard::Notice if notices.any? notice = notices.first - notice.updated_at = Time.now + notice.updated_at = DateTime.now.iso8601(3) notice.save else notice = new( @@ -226,8 +228,8 @@ class CustomWizard::Notice message: I18n.t("wizard.notice.#{archetype.to_s}.connection_error.message", domain: domain), archetype: archetypes[archetype.to_sym], type: types[:connection_error], - created_at: Time.now, - updated_at: Time.now + created_at: DateTime.now.iso8601(3), + updated_at: DateTime.now.iso8601(3) ) notice.save end @@ -240,7 +242,8 @@ class CustomWizard::Notice end def self.subscription_message_domain - (Rails.env.test? || Rails.env.development?) ? LOCALHOST_DOMAIN : SUBSCRIPTION_MESSAGE_DOMAIN + return LOCALHOST_DOMAIN if (Rails.env.test? || Rails.env.development?) + SUBSCRIPTION_MESSAGE_DOMAIN end def self.subscription_message_url From e1a746ca28d5f1516e193034f287c5e85f7d55e3 Mon Sep 17 00:00:00 2001 From: angusmcleod Date: Thu, 18 Nov 2021 16:32:23 +0800 Subject: [PATCH 099/160] Add pluginId and console.logs --- .../discourse/controllers/admin-wizards-notices.js.es6 | 1 + assets/javascripts/discourse/controllers/admin-wizards.js.es6 | 1 + .../discourse/initializers/custom-wizard-edits.js.es6 | 3 +++ 3 files changed, 5 insertions(+) diff --git a/assets/javascripts/discourse/controllers/admin-wizards-notices.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards-notices.js.es6 index 1721e699..06c61054 100644 --- a/assets/javascripts/discourse/controllers/admin-wizards-notices.js.es6 +++ b/assets/javascripts/discourse/controllers/admin-wizards-notices.js.es6 @@ -29,6 +29,7 @@ export default Controller.extend({ CustomWizardNotice.list({ page, include_all: true }) .then((result) => { + console.log('loadMoreNotices result', result); if (result.notices.length === 0) { this.set("canLoadMore", false); return; diff --git a/assets/javascripts/discourse/controllers/admin-wizards.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards.js.es6 index 33841460..15a98525 100644 --- a/assets/javascripts/discourse/controllers/admin-wizards.js.es6 +++ b/assets/javascripts/discourse/controllers/admin-wizards.js.es6 @@ -14,6 +14,7 @@ export default Controller.extend({ this.messageBus.subscribe("/custom-wizard/notices", (data) => { if (isPresent(data.active_notice_count)) { this.set("activeNoticeCount", data.active_notice_count); + console.log('adminWizards resetting'); this.adminWizardsNotices.setProperties({ notices: A(), page: 0, diff --git a/assets/javascripts/discourse/initializers/custom-wizard-edits.js.es6 b/assets/javascripts/discourse/initializers/custom-wizard-edits.js.es6 index e67f1ebd..a74dafdc 100644 --- a/assets/javascripts/discourse/initializers/custom-wizard-edits.js.es6 +++ b/assets/javascripts/discourse/initializers/custom-wizard-edits.js.es6 @@ -23,6 +23,8 @@ export default { withPluginApi("0.8.36", (api) => { api.modifyClass("route:admin-dashboard", { + pluginId: "custom-wizard", + setupController(controller) { this._super(...arguments); @@ -32,6 +34,7 @@ export default { }); api.modifyClass("controller:admin-dashboard", { + pluginId: "custom-wizard", criticalNotices: A(), unsubscribe() { From fac8d821cf9ede74e170be058844bb44c3dd656c Mon Sep 17 00:00:00 2001 From: angusmcleod Date: Thu, 18 Nov 2021 17:07:15 +0800 Subject: [PATCH 100/160] publish notice count at the end of the update cycle --- .../discourse/controllers/admin-wizards-notices.js.es6 | 1 - .../javascripts/discourse/controllers/admin-wizards.js.es6 | 1 - lib/custom_wizard/notice.rb | 6 ++---- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/assets/javascripts/discourse/controllers/admin-wizards-notices.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards-notices.js.es6 index 06c61054..1721e699 100644 --- a/assets/javascripts/discourse/controllers/admin-wizards-notices.js.es6 +++ b/assets/javascripts/discourse/controllers/admin-wizards-notices.js.es6 @@ -29,7 +29,6 @@ export default Controller.extend({ CustomWizardNotice.list({ page, include_all: true }) .then((result) => { - console.log('loadMoreNotices result', result); if (result.notices.length === 0) { this.set("canLoadMore", false); return; diff --git a/assets/javascripts/discourse/controllers/admin-wizards.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards.js.es6 index 15a98525..33841460 100644 --- a/assets/javascripts/discourse/controllers/admin-wizards.js.es6 +++ b/assets/javascripts/discourse/controllers/admin-wizards.js.es6 @@ -14,7 +14,6 @@ export default Controller.extend({ this.messageBus.subscribe("/custom-wizard/notices", (data) => { if (isPresent(data.active_notice_count)) { this.set("activeNoticeCount", data.active_notice_count); - console.log('adminWizards resetting'); this.adminWizardsNotices.setProperties({ notices: A(), page: 0, diff --git a/lib/custom_wizard/notice.rb b/lib/custom_wizard/notice.rb index 6f5c3045..abd27559 100644 --- a/lib/custom_wizard/notice.rb +++ b/lib/custom_wizard/notice.rb @@ -159,9 +159,9 @@ class CustomWizard::Notice notice.retrieved_at = DateTime.now.iso8601(3) notice.save end - - publish_notice_count end + + publish_notice_count end def self.publish_notice_count @@ -233,8 +233,6 @@ class CustomWizard::Notice ) notice.save end - - publish_notice_count end def self.request_plugin_status? From 7af77533bacccbc97a5d5619bbbcd233187e3ead Mon Sep 17 00:00:00 2001 From: angusmcleod Date: Thu, 25 Nov 2021 14:38:16 +0800 Subject: [PATCH 101/160] Add https to notice requests --- lib/custom_wizard/notice.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/custom_wizard/notice.rb b/lib/custom_wizard/notice.rb index abd27559..9d5c1edf 100644 --- a/lib/custom_wizard/notice.rb +++ b/lib/custom_wizard/notice.rb @@ -245,7 +245,7 @@ class CustomWizard::Notice end def self.subscription_message_url - "http://#{subscription_message_domain}/subscription-server/messages.json" + "https://#{subscription_message_domain}/subscription-server/messages.json" end def self.plugin_status_domain @@ -254,7 +254,7 @@ class CustomWizard::Notice end def self.plugin_status_url - "http://#{plugin_status_domain}/plugin-manager/status/discourse-custom-wizard" + "https://#{plugin_status_domain}/plugin-manager/status/discourse-custom-wizard" end def self.request(archetype) From b62aee8a48e48eb25ad89988e2732898feb6183b Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Tue, 15 Feb 2022 17:16:21 +1100 Subject: [PATCH 102/160] WIP --- test/javascripts/wizard/test_helper.js | 74 ++++++++++++++ test/javascripts/wizard/wizard-pretender.js | 106 ++++++++++++++++++++ test/javascripts/wizard/wizard-test.js | 78 ++++++++++++++ 3 files changed, 258 insertions(+) create mode 100644 test/javascripts/wizard/test_helper.js create mode 100644 test/javascripts/wizard/wizard-pretender.js create mode 100644 test/javascripts/wizard/wizard-test.js diff --git a/test/javascripts/wizard/test_helper.js b/test/javascripts/wizard/test_helper.js new file mode 100644 index 00000000..7d851f1b --- /dev/null +++ b/test/javascripts/wizard/test_helper.js @@ -0,0 +1,74 @@ +// discourse-skip-module +/*global document, Logster, QUnit */ + +window.Discourse = {}; +window.Wizard = {}; +Wizard.SiteSettings = {}; +Discourse.__widget_helpers = {}; +Discourse.SiteSettings = Wizard.SiteSettings; + +//= require env +//= require jquery.debug +//= require ember.debug +//= require locales/i18n +//= require locales/en +//= require route-recognizer +//= require fake_xml_http_request +//= require pretender +//= require qunit +//= require ember-qunit +//= require discourse-loader +//= require jquery.debug +//= require handlebars +//= require ember-template-compiler +//= require wizard-application +//= require wizard-vendor +//= require_tree ./helpers +//= require_tree ./acceptance +//= require_tree ./models +//= require_tree ./components +//= require ./wizard-pretender +//= require test-shims + +document.addEventListener("DOMContentLoaded", function () { + document.body.insertAdjacentHTML( + "afterbegin", + ` +
+ + ` + ); +}); + +if (window.Logster) { + Logster.enabled = false; +} else { + window.Logster = { enabled: false }; +} +Ember.Test.adapter = window.QUnitAdapter.create(); + +let createPretendServer = requirejs( + "wizard/test/wizard-pretender", + null, + null, + false +).default; + +let server; +QUnit.testStart(function () { + server = createPretendServer(); +}); + +QUnit.testDone(function () { + server.shutdown(); +}); + +let _testApp = requirejs("wizard/test/helpers/start-app").default(); +let _buildResolver = requirejs("discourse-common/resolver").buildResolver; +window.setResolver(_buildResolver("wizard").create({ namespace: _testApp })); + +Object.keys(requirejs.entries).forEach(function (entry) { + if (/\-test/.test(entry)) { + requirejs(entry, null, null, true); + } +}); diff --git a/test/javascripts/wizard/wizard-pretender.js b/test/javascripts/wizard/wizard-pretender.js new file mode 100644 index 00000000..e9dccfb3 --- /dev/null +++ b/test/javascripts/wizard/wizard-pretender.js @@ -0,0 +1,106 @@ +import Pretender from "pretender"; + +// TODO: This file has some copied and pasted functions from `create-pretender` - would be good +// to centralize that code at some point. + +function parsePostData(query) { + const result = {}; + query.split("&").forEach(function (part) { + const item = part.split("="); + const firstSeg = decodeURIComponent(item[0]); + const m = /^([^\[]+)\[([^\]]+)\]/.exec(firstSeg); + + const val = decodeURIComponent(item[1]).replace(/\+/g, " "); + if (m) { + result[m[1]] = result[m[1]] || {}; + result[m[1]][m[2]] = val; + } else { + result[firstSeg] = val; + } + }); + return result; +} + +function response(code, obj) { + if (typeof code === "object") { + obj = code; + code = 200; + } + return [code, { "Content-Type": "application/json" }, obj]; +} + +export default function () { + const server = new Pretender(function () { + this.get("/wizard.json", () => { + return response(200, { + wizard: { + start: "hello-world", + completed: true, + steps: [ + { + id: "hello-world", + title: "hello there", + index: 0, + description: "hello!", + fields: [ + { + id: "full_name", + type: "text", + required: true, + description: "Your name", + }, + ], + next: "second-step", + }, + { + id: "second-step", + title: "Second step", + index: 1, + fields: [{ id: "some-title", type: "text" }], + previous: "hello-world", + next: "last-step", + }, + { + id: "last-step", + index: 2, + fields: [ + { id: "snack", type: "dropdown", required: true }, + { id: "theme-preview", type: "component" }, + { id: "an-image", type: "image" }, + ], + previous: "second-step", + }, + ], + }, + }); + }); + + this.put("/wizard/steps/:id", (request) => { + const body = parsePostData(request.requestBody); + + if (body.fields.full_name === "Server Fail") { + return response(422, { + errors: [{ field: "full_name", description: "Invalid name" }], + }); + } else { + return response(200, { success: true }); + } + }); + }); + + server.prepareBody = function (body) { + if (body && typeof body === "object") { + return JSON.stringify(body); + } + return body; + }; + + server.unhandledRequest = function (verb, path) { + const error = + "Unhandled request in test environment: " + path + " (" + verb + ")"; + window.console.error(error); + throw error; + }; + + return server; +} diff --git a/test/javascripts/wizard/wizard-test.js b/test/javascripts/wizard/wizard-test.js new file mode 100644 index 00000000..ea550cf2 --- /dev/null +++ b/test/javascripts/wizard/wizard-test.js @@ -0,0 +1,78 @@ +import { click, currentRouteName, fillIn, visit } from "@ember/test-helpers"; +import { module, test } from "qunit"; +import { run } from "@ember/runloop"; +import startApp from "wizard/test/helpers/start-app"; + +let wizard; +module("Acceptance: wizard", { + beforeEach() { + wizard = startApp(); + }, + + afterEach() { + run(wizard, "destroy"); + }, +}); + +function exists(selector) { + return document.querySelector(selector) !== null; +} + +test("Wizard starts", async function (assert) { + await visit("/"); + assert.ok(exists(".wizard-column-contents")); + assert.strictEqual(currentRouteName(), "step"); +}); + +test("Going back and forth in steps", async function (assert) { + await visit("/steps/hello-world"); + assert.ok(exists(".wizard-step")); + assert.ok( + exists(".wizard-step-hello-world"), + "it adds a class for the step id" + ); + assert.ok(!exists(".wizard-btn.finish"), "can’t finish on first step"); + assert.ok(exists(".wizard-progress")); + assert.ok(exists(".wizard-step-title")); + assert.ok(exists(".wizard-step-description")); + assert.ok( + !exists(".invalid .field-full-name"), + "don't show it as invalid until the user does something" + ); + assert.ok(exists(".wizard-field .field-description")); + assert.ok(!exists(".wizard-btn.back")); + assert.ok(!exists(".wizard-field .field-error-description")); + + // invalid data + await click(".wizard-btn.next"); + assert.ok(exists(".invalid .field-full-name")); + + // server validation fail + await fillIn("input.field-full-name", "Server Fail"); + await click(".wizard-btn.next"); + assert.ok(exists(".invalid .field-full-name")); + assert.ok(exists(".wizard-field .field-error-description")); + + // server validation ok + await fillIn("input.field-full-name", "Evil Trout"); + await click(".wizard-btn.next"); + assert.ok(!exists(".wizard-field .field-error-description")); + assert.ok(!exists(".wizard-step-description")); + assert.ok( + exists(".wizard-btn.finish"), + "shows finish on an intermediate step" + ); + + await click(".wizard-btn.next"); + assert.ok(exists(".select-kit.field-snack"), "went to the next step"); + assert.ok(exists(".preview-area"), "renders the component field"); + assert.ok(exists(".wizard-btn.done"), "last step shows a done button"); + assert.ok(exists(".action-link.back"), "shows the back button"); + assert.ok(!exists(".wizard-step-title")); + assert.ok(!exists(".wizard-btn.finish"), "can’t finish on last step"); + + await click(".action-link.back"); + assert.ok(exists(".wizard-step-title")); + assert.ok(exists(".wizard-btn.next")); + assert.ok(!exists(".wizard-prev")); +}); From b20b8ce3335af792b38b7d6319783e688c4eea39 Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Thu, 17 Feb 2022 17:08:14 +1100 Subject: [PATCH 103/160] WIP 2 --- .github/workflows/plugin-tests.yml | 2 +- .../javascripts/acceptance/wizard-test.js.es6 | 30 ++ test/javascripts/fixtures/wizard.js | 340 ++++++++++++++++++ test/javascripts/helpers/start-app.js | 23 ++ .../test_helper.js => plugin_helper.js} | 18 +- test/javascripts/wizard/wizard-test.js | 78 ---- .../wizard => views}/wizard-pretender.js | 45 +-- 7 files changed, 404 insertions(+), 132 deletions(-) create mode 100644 test/javascripts/acceptance/wizard-test.js.es6 create mode 100644 test/javascripts/fixtures/wizard.js create mode 100644 test/javascripts/helpers/start-app.js rename test/javascripts/{wizard/test_helper.js => plugin_helper.js} (86%) delete mode 100644 test/javascripts/wizard/wizard-test.js rename {test/javascripts/wizard => views}/wizard-pretender.js (57%) diff --git a/.github/workflows/plugin-tests.yml b/.github/workflows/plugin-tests.yml index 855bdfec..ef042fbc 100644 --- a/.github/workflows/plugin-tests.yml +++ b/.github/workflows/plugin-tests.yml @@ -31,7 +31,7 @@ jobs: build_type: ["backend", "frontend"] ruby: ["2.7"] postgres: ["12"] - redis: ["4.x"] + redis: ["6.x"] services: postgres: diff --git a/test/javascripts/acceptance/wizard-test.js.es6 b/test/javascripts/acceptance/wizard-test.js.es6 new file mode 100644 index 00000000..d7aa706a --- /dev/null +++ b/test/javascripts/acceptance/wizard-test.js.es6 @@ -0,0 +1,30 @@ +import { click, currentRouteName, fillIn, visit } from "@ember/test-helpers"; +import { module, test } from "qunit"; +import { run } from "@ember/runloop"; +import startApp from "../helpers/start-app"; +console.log("STARTING TEST"); +let wizard; +window.onerror = function (msg, url, lineNo, columnNo, error) { + console.log(error); + return false +} +module("Acceptance: Custom Wizard", { + beforeEach() { + console.log("BEFORE EACH") + wizard = startApp(); + }, + + afterEach() { + run(wizard, "destroy"); + }, +}); + +function exists(selector) { + return document.querySelector(selector) !== null; +} + +test("Wizard starts", async function (assert) { + console.log("TEST") + await visit("/w/wizard"); + assert.ok(exists(".wizard-column")); +}); diff --git a/test/javascripts/fixtures/wizard.js b/test/javascripts/fixtures/wizard.js new file mode 100644 index 00000000..0b3a219c --- /dev/null +++ b/test/javascripts/fixtures/wizard.js @@ -0,0 +1,340 @@ +export default { + "id": "super_mega_fun_wizard", + "name": "Super Mega Fun Wizard", + "background": "#333333", + "save_submissions": true, + "after_signup": false, + "prompt_completion": false, + "theme_id": 2, + "steps": [ + { + "id": "step_1", + "title": "Text", + "raw_description": "Text inputs!", + "fields": [ + { + "id": "step_1_field_1", + "label": "Text", + "description": "Text field description.", + "type": "text", + "min_length": "3", + "prefill": [ + { + "type": "assignment", + "output": "I am prefilled", + "output_type": "text", + "output_connector": "set" + } + ] + }, + { + "id": "step_1_field_2", + "label": "Textarea", + "type": "textarea", + "min_length": "" + }, + { + "id": "step_1_field_3", + "label": "Composer", + "type": "composer" + }, + { + "id": "step_1_field_4", + "label": "I'm only text", + "description": "", + "type": "text_only" + } + ], + "description": "Text inputs!" + }, + { + "id": "step_2", + "title": "Values", + "raw_description": "Because I couldn't think of another name for this step :)", + "fields": [ + { + "id": "step_2_field_1", + "label": "Date", + "type": "date", + "format": "YYYY-MM-DD" + }, + { + "id": "step_2_field_2", + "label": "Time", + "type": "time", + "format": "HH:mm" + }, + { + "id": "step_2_field_3", + "label": "Date & Time", + "type": "date_time", + "format": "" + }, + { + "id": "step_2_field_4", + "label": "Number", + "type": "number" + }, + { + "id": "step_2_field_5", + "label": "Checkbox", + "type": "checkbox" + }, + { + "id": "step_2_field_6", + "label": "Url", + "type": "url" + }, + { + "id": "step_2_field_7", + "label": "Upload", + "type": "upload", + "file_types": ".jpg,.jpeg,.png" + } + ], + "description": "Because I couldn't think of another name for this step :)" + }, + { + "id": "step_3", + "title": "Combo-boxes", + "raw_description": "Unfortunately not the edible type :sushi: ", + "fields": [ + { + "id": "step_3_field_1", + "label": "Custom Dropdown", + "type": "dropdown", + "content": [ + { + "type": "association", + "pairs": [ + { + "index": 0, + "key": "choice1", + "key_type": "text", + "value": "Choice 1", + "value_type": "text", + "connector": "equal" + }, + { + "index": 1, + "key": "choice2", + "key_type": "text", + "value": "Choice 2", + "value_type": "text", + "connector": "association" + }, + { + "index": 2, + "key": "choice3", + "key_type": "text", + "value": "Choice 3", + "value_type": "text", + "connector": "association" + } + ] + } + ] + }, + { + "id": "step_3_field_2", + "label": "Tag", + "type": "tag" + }, + { + "id": "step_3_field_3", + "label": "Category", + "type": "category", + "limit": 1, + "property": "id" + }, + { + "id": "step_3_field_4", + "label": "Group", + "type": "group" + }, + { + "id": "step_3_field_5", + "label": "User Selector", + "description": "", + "type": "user_selector" + }, + { + "id": "step_3_field_6", + "label": "Conditional User Selector", + "description": "Shown when checkbox in step_2_field_5 is true", + "type": "user_selector" + } + ], + "description": "Unfortunately not the edible type :sushi: " + } + ], + "actions": [ + { + "id": "action_1", + "run_after": "step_3", + "type": "create_topic", + "skip_redirect": true, + "post": "step_1_field_2", + "title": [ + { + "type": "assignment", + "output": "step_1_field_1", + "output_type": "wizard_field", + "output_connector": "set" + } + ], + "category": [ + { + "type": "assignment", + "output": "step_3_field_3", + "output_type": "wizard_field", + "output_connector": "set" + } + ], + "tags": [ + { + "type": "assignment", + "output": "step_3_field_2", + "output_type": "wizard_field", + "output_connector": "set" + } + ], + "custom_fields": [ + { + "type": "association", + "pairs": [ + { + "index": 0, + "key": "post_field", + "key_type": "text", + "value": "Post custom field value", + "value_type": "text", + "connector": "association" + }, + { + "index": 1, + "key": "topic.topic_field", + "key_type": "text", + "value": "Topic custom field value", + "value_type": "text", + "connector": "association" + }, + { + "index": 2, + "key": "topic.topic_json_field{}.key_1", + "key_type": "text", + "value": "Key 1 value", + "value_type": "text", + "connector": "association" + }, + { + "index": 3, + "key": "topic.topic_json_field{}.key_2", + "key_type": "text", + "value": "Key 2 value", + "value_type": "text", + "connector": "association" + } + ] + } + ], + "visible": [ + { + "type": "conditional", + "output": "true", + "output_type": "text", + "output_connector": "then", + "pairs": [ + { + "index": 0, + "key": "name", + "key_type": "user_field", + "value": "Angus", + "value_type": "text", + "connector": "equal" + } + ] + } + ] + }, + { + "id": "action_5", + "run_after": "step_1", + "type": "watch_categories", + "notification_level": "tracking", + "wizard_user": true, + "categories": [ + { + "type": "assignment", + "output": "action_8", + "output_type": "wizard_action", + "output_connector": "set" + } + ], + "mute_remainder": [ + { + "type": "assignment", + "output": "true", + "output_type": "text", + "output_connector": "set" + } + ] + }, + { + "id": "action_4", + "run_after": "step_2", + "type": "update_profile", + "profile_updates": [ + { + "type": "association", + "pairs": [ + { + "index": 0, + "key": "profile_background", + "key_type": "user_field", + "value": "step_2_field_7", + "value_type": "wizard_field", + "connector": "association" + } + ] + } + ] + }, + { + "id": "action_3", + "run_after": "step_2", + "type": "open_composer", + "post_builder": true, + "post_template": "I am interpolating some user fields u{name} u{username} u{email}", + "title": [ + { + "type": "assignment", + "output": "Title of the composer topic", + "output_type": "text", + "output_connector": "set" + } + ], + "tags": [ + { + "type": "assignment", + "output": "tag1", + "output_type": "text", + "output_connector": "set" + } + ] + }, + { + "id": "action_10", + "run_after": "step_3", + "type": "route_to", + "url": [ + { + "type": "assignment", + "output": "https://google.com", + "output_type": "text", + "output_connector": "set" + } + ] + } + ] +} diff --git a/test/javascripts/helpers/start-app.js b/test/javascripts/helpers/start-app.js new file mode 100644 index 00000000..6f5dbcc6 --- /dev/null +++ b/test/javascripts/helpers/start-app.js @@ -0,0 +1,23 @@ +import CustomWizard from "discourse/plugins/discourse-custom-wizard/wizard/custom-wizard"; +import wizardInitializer from "discourse/plugins/discourse-custom-wizard/wizard/initializers/custom-wizard"; +import stepInitializer from "discourse/plugins/discourse-custom-wizard/wizard/initializers/custom-wizard-step"; +import fieldInitializer from "discourse/plugins/discourse-custom-wizard/wizard/initializers/custom-wizard-field"; +import { run } from "@ember/runloop"; + +let app; +let started = false; + +export default function () { + run(() => (app = CustomWizard.create({ rootElement: "#ember-testing" }))); + + if (!started) { + wizardInitializer.initialize(app); + stepInitializer.initialize(app); + fieldInitializer.initialize(app); + app.start(); + started = true; + } + app.setupForTesting(); + app.injectTestHelpers(); + return app; +} diff --git a/test/javascripts/wizard/test_helper.js b/test/javascripts/plugin_helper.js similarity index 86% rename from test/javascripts/wizard/test_helper.js rename to test/javascripts/plugin_helper.js index 7d851f1b..9b1558cd 100644 --- a/test/javascripts/wizard/test_helper.js +++ b/test/javascripts/plugin_helper.js @@ -1,6 +1,6 @@ // discourse-skip-module /*global document, Logster, QUnit */ - +console.log('starting test_helper') window.Discourse = {}; window.Wizard = {}; Wizard.SiteSettings = {}; @@ -21,7 +21,7 @@ Discourse.SiteSettings = Wizard.SiteSettings; //= require jquery.debug //= require handlebars //= require ember-template-compiler -//= require wizard-application +//= require wizard-custom //= require wizard-vendor //= require_tree ./helpers //= require_tree ./acceptance @@ -29,7 +29,7 @@ Discourse.SiteSettings = Wizard.SiteSettings; //= require_tree ./components //= require ./wizard-pretender //= require test-shims - +console.log ("end of require") document.addEventListener("DOMContentLoaded", function () { document.body.insertAdjacentHTML( "afterbegin", @@ -47,12 +47,7 @@ if (window.Logster) { } Ember.Test.adapter = window.QUnitAdapter.create(); -let createPretendServer = requirejs( - "wizard/test/wizard-pretender", - null, - null, - false -).default; +/*let createPretendServer = requirejs("./wizard-pretender", null, null, false).default; let server; QUnit.testStart(function () { @@ -61,9 +56,9 @@ QUnit.testStart(function () { QUnit.testDone(function () { server.shutdown(); -}); +});*/ -let _testApp = requirejs("wizard/test/helpers/start-app").default(); +let _testApp = requirejs("./helpers/start-app").default(); let _buildResolver = requirejs("discourse-common/resolver").buildResolver; window.setResolver(_buildResolver("wizard").create({ namespace: _testApp })); @@ -72,3 +67,4 @@ Object.keys(requirejs.entries).forEach(function (entry) { requirejs(entry, null, null, true); } }); +console.log ("end of helper") diff --git a/test/javascripts/wizard/wizard-test.js b/test/javascripts/wizard/wizard-test.js deleted file mode 100644 index ea550cf2..00000000 --- a/test/javascripts/wizard/wizard-test.js +++ /dev/null @@ -1,78 +0,0 @@ -import { click, currentRouteName, fillIn, visit } from "@ember/test-helpers"; -import { module, test } from "qunit"; -import { run } from "@ember/runloop"; -import startApp from "wizard/test/helpers/start-app"; - -let wizard; -module("Acceptance: wizard", { - beforeEach() { - wizard = startApp(); - }, - - afterEach() { - run(wizard, "destroy"); - }, -}); - -function exists(selector) { - return document.querySelector(selector) !== null; -} - -test("Wizard starts", async function (assert) { - await visit("/"); - assert.ok(exists(".wizard-column-contents")); - assert.strictEqual(currentRouteName(), "step"); -}); - -test("Going back and forth in steps", async function (assert) { - await visit("/steps/hello-world"); - assert.ok(exists(".wizard-step")); - assert.ok( - exists(".wizard-step-hello-world"), - "it adds a class for the step id" - ); - assert.ok(!exists(".wizard-btn.finish"), "can’t finish on first step"); - assert.ok(exists(".wizard-progress")); - assert.ok(exists(".wizard-step-title")); - assert.ok(exists(".wizard-step-description")); - assert.ok( - !exists(".invalid .field-full-name"), - "don't show it as invalid until the user does something" - ); - assert.ok(exists(".wizard-field .field-description")); - assert.ok(!exists(".wizard-btn.back")); - assert.ok(!exists(".wizard-field .field-error-description")); - - // invalid data - await click(".wizard-btn.next"); - assert.ok(exists(".invalid .field-full-name")); - - // server validation fail - await fillIn("input.field-full-name", "Server Fail"); - await click(".wizard-btn.next"); - assert.ok(exists(".invalid .field-full-name")); - assert.ok(exists(".wizard-field .field-error-description")); - - // server validation ok - await fillIn("input.field-full-name", "Evil Trout"); - await click(".wizard-btn.next"); - assert.ok(!exists(".wizard-field .field-error-description")); - assert.ok(!exists(".wizard-step-description")); - assert.ok( - exists(".wizard-btn.finish"), - "shows finish on an intermediate step" - ); - - await click(".wizard-btn.next"); - assert.ok(exists(".select-kit.field-snack"), "went to the next step"); - assert.ok(exists(".preview-area"), "renders the component field"); - assert.ok(exists(".wizard-btn.done"), "last step shows a done button"); - assert.ok(exists(".action-link.back"), "shows the back button"); - assert.ok(!exists(".wizard-step-title")); - assert.ok(!exists(".wizard-btn.finish"), "can’t finish on last step"); - - await click(".action-link.back"); - assert.ok(exists(".wizard-step-title")); - assert.ok(exists(".wizard-btn.next")); - assert.ok(!exists(".wizard-prev")); -}); diff --git a/test/javascripts/wizard/wizard-pretender.js b/views/wizard-pretender.js similarity index 57% rename from test/javascripts/wizard/wizard-pretender.js rename to views/wizard-pretender.js index e9dccfb3..ac0ef1e8 100644 --- a/test/javascripts/wizard/wizard-pretender.js +++ b/views/wizard-pretender.js @@ -1,4 +1,5 @@ import Pretender from "pretender"; +import WizardJson from "./fixtures/wizard"; // TODO: This file has some copied and pasted functions from `create-pretender` - would be good // to centralize that code at some point. @@ -31,48 +32,8 @@ function response(code, obj) { export default function () { const server = new Pretender(function () { - this.get("/wizard.json", () => { - return response(200, { - wizard: { - start: "hello-world", - completed: true, - steps: [ - { - id: "hello-world", - title: "hello there", - index: 0, - description: "hello!", - fields: [ - { - id: "full_name", - type: "text", - required: true, - description: "Your name", - }, - ], - next: "second-step", - }, - { - id: "second-step", - title: "Second step", - index: 1, - fields: [{ id: "some-title", type: "text" }], - previous: "hello-world", - next: "last-step", - }, - { - id: "last-step", - index: 2, - fields: [ - { id: "snack", type: "dropdown", required: true }, - { id: "theme-preview", type: "component" }, - { id: "an-image", type: "image" }, - ], - previous: "second-step", - }, - ], - }, - }); + this.get("/w/wizard.json", () => { + return response(200, cloneJSON(WizardJson); }); this.put("/wizard/steps/:id", (request) => { From cb7bb4e12f95c6a3894ef8bb61ac532d118d0629 Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Sat, 12 Mar 2022 14:49:41 +0100 Subject: [PATCH 104/160] Move to proper folder structure --- .../controllers}/custom_wizard/admin/admin.rb | 0 .../controllers}/custom_wizard/admin/api.rb | 0 .../custom_wizard/admin/custom_fields.rb | 0 .../controllers}/custom_wizard/admin/logs.rb | 0 .../custom_wizard/admin/manager.rb | 0 .../custom_wizard/admin/notice.rb | 0 .../custom_wizard/admin/submissions.rb | 0 .../custom_wizard/admin/subscription.rb | 0 .../custom_wizard/admin/wizard.rb | 0 .../custom_wizard/realtime_validations.rb | 0 .../controllers}/custom_wizard/steps.rb | 0 .../controllers}/custom_wizard/wizard.rb | 0 .../jobs}/regular/refresh_api_access_token.rb | 0 .../jobs}/regular/set_after_time_wizard.rb | 0 .../scheduled/custom_wizard/update_notices.rb | 0 .../custom_wizard/update_subscription.rb | 0 .../api/authorization_serializer.rb | 0 .../api/basic_endpoint_serializer.rb | 0 .../custom_wizard/api/endpoint_serializer.rb | 0 .../custom_wizard/api/log_serializer.rb | 0 .../custom_wizard/api_serializer.rb | 0 .../custom_wizard/basic_api_serializer.rb | 0 .../custom_wizard/basic_wizard_serializer.rb | 0 .../custom_wizard/custom_field_serializer.rb | 0 .../custom_wizard/log_serializer.rb | 0 .../custom_wizard/notice_serializer.rb | 0 .../similar_topics_serializer.rb | 0 .../custom_wizard/submission_serializer.rb | 0 .../subscription/authentication_serializer.rb | 0 .../subscription/subscription_serializer.rb | 0 .../custom_wizard/subscription_serializer.rb | 0 .../custom_wizard/wizard_field_serializer.rb | 0 .../custom_wizard/wizard_serializer.rb | 0 .../custom_wizard/wizard_step_serializer.rb | 0 {views => app/views}/layouts/wizard.html.erb | 0 .../extensions}/custom_field/extension.rb | 0 .../extensions}/custom_field/preloader.rb | 0 .../extensions}/custom_field/serializer.rb | 0 .../extensions}/extra_locales_controller.rb | 0 .../extensions}/invites_controller.rb | 0 .../extensions}/users_controller.rb | 0 plugin.rb | 80 +++++++++---------- 42 files changed, 40 insertions(+), 40 deletions(-) rename {controllers => app/controllers}/custom_wizard/admin/admin.rb (100%) rename {controllers => app/controllers}/custom_wizard/admin/api.rb (100%) rename {controllers => app/controllers}/custom_wizard/admin/custom_fields.rb (100%) rename {controllers => app/controllers}/custom_wizard/admin/logs.rb (100%) rename {controllers => app/controllers}/custom_wizard/admin/manager.rb (100%) rename {controllers => app/controllers}/custom_wizard/admin/notice.rb (100%) rename {controllers => app/controllers}/custom_wizard/admin/submissions.rb (100%) rename {controllers => app/controllers}/custom_wizard/admin/subscription.rb (100%) rename {controllers => app/controllers}/custom_wizard/admin/wizard.rb (100%) rename {controllers => app/controllers}/custom_wizard/realtime_validations.rb (100%) rename {controllers => app/controllers}/custom_wizard/steps.rb (100%) rename {controllers => app/controllers}/custom_wizard/wizard.rb (100%) rename {jobs => app/jobs}/regular/refresh_api_access_token.rb (100%) rename {jobs => app/jobs}/regular/set_after_time_wizard.rb (100%) rename {jobs => app/jobs}/scheduled/custom_wizard/update_notices.rb (100%) rename {jobs => app/jobs}/scheduled/custom_wizard/update_subscription.rb (100%) rename {serializers => app/serializers}/custom_wizard/api/authorization_serializer.rb (100%) rename {serializers => app/serializers}/custom_wizard/api/basic_endpoint_serializer.rb (100%) rename {serializers => app/serializers}/custom_wizard/api/endpoint_serializer.rb (100%) rename {serializers => app/serializers}/custom_wizard/api/log_serializer.rb (100%) rename {serializers => app/serializers}/custom_wizard/api_serializer.rb (100%) rename {serializers => app/serializers}/custom_wizard/basic_api_serializer.rb (100%) rename {serializers => app/serializers}/custom_wizard/basic_wizard_serializer.rb (100%) rename {serializers => app/serializers}/custom_wizard/custom_field_serializer.rb (100%) rename {serializers => app/serializers}/custom_wizard/log_serializer.rb (100%) rename {serializers => app/serializers}/custom_wizard/notice_serializer.rb (100%) rename {serializers => app/serializers}/custom_wizard/realtime_validation/similar_topics_serializer.rb (100%) rename {serializers => app/serializers}/custom_wizard/submission_serializer.rb (100%) rename {serializers => app/serializers}/custom_wizard/subscription/authentication_serializer.rb (100%) rename {serializers => app/serializers}/custom_wizard/subscription/subscription_serializer.rb (100%) rename {serializers => app/serializers}/custom_wizard/subscription_serializer.rb (100%) rename {serializers => app/serializers}/custom_wizard/wizard_field_serializer.rb (100%) rename {serializers => app/serializers}/custom_wizard/wizard_serializer.rb (100%) rename {serializers => app/serializers}/custom_wizard/wizard_step_serializer.rb (100%) rename {views => app/views}/layouts/wizard.html.erb (100%) rename {extensions => lib/custom_wizard/extensions}/custom_field/extension.rb (100%) rename {extensions => lib/custom_wizard/extensions}/custom_field/preloader.rb (100%) rename {extensions => lib/custom_wizard/extensions}/custom_field/serializer.rb (100%) rename {extensions => lib/custom_wizard/extensions}/extra_locales_controller.rb (100%) rename {extensions => lib/custom_wizard/extensions}/invites_controller.rb (100%) rename {extensions => lib/custom_wizard/extensions}/users_controller.rb (100%) diff --git a/controllers/custom_wizard/admin/admin.rb b/app/controllers/custom_wizard/admin/admin.rb similarity index 100% rename from controllers/custom_wizard/admin/admin.rb rename to app/controllers/custom_wizard/admin/admin.rb diff --git a/controllers/custom_wizard/admin/api.rb b/app/controllers/custom_wizard/admin/api.rb similarity index 100% rename from controllers/custom_wizard/admin/api.rb rename to app/controllers/custom_wizard/admin/api.rb diff --git a/controllers/custom_wizard/admin/custom_fields.rb b/app/controllers/custom_wizard/admin/custom_fields.rb similarity index 100% rename from controllers/custom_wizard/admin/custom_fields.rb rename to app/controllers/custom_wizard/admin/custom_fields.rb diff --git a/controllers/custom_wizard/admin/logs.rb b/app/controllers/custom_wizard/admin/logs.rb similarity index 100% rename from controllers/custom_wizard/admin/logs.rb rename to app/controllers/custom_wizard/admin/logs.rb diff --git a/controllers/custom_wizard/admin/manager.rb b/app/controllers/custom_wizard/admin/manager.rb similarity index 100% rename from controllers/custom_wizard/admin/manager.rb rename to app/controllers/custom_wizard/admin/manager.rb diff --git a/controllers/custom_wizard/admin/notice.rb b/app/controllers/custom_wizard/admin/notice.rb similarity index 100% rename from controllers/custom_wizard/admin/notice.rb rename to app/controllers/custom_wizard/admin/notice.rb diff --git a/controllers/custom_wizard/admin/submissions.rb b/app/controllers/custom_wizard/admin/submissions.rb similarity index 100% rename from controllers/custom_wizard/admin/submissions.rb rename to app/controllers/custom_wizard/admin/submissions.rb diff --git a/controllers/custom_wizard/admin/subscription.rb b/app/controllers/custom_wizard/admin/subscription.rb similarity index 100% rename from controllers/custom_wizard/admin/subscription.rb rename to app/controllers/custom_wizard/admin/subscription.rb diff --git a/controllers/custom_wizard/admin/wizard.rb b/app/controllers/custom_wizard/admin/wizard.rb similarity index 100% rename from controllers/custom_wizard/admin/wizard.rb rename to app/controllers/custom_wizard/admin/wizard.rb diff --git a/controllers/custom_wizard/realtime_validations.rb b/app/controllers/custom_wizard/realtime_validations.rb similarity index 100% rename from controllers/custom_wizard/realtime_validations.rb rename to app/controllers/custom_wizard/realtime_validations.rb diff --git a/controllers/custom_wizard/steps.rb b/app/controllers/custom_wizard/steps.rb similarity index 100% rename from controllers/custom_wizard/steps.rb rename to app/controllers/custom_wizard/steps.rb diff --git a/controllers/custom_wizard/wizard.rb b/app/controllers/custom_wizard/wizard.rb similarity index 100% rename from controllers/custom_wizard/wizard.rb rename to app/controllers/custom_wizard/wizard.rb diff --git a/jobs/regular/refresh_api_access_token.rb b/app/jobs/regular/refresh_api_access_token.rb similarity index 100% rename from jobs/regular/refresh_api_access_token.rb rename to app/jobs/regular/refresh_api_access_token.rb diff --git a/jobs/regular/set_after_time_wizard.rb b/app/jobs/regular/set_after_time_wizard.rb similarity index 100% rename from jobs/regular/set_after_time_wizard.rb rename to app/jobs/regular/set_after_time_wizard.rb diff --git a/jobs/scheduled/custom_wizard/update_notices.rb b/app/jobs/scheduled/custom_wizard/update_notices.rb similarity index 100% rename from jobs/scheduled/custom_wizard/update_notices.rb rename to app/jobs/scheduled/custom_wizard/update_notices.rb diff --git a/jobs/scheduled/custom_wizard/update_subscription.rb b/app/jobs/scheduled/custom_wizard/update_subscription.rb similarity index 100% rename from jobs/scheduled/custom_wizard/update_subscription.rb rename to app/jobs/scheduled/custom_wizard/update_subscription.rb diff --git a/serializers/custom_wizard/api/authorization_serializer.rb b/app/serializers/custom_wizard/api/authorization_serializer.rb similarity index 100% rename from serializers/custom_wizard/api/authorization_serializer.rb rename to app/serializers/custom_wizard/api/authorization_serializer.rb diff --git a/serializers/custom_wizard/api/basic_endpoint_serializer.rb b/app/serializers/custom_wizard/api/basic_endpoint_serializer.rb similarity index 100% rename from serializers/custom_wizard/api/basic_endpoint_serializer.rb rename to app/serializers/custom_wizard/api/basic_endpoint_serializer.rb diff --git a/serializers/custom_wizard/api/endpoint_serializer.rb b/app/serializers/custom_wizard/api/endpoint_serializer.rb similarity index 100% rename from serializers/custom_wizard/api/endpoint_serializer.rb rename to app/serializers/custom_wizard/api/endpoint_serializer.rb diff --git a/serializers/custom_wizard/api/log_serializer.rb b/app/serializers/custom_wizard/api/log_serializer.rb similarity index 100% rename from serializers/custom_wizard/api/log_serializer.rb rename to app/serializers/custom_wizard/api/log_serializer.rb diff --git a/serializers/custom_wizard/api_serializer.rb b/app/serializers/custom_wizard/api_serializer.rb similarity index 100% rename from serializers/custom_wizard/api_serializer.rb rename to app/serializers/custom_wizard/api_serializer.rb diff --git a/serializers/custom_wizard/basic_api_serializer.rb b/app/serializers/custom_wizard/basic_api_serializer.rb similarity index 100% rename from serializers/custom_wizard/basic_api_serializer.rb rename to app/serializers/custom_wizard/basic_api_serializer.rb diff --git a/serializers/custom_wizard/basic_wizard_serializer.rb b/app/serializers/custom_wizard/basic_wizard_serializer.rb similarity index 100% rename from serializers/custom_wizard/basic_wizard_serializer.rb rename to app/serializers/custom_wizard/basic_wizard_serializer.rb diff --git a/serializers/custom_wizard/custom_field_serializer.rb b/app/serializers/custom_wizard/custom_field_serializer.rb similarity index 100% rename from serializers/custom_wizard/custom_field_serializer.rb rename to app/serializers/custom_wizard/custom_field_serializer.rb diff --git a/serializers/custom_wizard/log_serializer.rb b/app/serializers/custom_wizard/log_serializer.rb similarity index 100% rename from serializers/custom_wizard/log_serializer.rb rename to app/serializers/custom_wizard/log_serializer.rb diff --git a/serializers/custom_wizard/notice_serializer.rb b/app/serializers/custom_wizard/notice_serializer.rb similarity index 100% rename from serializers/custom_wizard/notice_serializer.rb rename to app/serializers/custom_wizard/notice_serializer.rb diff --git a/serializers/custom_wizard/realtime_validation/similar_topics_serializer.rb b/app/serializers/custom_wizard/realtime_validation/similar_topics_serializer.rb similarity index 100% rename from serializers/custom_wizard/realtime_validation/similar_topics_serializer.rb rename to app/serializers/custom_wizard/realtime_validation/similar_topics_serializer.rb diff --git a/serializers/custom_wizard/submission_serializer.rb b/app/serializers/custom_wizard/submission_serializer.rb similarity index 100% rename from serializers/custom_wizard/submission_serializer.rb rename to app/serializers/custom_wizard/submission_serializer.rb diff --git a/serializers/custom_wizard/subscription/authentication_serializer.rb b/app/serializers/custom_wizard/subscription/authentication_serializer.rb similarity index 100% rename from serializers/custom_wizard/subscription/authentication_serializer.rb rename to app/serializers/custom_wizard/subscription/authentication_serializer.rb diff --git a/serializers/custom_wizard/subscription/subscription_serializer.rb b/app/serializers/custom_wizard/subscription/subscription_serializer.rb similarity index 100% rename from serializers/custom_wizard/subscription/subscription_serializer.rb rename to app/serializers/custom_wizard/subscription/subscription_serializer.rb diff --git a/serializers/custom_wizard/subscription_serializer.rb b/app/serializers/custom_wizard/subscription_serializer.rb similarity index 100% rename from serializers/custom_wizard/subscription_serializer.rb rename to app/serializers/custom_wizard/subscription_serializer.rb diff --git a/serializers/custom_wizard/wizard_field_serializer.rb b/app/serializers/custom_wizard/wizard_field_serializer.rb similarity index 100% rename from serializers/custom_wizard/wizard_field_serializer.rb rename to app/serializers/custom_wizard/wizard_field_serializer.rb diff --git a/serializers/custom_wizard/wizard_serializer.rb b/app/serializers/custom_wizard/wizard_serializer.rb similarity index 100% rename from serializers/custom_wizard/wizard_serializer.rb rename to app/serializers/custom_wizard/wizard_serializer.rb diff --git a/serializers/custom_wizard/wizard_step_serializer.rb b/app/serializers/custom_wizard/wizard_step_serializer.rb similarity index 100% rename from serializers/custom_wizard/wizard_step_serializer.rb rename to app/serializers/custom_wizard/wizard_step_serializer.rb diff --git a/views/layouts/wizard.html.erb b/app/views/layouts/wizard.html.erb similarity index 100% rename from views/layouts/wizard.html.erb rename to app/views/layouts/wizard.html.erb diff --git a/extensions/custom_field/extension.rb b/lib/custom_wizard/extensions/custom_field/extension.rb similarity index 100% rename from extensions/custom_field/extension.rb rename to lib/custom_wizard/extensions/custom_field/extension.rb diff --git a/extensions/custom_field/preloader.rb b/lib/custom_wizard/extensions/custom_field/preloader.rb similarity index 100% rename from extensions/custom_field/preloader.rb rename to lib/custom_wizard/extensions/custom_field/preloader.rb diff --git a/extensions/custom_field/serializer.rb b/lib/custom_wizard/extensions/custom_field/serializer.rb similarity index 100% rename from extensions/custom_field/serializer.rb rename to lib/custom_wizard/extensions/custom_field/serializer.rb diff --git a/extensions/extra_locales_controller.rb b/lib/custom_wizard/extensions/extra_locales_controller.rb similarity index 100% rename from extensions/extra_locales_controller.rb rename to lib/custom_wizard/extensions/extra_locales_controller.rb diff --git a/extensions/invites_controller.rb b/lib/custom_wizard/extensions/invites_controller.rb similarity index 100% rename from extensions/invites_controller.rb rename to lib/custom_wizard/extensions/invites_controller.rb diff --git a/extensions/users_controller.rb b/lib/custom_wizard/extensions/users_controller.rb similarity index 100% rename from extensions/users_controller.rb rename to lib/custom_wizard/extensions/users_controller.rb diff --git a/plugin.rb b/plugin.rb index 8c6be4db..e41fcee8 100644 --- a/plugin.rb +++ b/plugin.rb @@ -63,22 +63,22 @@ after_initialize do %w[ ../lib/custom_wizard/engine.rb ../config/routes.rb - ../controllers/custom_wizard/admin/admin.rb - ../controllers/custom_wizard/admin/wizard.rb - ../controllers/custom_wizard/admin/submissions.rb - ../controllers/custom_wizard/admin/api.rb - ../controllers/custom_wizard/admin/logs.rb - ../controllers/custom_wizard/admin/manager.rb - ../controllers/custom_wizard/admin/custom_fields.rb - ../controllers/custom_wizard/admin/subscription.rb - ../controllers/custom_wizard/admin/notice.rb - ../controllers/custom_wizard/wizard.rb - ../controllers/custom_wizard/steps.rb - ../controllers/custom_wizard/realtime_validations.rb - ../jobs/regular/refresh_api_access_token.rb - ../jobs/regular/set_after_time_wizard.rb - ../jobs/scheduled/custom_wizard/update_subscription.rb - ../jobs/scheduled/custom_wizard/update_notices.rb + ../app/controllers/custom_wizard/admin/admin.rb + ../app/controllers/custom_wizard/admin/wizard.rb + ../app/controllers/custom_wizard/admin/submissions.rb + ../app/controllers/custom_wizard/admin/api.rb + ../app/controllers/custom_wizard/admin/logs.rb + ../app/controllers/custom_wizard/admin/manager.rb + ../app/controllers/custom_wizard/admin/custom_fields.rb + ../app/controllers/custom_wizard/admin/subscription.rb + ../app/controllers/custom_wizard/admin/notice.rb + ../app/controllers/custom_wizard/wizard.rb + ../app/controllers/custom_wizard/steps.rb + ../app/controllers/custom_wizard/realtime_validations.rb + ../app/jobs/regular/refresh_api_access_token.rb + ../app/jobs/regular/set_after_time_wizard.rb + ../app/jobs/scheduled/custom_wizard/update_subscription.rb + ../app/jobs/scheduled/custom_wizard/update_notices.rb ../lib/custom_wizard/validators/template.rb ../lib/custom_wizard/validators/update.rb ../lib/custom_wizard/action_result.rb @@ -108,30 +108,30 @@ after_initialize do ../lib/custom_wizard/api/log_entry.rb ../lib/custom_wizard/liquid_extensions/first_non_empty.rb ../lib/custom_wizard/exceptions/exceptions.rb - ../serializers/custom_wizard/api/authorization_serializer.rb - ../serializers/custom_wizard/api/basic_endpoint_serializer.rb - ../serializers/custom_wizard/api/endpoint_serializer.rb - ../serializers/custom_wizard/api/log_serializer.rb - ../serializers/custom_wizard/api_serializer.rb - ../serializers/custom_wizard/basic_api_serializer.rb - ../serializers/custom_wizard/basic_wizard_serializer.rb - ../serializers/custom_wizard/custom_field_serializer.rb - ../serializers/custom_wizard/wizard_field_serializer.rb - ../serializers/custom_wizard/wizard_step_serializer.rb - ../serializers/custom_wizard/wizard_serializer.rb - ../serializers/custom_wizard/log_serializer.rb - ../serializers/custom_wizard/submission_serializer.rb - ../serializers/custom_wizard/realtime_validation/similar_topics_serializer.rb - ../serializers/custom_wizard/subscription/authentication_serializer.rb - ../serializers/custom_wizard/subscription/subscription_serializer.rb - ../serializers/custom_wizard/subscription_serializer.rb - ../serializers/custom_wizard/notice_serializer.rb - ../extensions/extra_locales_controller.rb - ../extensions/invites_controller.rb - ../extensions/users_controller.rb - ../extensions/custom_field/preloader.rb - ../extensions/custom_field/serializer.rb - ../extensions/custom_field/extension.rb + ../app/serializers/custom_wizard/api/authorization_serializer.rb + ../app/serializers/custom_wizard/api/basic_endpoint_serializer.rb + ../app/serializers/custom_wizard/api/endpoint_serializer.rb + ../app/serializers/custom_wizard/api/log_serializer.rb + ../app/serializers/custom_wizard/api_serializer.rb + ../app/serializers/custom_wizard/basic_api_serializer.rb + ../app/serializers/custom_wizard/basic_wizard_serializer.rb + ../app/serializers/custom_wizard/custom_field_serializer.rb + ../app/serializers/custom_wizard/wizard_field_serializer.rb + ../app/serializers/custom_wizard/wizard_step_serializer.rb + ../app/serializers/custom_wizard/wizard_serializer.rb + ../app/serializers/custom_wizard/log_serializer.rb + ../app/serializers/custom_wizard/submission_serializer.rb + ../app/serializers/custom_wizard/realtime_validation/similar_topics_serializer.rb + ../app/serializers/custom_wizard/subscription/authentication_serializer.rb + ../app/serializers/custom_wizard/subscription/subscription_serializer.rb + ../app/serializers/custom_wizard/subscription_serializer.rb + ../app/serializers/custom_wizard/notice_serializer.rb + ..//lib/custom_wizard/extensions/extra_locales_controller.rb + ..//lib/custom_wizard/extensions/invites_controller.rb + ..//lib/custom_wizard/extensions/users_controller.rb + ..//lib/custom_wizard/extensions/custom_field/preloader.rb + ..//lib/custom_wizard/extensions/custom_field/serializer.rb + ..//lib/custom_wizard/extensions/custom_field/extension.rb ].each do |path| load File.expand_path(path, __FILE__) end From d57f260defa01052fa0e9f544add48efc3606982 Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Sat, 12 Mar 2022 15:20:54 +0100 Subject: [PATCH 105/160] Cleanup after merge --- app/controllers/custom_wizard/admin/logs.rb | 41 +++++++++++++++++-- .../custom_wizard/log_serializer.rb | 8 +++- .../custom_wizard/submission_serializer.rb | 34 +++++++++++---- spec/plugin_helper.rb | 17 -------- 4 files changed, 70 insertions(+), 30 deletions(-) diff --git a/app/controllers/custom_wizard/admin/logs.rb b/app/controllers/custom_wizard/admin/logs.rb index 976814f8..7ca37bb2 100644 --- a/app/controllers/custom_wizard/admin/logs.rb +++ b/app/controllers/custom_wizard/admin/logs.rb @@ -1,9 +1,44 @@ # frozen_string_literal: true class CustomWizard::AdminLogsController < CustomWizard::AdminController + before_action :find_wizard, except: [:index] + def index - render_serialized( - CustomWizard::Log.list(params[:page].to_i, params[:limit].to_i), - CustomWizard::LogSerializer + render json: ActiveModel::ArraySerializer.new( + CustomWizard::Wizard.list(current_user), + each_serializer: CustomWizard::BasicWizardSerializer ) end + + def show + render_json_dump( + wizard: CustomWizard::BasicWizardSerializer.new(@wizard, root: false), + logs: ActiveModel::ArraySerializer.new( + log_list.logs, + each_serializer: CustomWizard::LogSerializer + ), + total: log_list.total + ) + end + + protected + + def log_list + @log_list ||= begin + list = CustomWizard::Log.list(params[:page].to_i, params[:limit].to_i, params[:wizard_id]) + + if list.logs.any? && (usernames = list.logs.map(&:username)).present? + user_map = User.where(username: usernames) + .reduce({}) do |result, user| + result[user.username] = user + result + end + + list.logs.each do |log_item| + log_item.user = user_map[log_item.username] + end + end + + list + end + end end diff --git a/app/serializers/custom_wizard/log_serializer.rb b/app/serializers/custom_wizard/log_serializer.rb index e521c573..56c5fd8f 100644 --- a/app/serializers/custom_wizard/log_serializer.rb +++ b/app/serializers/custom_wizard/log_serializer.rb @@ -1,4 +1,10 @@ # frozen_string_literal: true + class CustomWizard::LogSerializer < ApplicationSerializer - attributes :message, :date + attributes :date, + :action, + :username, + :message + + has_one :user, serializer: ::BasicUserSerializer, embed: :objects end diff --git a/app/serializers/custom_wizard/submission_serializer.rb b/app/serializers/custom_wizard/submission_serializer.rb index 52f0cb32..9c2f8133 100644 --- a/app/serializers/custom_wizard/submission_serializer.rb +++ b/app/serializers/custom_wizard/submission_serializer.rb @@ -1,16 +1,32 @@ # frozen_string_literal: true class CustomWizard::SubmissionSerializer < ApplicationSerializer attributes :id, - :username, :fields, - :submitted_at, - :route_to, - :redirect_on_complete, - :redirect_to + :submitted_at - def username - object.user.present? ? - object.user.username : - I18n.t('admin.wizard.submission.no_user', user_id: object.user_id) + has_one :user, serializer: ::BasicUserSerializer, embed: :objects + + def include_user? + object.user.present? + end + + def fields + @fields ||= begin + result = {} + + object.wizard.template['steps'].each do |step| + step['fields'].each do |field| + if value = object.fields[field['id']] + result[field['id']] = { + value: value, + type: field['type'], + label: field['label'] + } + end + end + end + + result + end end end diff --git a/spec/plugin_helper.rb b/spec/plugin_helper.rb index 232a2411..6e340ccf 100644 --- a/spec/plugin_helper.rb +++ b/spec/plugin_helper.rb @@ -1,22 +1,5 @@ # frozen_string_literal: true -if ENV['SIMPLECOV'] - require 'simplecov' - - SimpleCov.start do - root "plugins/discourse-custom-wizard" - track_files "plugins/discourse-custom-wizard/**/*.rb" - add_filter { |src| src.filename =~ /(\/spec\/|\/db\/|plugin\.rb|api|gems)/ } - SimpleCov.minimum_coverage 80 - end -end - -require 'oj' -Oj.default_options = Oj.default_options.merge(cache_str: -1) - -require 'rails_helper' -require 'webmock/rspec' - def get_wizard_fixture(path) JSON.parse( File.open( From 8893e6caf1dc97c7351023451f7916a86fbd9c3d Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Wed, 16 Mar 2022 12:33:34 +0100 Subject: [PATCH 106/160] Refactor wizard client and add tests --- assets/javascripts/wizard-custom-globals.js | 6 - assets/javascripts/wizard-custom-start.js | 2 +- assets/javascripts/wizard-custom.js | 15 +- assets/javascripts/wizard-qunit.js | 16 + assets/javascripts/wizard/application.js.es6 | 19 + .../components/custom-user-selector.js.es6 | 9 +- .../wizard/components/field-validators.js.es6 | 3 + .../similar-topics-validator.js.es6 | 1 + .../wizard/components/validator.js.es6 | 3 +- .../components/wizard-composer-editor.js.es6 | 1 + .../wizard-composer-hyperlink.js.es6 | 1 + .../components/wizard-date-input.js.es6 | 1 + .../components/wizard-date-time-input.js.es6 | 2 + .../components/wizard-field-category.js.es6 | 2 + .../components/wizard-field-checkbox.js.es6 | 5 + .../wizard-field-composer-preview.js.es6 | 2 + .../components/wizard-field-composer.js.es6 | 2 + .../components/wizard-field-date-time.js.es6 | 2 + .../components/wizard-field-date.js.es6 | 2 + .../components/wizard-field-dropdown.js.es6 | 15 + .../components/wizard-field-group.js.es6 | 5 + .../components/wizard-field-number.js.es6 | 5 + .../wizard/components/wizard-field-tag.js.es6 | 5 + .../components/wizard-field-text.js.es6 | 9 + .../components/wizard-field-textarea.js.es6 | 9 + .../components/wizard-field-time.js.es6 | 2 + .../components/wizard-field-upload.js.es6 | 4 + .../wizard/components/wizard-field-url.js.es6 | 5 + .../wizard-field-user-selector.js.es6 | 5 + .../wizard/components/wizard-field.js.es6 | 34 ++ .../components/wizard-group-selector.js.es6 | 1 + .../wizard/components/wizard-no-access.js.es6 | 23 +- .../components/wizard-similar-topics.js.es6 | 1 + .../wizard/components/wizard-step-form.js.es6 | 9 + .../wizard/components/wizard-step.js.es6 | 247 +++++++++ .../components/wizard-text-field.js.es6 | 6 +- .../components/wizard-time-input.js.es6 | 4 +- .../wizard/controllers/custom.js.es6 | 3 - .../{custom-step.js.es6 => step.js.es6} | 11 +- .../wizard/controllers/wizard-index.js.es6 | 24 + .../wizard/controllers/wizard.js.es6 | 5 + .../javascripts/wizard/custom-wizard.js.es6 | 39 -- .../initializers/custom-wizard-step.js.es6 | 219 -------- .../wizard/initializers/custom-wizard.js.es6 | 126 ----- .../lib/initialize/create-contexts.js.es6 | 12 + .../lib/initialize/inject-objects.js.es6 | 49 ++ .../initialize/patch-components.js.es6} | 140 ++---- .../lib/initialize/register-files.js.es6 | 26 + .../wizard/lib/initialize/wizard.js.es6 | 49 ++ .../wizard/lib/utilities-lite.js.es6 | 71 --- assets/javascripts/wizard/models/field.js.es6 | 79 +++ assets/javascripts/wizard/models/site.js.es6 | 3 +- assets/javascripts/wizard/models/step.js.es6 | 114 +++++ .../models/{custom.js.es6 => wizard.js.es6} | 6 +- assets/javascripts/wizard/router.js.es6 | 17 + .../wizard/routes/application.js.es6 | 3 + .../wizard/routes/custom-steps.js.es6 | 5 - assets/javascripts/wizard/routes/index.js.es6 | 9 + .../{custom-step.js.es6 => step.js.es6} | 11 +- assets/javascripts/wizard/routes/steps.js.es6 | 7 + ...ustom-index.js.es6 => wizard-index.js.es6} | 26 +- .../routes/{custom.js.es6 => wizard.js.es6} | 59 ++- .../components/wizard-field-dropdown.hbs | 1 + .../wizard/templates/custom.index.hbs | 15 - assets/javascripts/wizard/templates/index.hbs | 1 + .../templates/{custom.step.hbs => step.hbs} | 0 .../wizard/templates/wizard-index.hbs | 3 + .../templates/{custom.hbs => wizard.hbs} | 4 - .../wizard/tests/acceptance/field-test.js.es6 | 135 +++++ .../wizard/tests/acceptance/step-test.js.es6 | 47 ++ .../tests/acceptance/wizard-test.js.es6 | 72 +++ .../javascripts/wizard/tests/bootstrap.js.es6 | 17 + .../wizard/tests/fixtures/categories.js.es6 | 209 ++++++++ .../wizard/tests/fixtures/groups.js.es6 | 313 ++++++++++++ .../tests/fixtures/site-settings.js.es6 | 283 +++++++++++ .../wizard/tests/fixtures/tags.js.es6 | 22 + .../wizard/tests/fixtures/update.js.es6 | 5 + .../wizard/tests/fixtures/user.js.es6 | 34 ++ .../wizard/tests/fixtures/users.js.es6 | 14 + .../wizard/tests/fixtures/wizard.js.es6 | 469 ++++++++++++++++++ .../wizard/tests/helpers/acceptance.js.es6 | 53 ++ .../wizard/tests/helpers/start-app.js.es6 | 19 + .../wizard/tests/helpers/step.js.es6 | 20 + .../wizard/tests/helpers/test.js.es6 | 7 + .../wizard/tests/helpers/wizard.js.es6 | 52 ++ .../javascripts/wizard/tests/pretender.js.es6 | 28 +- .../stylesheets/wizard/custom/composer.scss | 2 +- config/routes.rb | 1 + controllers/custom_wizard/wizard.rb | 112 +++-- extensions/extra_locales_controller.rb | 1 + lib/custom_wizard/validators/update.rb | 2 +- plugin.rb | 3 +- .../javascripts/acceptance/wizard-test.js.es6 | 30 -- test/javascripts/fixtures/wizard.js | 340 ------------- test/javascripts/helpers/start-app.js | 23 - test/javascripts/plugin_helper.js | 70 --- views/layouts/qunit.html.erb | 28 ++ views/layouts/wizard.html.erb | 6 +- 98 files changed, 2834 insertions(+), 1203 deletions(-) delete mode 100644 assets/javascripts/wizard-custom-globals.js create mode 100644 assets/javascripts/wizard-qunit.js create mode 100644 assets/javascripts/wizard/application.js.es6 create mode 100644 assets/javascripts/wizard/components/wizard-field-checkbox.js.es6 create mode 100644 assets/javascripts/wizard/components/wizard-field-dropdown.js.es6 create mode 100644 assets/javascripts/wizard/components/wizard-field-group.js.es6 create mode 100644 assets/javascripts/wizard/components/wizard-field-number.js.es6 create mode 100644 assets/javascripts/wizard/components/wizard-field-tag.js.es6 create mode 100644 assets/javascripts/wizard/components/wizard-field-text.js.es6 create mode 100644 assets/javascripts/wizard/components/wizard-field-textarea.js.es6 create mode 100644 assets/javascripts/wizard/components/wizard-field-url.js.es6 create mode 100644 assets/javascripts/wizard/components/wizard-field-user-selector.js.es6 create mode 100644 assets/javascripts/wizard/components/wizard-field.js.es6 create mode 100644 assets/javascripts/wizard/components/wizard-step-form.js.es6 create mode 100644 assets/javascripts/wizard/components/wizard-step.js.es6 delete mode 100644 assets/javascripts/wizard/controllers/custom.js.es6 rename assets/javascripts/wizard/controllers/{custom-step.js.es6 => step.js.es6} (76%) create mode 100644 assets/javascripts/wizard/controllers/wizard-index.js.es6 create mode 100644 assets/javascripts/wizard/controllers/wizard.js.es6 delete mode 100644 assets/javascripts/wizard/custom-wizard.js.es6 delete mode 100644 assets/javascripts/wizard/initializers/custom-wizard-step.js.es6 delete mode 100644 assets/javascripts/wizard/initializers/custom-wizard.js.es6 create mode 100644 assets/javascripts/wizard/lib/initialize/create-contexts.js.es6 create mode 100644 assets/javascripts/wizard/lib/initialize/inject-objects.js.es6 rename assets/javascripts/wizard/{initializers/custom-wizard-field.js.es6 => lib/initialize/patch-components.js.es6} (52%) create mode 100644 assets/javascripts/wizard/lib/initialize/register-files.js.es6 create mode 100644 assets/javascripts/wizard/lib/initialize/wizard.js.es6 delete mode 100644 assets/javascripts/wizard/lib/utilities-lite.js.es6 create mode 100644 assets/javascripts/wizard/models/field.js.es6 create mode 100644 assets/javascripts/wizard/models/step.js.es6 rename assets/javascripts/wizard/models/{custom.js.es6 => wizard.js.es6} (96%) create mode 100644 assets/javascripts/wizard/router.js.es6 create mode 100644 assets/javascripts/wizard/routes/application.js.es6 delete mode 100644 assets/javascripts/wizard/routes/custom-steps.js.es6 create mode 100644 assets/javascripts/wizard/routes/index.js.es6 rename assets/javascripts/wizard/routes/{custom-step.js.es6 => step.js.es6} (78%) create mode 100644 assets/javascripts/wizard/routes/steps.js.es6 rename assets/javascripts/wizard/routes/{custom-index.js.es6 => wizard-index.js.es6} (50%) rename assets/javascripts/wizard/routes/{custom.js.es6 => wizard.js.es6} (59%) delete mode 100644 assets/javascripts/wizard/templates/custom.index.hbs create mode 100644 assets/javascripts/wizard/templates/index.hbs rename assets/javascripts/wizard/templates/{custom.step.hbs => step.hbs} (100%) create mode 100644 assets/javascripts/wizard/templates/wizard-index.hbs rename assets/javascripts/wizard/templates/{custom.hbs => wizard.hbs} (86%) create mode 100644 assets/javascripts/wizard/tests/acceptance/field-test.js.es6 create mode 100644 assets/javascripts/wizard/tests/acceptance/step-test.js.es6 create mode 100644 assets/javascripts/wizard/tests/acceptance/wizard-test.js.es6 create mode 100644 assets/javascripts/wizard/tests/bootstrap.js.es6 create mode 100644 assets/javascripts/wizard/tests/fixtures/categories.js.es6 create mode 100644 assets/javascripts/wizard/tests/fixtures/groups.js.es6 create mode 100644 assets/javascripts/wizard/tests/fixtures/site-settings.js.es6 create mode 100644 assets/javascripts/wizard/tests/fixtures/tags.js.es6 create mode 100644 assets/javascripts/wizard/tests/fixtures/update.js.es6 create mode 100644 assets/javascripts/wizard/tests/fixtures/user.js.es6 create mode 100644 assets/javascripts/wizard/tests/fixtures/users.js.es6 create mode 100644 assets/javascripts/wizard/tests/fixtures/wizard.js.es6 create mode 100644 assets/javascripts/wizard/tests/helpers/acceptance.js.es6 create mode 100644 assets/javascripts/wizard/tests/helpers/start-app.js.es6 create mode 100644 assets/javascripts/wizard/tests/helpers/step.js.es6 create mode 100644 assets/javascripts/wizard/tests/helpers/test.js.es6 create mode 100644 assets/javascripts/wizard/tests/helpers/wizard.js.es6 rename views/wizard-pretender.js => assets/javascripts/wizard/tests/pretender.js.es6 (56%) delete mode 100644 test/javascripts/acceptance/wizard-test.js.es6 delete mode 100644 test/javascripts/fixtures/wizard.js delete mode 100644 test/javascripts/helpers/start-app.js delete mode 100644 test/javascripts/plugin_helper.js create mode 100644 views/layouts/qunit.html.erb diff --git a/assets/javascripts/wizard-custom-globals.js b/assets/javascripts/wizard-custom-globals.js deleted file mode 100644 index 83923cba..00000000 --- a/assets/javascripts/wizard-custom-globals.js +++ /dev/null @@ -1,6 +0,0 @@ -/* eslint no-undef: 0*/ -window.Discourse = {}; -window.Wizard = {}; -Wizard.SiteSettings = {}; -Discourse.__widget_helpers = {}; -Discourse.SiteSettings = Wizard.SiteSettings; diff --git a/assets/javascripts/wizard-custom-start.js b/assets/javascripts/wizard-custom-start.js index 3ffa9c5a..b92d2871 100644 --- a/assets/javascripts/wizard-custom-start.js +++ b/assets/javascripts/wizard-custom-start.js @@ -1,4 +1,4 @@ (function () { - let wizard = require("discourse/plugins/discourse-custom-wizard/wizard/custom-wizard").default.create(); + let wizard = require("discourse/plugins/discourse-custom-wizard/wizard/application").default.create(); wizard.start(); })(); diff --git a/assets/javascripts/wizard-custom.js b/assets/javascripts/wizard-custom.js index 826cdf2d..2a5eb97a 100644 --- a/assets/javascripts/wizard-custom.js +++ b/assets/javascripts/wizard-custom.js @@ -1,10 +1,13 @@ +//= require_tree_discourse truth-helpers/addon +//= require_tree_discourse discourse-common/addon +//= require_tree_discourse select-kit/addon +//= require_tree_discourse wizard/lib +//= require_tree_discourse wizard/mixins //= require_tree_discourse discourse/app/lib //= require_tree_discourse discourse/app/mixins //= require discourse/app/adapters/rest - //= require message-bus - //= require_tree_discourse discourse/app/models //= require discourse/app/helpers/category-link @@ -12,9 +15,6 @@ //= require discourse/app/helpers/format-username //= require discourse/app/helpers/share-url //= require discourse/app/helpers/decorate-username-selector -//= require discourse-common/addon/helpers/component-for-collection -//= require discourse-common/addon/helpers/component-for-row -//= require discourse-common/addon/lib/raw-templates //= require discourse/app/helpers/discourse-tag //= require discourse/app/services/app-events @@ -59,7 +59,6 @@ //= require polyfills //= require markdown-it-bundle -//= require lodash.js //= require template_include.js //= require itsatrap.js //= require caret_position.js @@ -69,11 +68,11 @@ //= require bootbox.js //= require discourse-shims -//= require ./wizard/custom-wizard +//= require ./wizard/application +//= require ./wizard/router //= require_tree ./wizard/components //= require_tree ./wizard/controllers //= require_tree ./wizard/helpers -//= require_tree ./wizard/initializers //= require_tree ./wizard/lib //= require_tree ./wizard/models //= require_tree ./wizard/routes diff --git a/assets/javascripts/wizard-qunit.js b/assets/javascripts/wizard-qunit.js new file mode 100644 index 00000000..2866cb5d --- /dev/null +++ b/assets/javascripts/wizard-qunit.js @@ -0,0 +1,16 @@ +//= require route-recognizer +//= require fake_xml_http_request +//= require pretender +//= require qunit +//= require ember-qunit +//= require test-shims +//= require jquery.debug +//= require ember.debug +//= require ember-template-compiler + +//= require_tree ./wizard/tests/fixtures +//= require ./wizard/tests/pretender +//= require_tree ./wizard/tests/helpers +//= require_tree ./wizard/tests/acceptance + +//= require ./wizard/tests/bootstrap diff --git a/assets/javascripts/wizard/application.js.es6 b/assets/javascripts/wizard/application.js.es6 new file mode 100644 index 00000000..012949b3 --- /dev/null +++ b/assets/javascripts/wizard/application.js.es6 @@ -0,0 +1,19 @@ +import { buildResolver } from "discourse-common/resolver"; +import Application from "@ember/application"; +import WizardInitializer from "./lib/initialize/wizard"; +import { isTesting } from "discourse-common/config/environment"; + +export default Application.extend({ + rootElement: "#custom-wizard-main", + Resolver: buildResolver("discourse/plugins/discourse-custom-wizard/wizard"), + + customEvents: { + paste: "paste", + }, + + start() { + if (!isTesting()) { + this.initializer(WizardInitializer); + } + }, +}); diff --git a/assets/javascripts/wizard/components/custom-user-selector.js.es6 b/assets/javascripts/wizard/components/custom-user-selector.js.es6 index b2f08ede..1698a008 100644 --- a/assets/javascripts/wizard/components/custom-user-selector.js.es6 +++ b/assets/javascripts/wizard/components/custom-user-selector.js.es6 @@ -1,6 +1,6 @@ import { default as computed, - observes, + observes } from "discourse-common/utils/decorators"; import { renderAvatar } from "discourse/helpers/user-avatar"; import userSearch from "../lib/user-search"; @@ -55,7 +55,6 @@ export default Ember.TextField.extend({ let self = this, selected = [], groups = [], - currentUser = this.currentUser, includeMentionableGroups = this.get("includeMentionableGroups") === "true", includeMessageableGroups = @@ -66,13 +65,8 @@ export default Ember.TextField.extend({ function excludedUsernames() { // hack works around some issues with allowAny eventing const usernames = self.get("single") ? [] : selected; - - if (currentUser && self.get("excludeCurrentUser")) { - return usernames.concat([currentUser.get("username")]); - } return usernames; } - $(this.element) .val(this.get("usernames")) .autocomplete({ @@ -84,7 +78,6 @@ export default Ember.TextField.extend({ dataSource(term) { const termRegex = /[^a-zA-Z0-9_\-\.@\+]/; - let results = userSearch({ term: term.replace(termRegex, ""), topicId: self.get("topicId"), diff --git a/assets/javascripts/wizard/components/field-validators.js.es6 b/assets/javascripts/wizard/components/field-validators.js.es6 index a315020d..15cfc181 100644 --- a/assets/javascripts/wizard/components/field-validators.js.es6 +++ b/assets/javascripts/wizard/components/field-validators.js.es6 @@ -1,5 +1,8 @@ import Component from "@ember/component"; + export default Component.extend({ + layoutName: 'wizard/templates/components/field-validators', + actions: { perform() { this.appEvents.trigger("custom-wizard:validate"); diff --git a/assets/javascripts/wizard/components/similar-topics-validator.js.es6 b/assets/javascripts/wizard/components/similar-topics-validator.js.es6 index 98ea9270..e5133d4f 100644 --- a/assets/javascripts/wizard/components/similar-topics-validator.js.es6 +++ b/assets/javascripts/wizard/components/similar-topics-validator.js.es6 @@ -10,6 +10,7 @@ import { dasherize } from "@ember/string"; export default WizardFieldValidator.extend({ classNames: ["similar-topics-validator"], + layoutName: 'wizard/templates/components/similar-topics-validator', similarTopics: null, hasInput: notEmpty("field.value"), hasSimilarTopics: notEmpty("similarTopics"), diff --git a/assets/javascripts/wizard/components/validator.js.es6 b/assets/javascripts/wizard/components/validator.js.es6 index 6227cc64..ab442d97 100644 --- a/assets/javascripts/wizard/components/validator.js.es6 +++ b/assets/javascripts/wizard/components/validator.js.es6 @@ -6,11 +6,12 @@ import { getToken } from "wizard/lib/ajax"; export default Component.extend({ classNames: ["validator"], classNameBindings: ["isValid", "isInvalid"], + layoutName: 'wizard/templates/components/validator', validMessageKey: null, invalidMessageKey: null, isValid: null, isInvalid: equal("isValid", false), - layoutName: "components/validator", // useful for sharing the template with extending components + layoutName: "wizard/templates/components/validator", init() { this._super(...arguments); diff --git a/assets/javascripts/wizard/components/wizard-composer-editor.js.es6 b/assets/javascripts/wizard/components/wizard-composer-editor.js.es6 index 5e1d1aac..0bd003f5 100644 --- a/assets/javascripts/wizard/components/wizard-composer-editor.js.es6 +++ b/assets/javascripts/wizard/components/wizard-composer-editor.js.es6 @@ -13,6 +13,7 @@ import { uploadIcon } from "discourse/lib/uploads"; import { dasherize } from "@ember/string"; export default ComposerEditor.extend({ + layoutName: 'wizard/templates/components/wizard-composer-editor', classNameBindings: ["fieldClass"], allowUpload: true, showLink: false, diff --git a/assets/javascripts/wizard/components/wizard-composer-hyperlink.js.es6 b/assets/javascripts/wizard/components/wizard-composer-hyperlink.js.es6 index a56b7aff..c700d3ce 100644 --- a/assets/javascripts/wizard/components/wizard-composer-hyperlink.js.es6 +++ b/assets/javascripts/wizard/components/wizard-composer-hyperlink.js.es6 @@ -2,6 +2,7 @@ import Component from "@ember/component"; export default Component.extend({ classNames: ["wizard-composer-hyperlink"], + layoutName: 'wizard/templates/components/wizard-composer-hyperlink', actions: { addLink() { diff --git a/assets/javascripts/wizard/components/wizard-date-input.js.es6 b/assets/javascripts/wizard/components/wizard-date-input.js.es6 index 9c8e4bff..375b7195 100644 --- a/assets/javascripts/wizard/components/wizard-date-input.js.es6 +++ b/assets/javascripts/wizard/components/wizard-date-input.js.es6 @@ -3,6 +3,7 @@ import discourseComputed from "discourse-common/utils/decorators"; export default DateInput.extend({ useNativePicker: false, + layoutName: 'wizard/templates/components/wizard-date-input', @discourseComputed() placeholder() { diff --git a/assets/javascripts/wizard/components/wizard-date-time-input.js.es6 b/assets/javascripts/wizard/components/wizard-date-time-input.js.es6 index 44b675b0..0fe1e68a 100644 --- a/assets/javascripts/wizard/components/wizard-date-time-input.js.es6 +++ b/assets/javascripts/wizard/components/wizard-date-time-input.js.es6 @@ -2,6 +2,8 @@ import DateTimeInput from "discourse/components/date-time-input"; import discourseComputed from "discourse-common/utils/decorators"; export default DateTimeInput.extend({ + layoutName: 'wizard/templates/components/wizard-date-time-input', + @discourseComputed("timeFirst", "tabindex") timeTabindex(timeFirst, tabindex) { return timeFirst ? tabindex : tabindex + 1; diff --git a/assets/javascripts/wizard/components/wizard-field-category.js.es6 b/assets/javascripts/wizard/components/wizard-field-category.js.es6 index a7452214..dc20538e 100644 --- a/assets/javascripts/wizard/components/wizard-field-category.js.es6 +++ b/assets/javascripts/wizard/components/wizard-field-category.js.es6 @@ -2,6 +2,8 @@ import { observes } from "discourse-common/utils/decorators"; import Category from "discourse/models/category"; export default Ember.Component.extend({ + layoutName: 'wizard/templates/components/wizard-field-category', + didInsertElement() { const property = this.field.property || "id"; const value = this.field.value; diff --git a/assets/javascripts/wizard/components/wizard-field-checkbox.js.es6 b/assets/javascripts/wizard/components/wizard-field-checkbox.js.es6 new file mode 100644 index 00000000..f9653cd2 --- /dev/null +++ b/assets/javascripts/wizard/components/wizard-field-checkbox.js.es6 @@ -0,0 +1,5 @@ +import Component from "@ember/component"; + +export default Component.extend({ + layoutName: 'wizard/templates/components/wizard-field-checkbox' +}); diff --git a/assets/javascripts/wizard/components/wizard-field-composer-preview.js.es6 b/assets/javascripts/wizard/components/wizard-field-composer-preview.js.es6 index b49233f2..0aee0d13 100644 --- a/assets/javascripts/wizard/components/wizard-field-composer-preview.js.es6 +++ b/assets/javascripts/wizard/components/wizard-field-composer-preview.js.es6 @@ -7,6 +7,8 @@ import { ajax } from "discourse/lib/ajax"; import { on } from "discourse-common/utils/decorators"; export default Component.extend({ + layoutName: 'wizard/templates/components/wizard-field-composer-preview', + @on("init") updatePreview() { if (this.isDestroyed) { diff --git a/assets/javascripts/wizard/components/wizard-field-composer.js.es6 b/assets/javascripts/wizard/components/wizard-field-composer.js.es6 index 8b9ecb82..0ef5fe20 100644 --- a/assets/javascripts/wizard/components/wizard-field-composer.js.es6 +++ b/assets/javascripts/wizard/components/wizard-field-composer.js.es6 @@ -5,6 +5,8 @@ import { import EmberObject from "@ember/object"; export default Ember.Component.extend({ + layoutName: 'wizard/templates/components/wizard-field-composer', + showPreview: false, classNameBindings: [ ":wizard-field-composer", diff --git a/assets/javascripts/wizard/components/wizard-field-date-time.js.es6 b/assets/javascripts/wizard/components/wizard-field-date-time.js.es6 index 2d918636..a916f18e 100644 --- a/assets/javascripts/wizard/components/wizard-field-date-time.js.es6 +++ b/assets/javascripts/wizard/components/wizard-field-date-time.js.es6 @@ -2,6 +2,8 @@ import Component from "@ember/component"; import { observes } from "discourse-common/utils/decorators"; export default Component.extend({ + layoutName: 'wizard/templates/components/wizard-field-date-time', + @observes("dateTime") setValue() { this.set("field.value", this.dateTime.format(this.field.format)); diff --git a/assets/javascripts/wizard/components/wizard-field-date.js.es6 b/assets/javascripts/wizard/components/wizard-field-date.js.es6 index d5d0a830..a06d582a 100644 --- a/assets/javascripts/wizard/components/wizard-field-date.js.es6 +++ b/assets/javascripts/wizard/components/wizard-field-date.js.es6 @@ -2,6 +2,8 @@ import Component from "@ember/component"; import { observes } from "discourse-common/utils/decorators"; export default Component.extend({ + layoutName: 'wizard/templates/components/wizard-field-date', + @observes("date") setValue() { this.set("field.value", this.date.format(this.field.format)); diff --git a/assets/javascripts/wizard/components/wizard-field-dropdown.js.es6 b/assets/javascripts/wizard/components/wizard-field-dropdown.js.es6 new file mode 100644 index 00000000..4b8b7e63 --- /dev/null +++ b/assets/javascripts/wizard/components/wizard-field-dropdown.js.es6 @@ -0,0 +1,15 @@ +import Component from "@ember/component"; + +export default Component.extend({ + layoutName: 'wizard/templates/components/wizard-field-dropdown', + + keyPress(e) { + e.stopPropagation(); + }, + + actions: { + onChangeValue(value) { + this.set("field.value", value); + }, + }, +}); diff --git a/assets/javascripts/wizard/components/wizard-field-group.js.es6 b/assets/javascripts/wizard/components/wizard-field-group.js.es6 new file mode 100644 index 00000000..93538071 --- /dev/null +++ b/assets/javascripts/wizard/components/wizard-field-group.js.es6 @@ -0,0 +1,5 @@ +import Component from "@ember/component"; + +export default Component.extend({ + layoutName: 'wizard/templates/components/wizard-field-group' +}); diff --git a/assets/javascripts/wizard/components/wizard-field-number.js.es6 b/assets/javascripts/wizard/components/wizard-field-number.js.es6 new file mode 100644 index 00000000..e7c4d77f --- /dev/null +++ b/assets/javascripts/wizard/components/wizard-field-number.js.es6 @@ -0,0 +1,5 @@ +import Component from "@ember/component"; + +export default Component.extend({ + layoutName: 'wizard/templates/components/wizard-field-number' +}); diff --git a/assets/javascripts/wizard/components/wizard-field-tag.js.es6 b/assets/javascripts/wizard/components/wizard-field-tag.js.es6 new file mode 100644 index 00000000..45343522 --- /dev/null +++ b/assets/javascripts/wizard/components/wizard-field-tag.js.es6 @@ -0,0 +1,5 @@ +import Component from "@ember/component"; + +export default Component.extend({ + layoutName: 'wizard/templates/components/wizard-field-tag' +}); diff --git a/assets/javascripts/wizard/components/wizard-field-text.js.es6 b/assets/javascripts/wizard/components/wizard-field-text.js.es6 new file mode 100644 index 00000000..f9d5b056 --- /dev/null +++ b/assets/javascripts/wizard/components/wizard-field-text.js.es6 @@ -0,0 +1,9 @@ +import Component from "@ember/component"; + +export default Component.extend({ + layoutName: 'wizard/templates/components/wizard-field-text', + + keyPress(e) { + e.stopPropagation(); + }, +}); diff --git a/assets/javascripts/wizard/components/wizard-field-textarea.js.es6 b/assets/javascripts/wizard/components/wizard-field-textarea.js.es6 new file mode 100644 index 00000000..54865d3c --- /dev/null +++ b/assets/javascripts/wizard/components/wizard-field-textarea.js.es6 @@ -0,0 +1,9 @@ +import Component from "@ember/component"; + +export default Component.extend({ + layoutName: 'wizard/templates/components/wizard-field-textarea', + + keyPress(e) { + e.stopPropagation(); + }, +}); diff --git a/assets/javascripts/wizard/components/wizard-field-time.js.es6 b/assets/javascripts/wizard/components/wizard-field-time.js.es6 index 82f9c68b..bf954ec4 100644 --- a/assets/javascripts/wizard/components/wizard-field-time.js.es6 +++ b/assets/javascripts/wizard/components/wizard-field-time.js.es6 @@ -2,6 +2,8 @@ import Component from "@ember/component"; import { observes } from "discourse-common/utils/decorators"; export default Component.extend({ + layoutName: 'wizard/templates/components/wizard-field-time', + @observes("time") setValue() { this.set("field.value", this.time.format(this.field.format)); diff --git a/assets/javascripts/wizard/components/wizard-field-upload.js.es6 b/assets/javascripts/wizard/components/wizard-field-upload.js.es6 index 876cdf0e..edadb4f0 100644 --- a/assets/javascripts/wizard/components/wizard-field-upload.js.es6 +++ b/assets/javascripts/wizard/components/wizard-field-upload.js.es6 @@ -3,12 +3,16 @@ import Component from "@ember/component"; import { computed } from "@ember/object"; export default Component.extend(UppyUploadMixin, { + layoutName: 'wizard/templates/components/wizard-field-upload', classNames: ["wizard-field-upload"], classNameBindings: ["isImage"], uploading: false, type: computed(function () { return `wizard_${this.field.id}`; }), + id: computed(function () { + return `wizard_field_upload_${this.field.id}`; + }), isImage: computed("field.value.extension", function () { return ( this.field.value && diff --git a/assets/javascripts/wizard/components/wizard-field-url.js.es6 b/assets/javascripts/wizard/components/wizard-field-url.js.es6 new file mode 100644 index 00000000..411b5fe9 --- /dev/null +++ b/assets/javascripts/wizard/components/wizard-field-url.js.es6 @@ -0,0 +1,5 @@ +import Component from "@ember/component"; + +export default Component.extend({ + layoutName: 'wizard/templates/components/wizard-field-url' +}); diff --git a/assets/javascripts/wizard/components/wizard-field-user-selector.js.es6 b/assets/javascripts/wizard/components/wizard-field-user-selector.js.es6 new file mode 100644 index 00000000..c2a32f44 --- /dev/null +++ b/assets/javascripts/wizard/components/wizard-field-user-selector.js.es6 @@ -0,0 +1,5 @@ +import Component from "@ember/component"; + +export default Component.extend({ + layoutName: 'wizard/templates/components/wizard-field-user-selector' +}); diff --git a/assets/javascripts/wizard/components/wizard-field.js.es6 b/assets/javascripts/wizard/components/wizard-field.js.es6 new file mode 100644 index 00000000..372ee182 --- /dev/null +++ b/assets/javascripts/wizard/components/wizard-field.js.es6 @@ -0,0 +1,34 @@ +import Component from "@ember/component"; +import { dasherize } from "@ember/string"; +import discourseComputed from "discourse-common/utils/decorators"; +import { cook } from "discourse/plugins/discourse-custom-wizard/wizard/lib/text-lite"; + +export default Component.extend({ + layoutName: 'wizard/templates/components/wizard-field', + classNameBindings: [":wizard-field", "typeClasses", "field.invalid", "field.id"], + + @discourseComputed("field.type", "field.id") + typeClasses: (type, id) => + `${dasherize(type)}-field ${dasherize(type)}-${dasherize(id)}`, + + @discourseComputed("field.id") + fieldClass: (id) => `field-${dasherize(id)} wizard-focusable`, + + @discourseComputed("field.type", "field.id") + inputComponentName(type, id) { + if (["text_only"].includes(type)) { + return false; + } + return dasherize(type === "component" ? id : `wizard-field-${type}`); + }, + + @discourseComputed("field.translatedDescription") + cookedDescription(description) { + return cook(description); + }, + + @discourseComputed("field.type") + textType(fieldType) { + return ["text", "textarea"].includes(fieldType); + }, +}); diff --git a/assets/javascripts/wizard/components/wizard-group-selector.js.es6 b/assets/javascripts/wizard/components/wizard-group-selector.js.es6 index cb613107..b7523f9a 100644 --- a/assets/javascripts/wizard/components/wizard-group-selector.js.es6 +++ b/assets/javascripts/wizard/components/wizard-group-selector.js.es6 @@ -3,6 +3,7 @@ import { computed } from "@ember/object"; import { makeArray } from "discourse-common/lib/helpers"; export default ComboBox.extend({ + layoutName: 'wizard/templates/components/wizard-group-selector', content: computed("groups.[]", "field.content.[]", function () { const whitelist = makeArray(this.field.content); return this.groups diff --git a/assets/javascripts/wizard/components/wizard-no-access.js.es6 b/assets/javascripts/wizard/components/wizard-no-access.js.es6 index 57fe9111..78a23aa8 100644 --- a/assets/javascripts/wizard/components/wizard-no-access.js.es6 +++ b/assets/javascripts/wizard/components/wizard-no-access.js.es6 @@ -1,10 +1,21 @@ -import CustomWizard from "../models/custom"; +import CustomWizard from "../models/wizard"; +import discourseComputed from "discourse-common/utils/decorators"; +import Component from "@ember/component"; +import { dasherize } from "@ember/string"; -export default Ember.Component.extend({ - siteName: function () { - /*eslint no-undef:0*/ - return Wizard.SiteSettings.title; - }.property(), +export default Component.extend({ + classNameBindings: [':wizard-no-access', 'reasonClass'], + layoutName: 'wizard/templates/components/wizard-no-access', + + @discourseComputed('reason') + reasonClass(reason) { + return dasherize(reason); + }, + + @discourseComputed + siteName() { + return (this.siteSettings.title || ''); + }, actions: { skip() { diff --git a/assets/javascripts/wizard/components/wizard-similar-topics.js.es6 b/assets/javascripts/wizard/components/wizard-similar-topics.js.es6 index 687cfa86..c52bfeae 100644 --- a/assets/javascripts/wizard/components/wizard-similar-topics.js.es6 +++ b/assets/javascripts/wizard/components/wizard-similar-topics.js.es6 @@ -4,6 +4,7 @@ import { observes } from "discourse-common/utils/decorators"; export default Component.extend({ classNames: ["wizard-similar-topics"], + layoutName: 'wizard/templates/components/wizard-similar-topics', showTopics: true, didInsertElement() { diff --git a/assets/javascripts/wizard/components/wizard-step-form.js.es6 b/assets/javascripts/wizard/components/wizard-step-form.js.es6 new file mode 100644 index 00000000..73406b4f --- /dev/null +++ b/assets/javascripts/wizard/components/wizard-step-form.js.es6 @@ -0,0 +1,9 @@ +import Component from "@ember/component"; +import discourseComputed from "discourse-common/utils/decorators"; + +export default Component.extend({ + classNameBindings: [":wizard-step-form", "customStepClass"], + + @discourseComputed("step.id") + customStepClass: (stepId) => `wizard-step-${stepId}`, +}); diff --git a/assets/javascripts/wizard/components/wizard-step.js.es6 b/assets/javascripts/wizard/components/wizard-step.js.es6 new file mode 100644 index 00000000..4518afee --- /dev/null +++ b/assets/javascripts/wizard/components/wizard-step.js.es6 @@ -0,0 +1,247 @@ +import discourseComputed, { observes } from "discourse-common/utils/decorators"; +import Component from "@ember/component"; +import I18n from "I18n"; +import getUrl from "discourse-common/lib/get-url"; +import { htmlSafe } from "@ember/template"; +import { schedule } from "@ember/runloop"; +import { cook } from "discourse/plugins/discourse-custom-wizard/wizard/lib/text-lite"; +import { updateCachedWizard } from "discourse/plugins/discourse-custom-wizard/wizard/models/wizard"; +import { alias, not } from "@ember/object/computed"; +import CustomWizard from "../models/wizard"; + +const alreadyWarned = {}; + +export default Component.extend({ + layoutName: 'wizard/templates/components/wizard-step', + classNameBindings: [":wizard-step", "step.id"], + saving: null, + + init() { + this._super(...arguments); + this.set("stylingDropdown", {}); + }, + + didInsertElement() { + this._super(...arguments); + this.autoFocus(); + }, + + @discourseComputed("step.index", "wizard.required") + showQuitButton: (index, required) => (index === 0 && !required), + + showNextButton: not("step.final"), + showDoneButton: alias("step.final"), + + @discourseComputed("step.translatedTitle") + cookedTitle(title) { + return cook(title); + }, + + @discourseComputed("step.translatedDescription") + cookedDescription(description) { + return cook(description); + }, + + @discourseComputed( + "step.index", + "step.displayIndex", + "wizard.totalSteps", + "wizard.completed" + ) + showFinishButton: (index, displayIndex, total, completed) => { + return index !== 0 && displayIndex !== total && completed; + }, + + @discourseComputed("step.index") + showBackButton: (index) => index > 0, + + @discourseComputed("step.banner") + bannerImage(src) { + if (!src) { + return; + } + return getUrl(src); + }, + + @discourseComputed("step.id") + bannerAndDescriptionClass(id) { + return `wizard-banner-and-description wizard-banner-and-description-${id}`; + }, + + @discourseComputed("step.fields.[]") + primaryButtonIndex(fields) { + return fields.length + 1; + }, + + @discourseComputed("step.fields.[]") + secondaryButtonIndex(fields) { + return fields.length + 2; + }, + + @observes("step.id") + _stepChanged() { + this.set("saving", false); + this.autoFocus(); + }, + + @observes("step.message") + _handleMessage: function () { + const message = this.get("step.message"); + this.sendAction("showMessage", message); + }, + + keyPress(event) { + if (event.key === "Enter") { + if (this.showDoneButton) { + this.send("quit"); + } else { + this.send("nextStep"); + } + } + }, + + @discourseComputed("step.index", "wizard.totalSteps") + barStyle(displayIndex, totalSteps) { + let ratio = parseFloat(displayIndex) / parseFloat(totalSteps - 1); + if (ratio < 0) { + ratio = 0; + } + if (ratio > 1) { + ratio = 1; + } + + return htmlSafe(`width: ${ratio * 200}px`); + }, + + @discourseComputed("step.fields") + includeSidebar(fields) { + return !!fields.findBy("show_in_sidebar"); + }, + + autoFocus() { + schedule("afterRender", () => { + const $invalid = $( + ".wizard-field.invalid:nth-of-type(1) .wizard-focusable" + ); + + if ($invalid.length) { + return $invalid.focus(); + } + + $(".wizard-focusable:first").focus(); + }); + }, + + animateInvalidFields() { + schedule("afterRender", () => { + let $element = $( + ".invalid input[type=text],.invalid textarea,.invalid input[type=checkbox],.invalid .select-kit" + ); + + if ($element.length) { + $([document.documentElement, document.body]).animate( + { + scrollTop: $element.offset().top - 200, + }, + 400, + function () { + $element.wiggle(2, 100); + } + ); + } + }); + }, + + advance() { + this.set("saving", true); + this.get("step") + .save() + .then((response) => { + updateCachedWizard(CustomWizard.build(response["wizard"])); + + if (response["final"]) { + CustomWizard.finished(response); + } else { + this.sendAction("goNext", response); + } + }) + .catch(() => this.animateInvalidFields()) + .finally(() => this.set("saving", false)); + }, + + keyPress() {}, + + actions: { + quit() { + this.get("wizard").skip(); + }, + + done() { + this.send("nextStep"); + }, + + showMessage(message) { + this.sendAction("showMessage", message); + }, + + stylingDropdownChanged(id, value) { + this.set("stylingDropdown", { id, value }); + }, + + exitEarly() { + const step = this.step; + step.validate(); + + if (step.get("valid")) { + this.set("saving", true); + + step + .save() + .then(() => this.send("quit")) + .finally(() => this.set("saving", false)); + } else { + this.autoFocus(); + } + }, + + backStep() { + if (this.saving) { + return; + } + + this.goBack(); + }, + + nextStep() { + if (this.saving) { + return; + } + + const step = this.step; + const result = step.validate(); + + if (result.warnings.length) { + const unwarned = result.warnings.filter((w) => !alreadyWarned[w]); + if (unwarned.length) { + unwarned.forEach((w) => (alreadyWarned[w] = true)); + return window.bootbox.confirm( + unwarned.map((w) => I18n.t(`wizard.${w}`)).join("\n"), + I18n.t("no_value"), + I18n.t("yes_value"), + (confirmed) => { + if (confirmed) { + this.advance(); + } + } + ); + } + } + + if (step.get("valid")) { + this.advance(); + } else { + this.autoFocus(); + } + }, + }, +}); diff --git a/assets/javascripts/wizard/components/wizard-text-field.js.es6 b/assets/javascripts/wizard/components/wizard-text-field.js.es6 index c522eb2c..3d87be09 100644 --- a/assets/javascripts/wizard/components/wizard-text-field.js.es6 +++ b/assets/javascripts/wizard/components/wizard-text-field.js.es6 @@ -1,5 +1,3 @@ -/* eslint no-undef: 0*/ - import computed from "discourse-common/utils/decorators"; import { isLTR, isRTL, siteDir } from "discourse/lib/text-direction"; import WizardI18n from "../lib/wizard-i18n"; @@ -15,7 +13,7 @@ export default Ember.TextField.extend({ @computed dir() { - if (Wizard.SiteSettings.support_mixed_text_direction) { + if (this.siteSettings.support_mixed_text_direction) { let val = this.value; if (val) { return isRTL(val) ? "rtl" : "ltr"; @@ -26,7 +24,7 @@ export default Ember.TextField.extend({ }, keyUp() { - if (Wizard.SiteSettings.support_mixed_text_direction) { + if (this.siteSettings.support_mixed_text_direction) { let val = this.value; if (isRTL(val)) { this.set("dir", "rtl"); diff --git a/assets/javascripts/wizard/components/wizard-time-input.js.es6 b/assets/javascripts/wizard/components/wizard-time-input.js.es6 index 0bca244d..ec121002 100644 --- a/assets/javascripts/wizard/components/wizard-time-input.js.es6 +++ b/assets/javascripts/wizard/components/wizard-time-input.js.es6 @@ -1,3 +1,5 @@ import TimeInput from "discourse/components/time-input"; -export default TimeInput.extend(); +export default TimeInput.extend({ + layoutName: 'wizard/templates/components/wizard-time-input' +}); diff --git a/assets/javascripts/wizard/controllers/custom.js.es6 b/assets/javascripts/wizard/controllers/custom.js.es6 deleted file mode 100644 index 5f168f3a..00000000 --- a/assets/javascripts/wizard/controllers/custom.js.es6 +++ /dev/null @@ -1,3 +0,0 @@ -export default Ember.Controller.extend({ - queryParams: ["reset"], -}); diff --git a/assets/javascripts/wizard/controllers/custom-step.js.es6 b/assets/javascripts/wizard/controllers/step.js.es6 similarity index 76% rename from assets/javascripts/wizard/controllers/custom-step.js.es6 rename to assets/javascripts/wizard/controllers/step.js.es6 index b44c0fca..4b321173 100644 --- a/assets/javascripts/wizard/controllers/custom-step.js.es6 +++ b/assets/javascripts/wizard/controllers/step.js.es6 @@ -1,7 +1,10 @@ -import StepController from "wizard/controllers/step"; +import Controller from "@ember/controller"; import getUrl from "discourse-common/lib/get-url"; -export default StepController.extend({ +export default Controller.extend({ + wizard: null, + step: null, + actions: { goNext(response) { let nextStepId = response["next_step_id"]; @@ -12,12 +15,12 @@ export default StepController.extend({ const wizardId = this.get("wizard.id"); window.location.href = getUrl(`/w/${wizardId}/steps/${nextStepId}`); } else { - this.transitionToRoute("custom.step", nextStepId); + this.transitionToRoute("step", nextStepId); } }, goBack() { - this.transitionToRoute("custom.step", this.get("step.previous")); + this.transitionToRoute("step", this.get("step.previous")); }, showMessage(message) { diff --git a/assets/javascripts/wizard/controllers/wizard-index.js.es6 b/assets/javascripts/wizard/controllers/wizard-index.js.es6 new file mode 100644 index 00000000..2dfdee40 --- /dev/null +++ b/assets/javascripts/wizard/controllers/wizard-index.js.es6 @@ -0,0 +1,24 @@ +import Controller from "@ember/controller"; +import { or } from "@ember/object/computed"; +import discourseComputed from "discourse-common/utils/decorators"; + +const reasons = { + noWizard: "none", + requiresLogin: "requires_login", + notPermitted: "not_permitted", + completed: "completed" +} + +export default Controller.extend({ + noAccess: or('noWizard', 'requiresLogin', 'notPermitted', 'completed'), + + @discourseComputed('noAccessReason') + noAccessI18nKey(reason) { + return reason ? `wizard.${reasons[reason]}` : 'wizard.none'; + }, + + @discourseComputed + noAccessReason() { + return Object.keys(reasons).find(reason => this.get(reason)); + } +}); diff --git a/assets/javascripts/wizard/controllers/wizard.js.es6 b/assets/javascripts/wizard/controllers/wizard.js.es6 new file mode 100644 index 00000000..35f964e2 --- /dev/null +++ b/assets/javascripts/wizard/controllers/wizard.js.es6 @@ -0,0 +1,5 @@ +import Controller from "@ember/controller"; + +export default Controller.extend({ + queryParams: ["reset"], +}); diff --git a/assets/javascripts/wizard/custom-wizard.js.es6 b/assets/javascripts/wizard/custom-wizard.js.es6 deleted file mode 100644 index 8c0a473c..00000000 --- a/assets/javascripts/wizard/custom-wizard.js.es6 +++ /dev/null @@ -1,39 +0,0 @@ -import { buildResolver } from "discourse-common/resolver"; - -export default Ember.Application.extend({ - rootElement: "#custom-wizard-main", - Resolver: buildResolver("wizard"), - - customEvents: { - paste: "paste", - }, - - start() { - Object.keys(requirejs._eak_seen).forEach((key) => { - if (/\/pre\-initializers\//.test(key)) { - const module = requirejs(key, null, null, true); - if (!module) { - throw new Error(key + " must export an initializer."); - } - - const init = module.default; - const oldInitialize = init.initialize; - init.initialize = () => { - oldInitialize.call(this, this.__container__, this); - }; - - this.initializer(init); - } - }); - - Object.keys(requirejs._eak_seen).forEach((key) => { - if (/\/initializers\//.test(key)) { - const module = requirejs(key, null, null, true); - if (!module) { - throw new Error(key + " must export an initializer."); - } - this.initializer(module.default); - } - }); - }, -}); diff --git a/assets/javascripts/wizard/initializers/custom-wizard-step.js.es6 b/assets/javascripts/wizard/initializers/custom-wizard-step.js.es6 deleted file mode 100644 index 6a679796..00000000 --- a/assets/javascripts/wizard/initializers/custom-wizard-step.js.es6 +++ /dev/null @@ -1,219 +0,0 @@ -export default { - name: "custom-wizard-step", - initialize() { - if (window.location.pathname.indexOf("/w/") < 0) { - return; - } - - const CustomWizard = requirejs( - "discourse/plugins/discourse-custom-wizard/wizard/models/custom" - ).default; - const updateCachedWizard = requirejs( - "discourse/plugins/discourse-custom-wizard/wizard/models/custom" - ).updateCachedWizard; - const StepModel = requirejs("wizard/models/step").default; - const StepComponent = requirejs("wizard/components/wizard-step").default; - const ajax = requirejs("wizard/lib/ajax").ajax; - const getUrl = requirejs("discourse-common/lib/get-url").default; - const discourseComputed = requirejs("discourse-common/utils/decorators") - .default; - const cook = requirejs( - "discourse/plugins/discourse-custom-wizard/wizard/lib/text-lite" - ).cook; - const { schedule } = requirejs("@ember/runloop"); - const { alias, not } = requirejs("@ember/object/computed"); - const { translatedText } = requirejs( - "discourse/plugins/discourse-custom-wizard/wizard/lib/wizard-i18n" - ); - - StepModel.reopen({ - @discourseComputed("wizardId", "id") - i18nKey(wizardId, stepId) { - return `${wizardId}.${stepId}`; - }, - - @discourseComputed("i18nKey", "title") - translatedTitle(i18nKey, title) { - return translatedText(`${i18nKey}.title`, title); - }, - - @discourseComputed("i18nKey", "description") - translatedDescription(i18nKey, description) { - return translatedText(`${i18nKey}.description`, description); - }, - - save() { - const wizardId = this.get("wizardId"); - const fields = {}; - - this.get("fields").forEach((f) => { - if (f.type !== "text_only") { - fields[f.id] = f.value; - } - }); - - return ajax({ - url: `/w/${wizardId}/steps/${this.get("id")}`, - type: "PUT", - data: { fields }, - }).catch((response) => { - if ( - response && - response.responseJSON && - response.responseJSON.errors - ) { - let wizardErrors = []; - response.responseJSON.errors.forEach((err) => { - if (err.field === wizardId) { - wizardErrors.push(err.description); - } else if (err.field) { - this.fieldError(err.field, err.description); - } else if (err) { - wizardErrors.push(err); - } - }); - if (wizardErrors.length) { - this.handleWizardError(wizardErrors.join("\n")); - } - this.animateInvalidFields(); - throw response; - } - - if (response && response.responseText) { - const responseText = response.responseText; - const start = responseText.indexOf(">") + 1; - const end = responseText.indexOf("plugins"); - const message = responseText.substring(start, end); - this.handleWizardError(message); - throw message; - } - }); - }, - - handleWizardError(message) { - this.set("message", { - state: "error", - text: message, - }); - Ember.run.later(() => this.set("message", null), 6000); - }, - }); - - StepComponent.reopen({ - classNameBindings: ["step.id"], - - autoFocus() { - schedule("afterRender", () => { - const $invalid = $( - ".wizard-field.invalid:nth-of-type(1) .wizard-focusable" - ); - - if ($invalid.length) { - return $invalid.focus(); - } - - $(".wizard-focusable:first").focus(); - }); - }, - - animateInvalidFields() { - schedule("afterRender", () => { - let $element = $( - ".invalid input[type=text],.invalid textarea,.invalid input[type=checkbox],.invalid .select-kit" - ); - - if ($element.length) { - $([document.documentElement, document.body]).animate( - { - scrollTop: $element.offset().top - 200, - }, - 400, - function () { - $element.wiggle(2, 100); - } - ); - } - }); - }, - - ensureStartsAtTop: function () { - window.scrollTo(0, 0); - }.observes("step.id"), - - showQuitButton: function () { - const index = this.get("step.index"); - const required = this.get("wizard.required"); - return index === 0 && !required; - }.property("step.index", "wizard.required"), - - @discourseComputed("step.translatedTitle") - cookedTitle(title) { - return cook(title); - }, - - @discourseComputed("step.translatedDescription") - cookedDescription(description) { - return cook(description); - }, - - bannerImage: function () { - const src = this.get("step.banner"); - if (!src) { - return; - } - return getUrl(src); - }.property("step.banner"), - - @discourseComputed("step.fields.[]") - primaryButtonIndex(fields) { - return fields.length + 1; - }, - - @discourseComputed("step.fields.[]") - secondaryButtonIndex(fields) { - return fields.length + 2; - }, - - handleMessage: function () { - const message = this.get("step.message"); - this.sendAction("showMessage", message); - }.observes("step.message"), - - showNextButton: not("step.final"), - showDoneButton: alias("step.final"), - - advance() { - this.set("saving", true); - this.get("step") - .save() - .then((response) => { - updateCachedWizard(CustomWizard.build(response["wizard"])); - - if (response["final"]) { - CustomWizard.finished(response); - } else { - this.sendAction("goNext", response); - } - }) - .catch(() => this.animateInvalidFields()) - .finally(() => this.set("saving", false)); - }, - - keyPress() {}, - - actions: { - quit() { - this.get("wizard").skip(); - }, - - done() { - this.send("nextStep"); - }, - - showMessage(message) { - this.sendAction("showMessage", message); - }, - }, - }); - }, -}; diff --git a/assets/javascripts/wizard/initializers/custom-wizard.js.es6 b/assets/javascripts/wizard/initializers/custom-wizard.js.es6 deleted file mode 100644 index f2095827..00000000 --- a/assets/javascripts/wizard/initializers/custom-wizard.js.es6 +++ /dev/null @@ -1,126 +0,0 @@ -export default { - name: "custom-routes", - initialize(app) { - if (window.location.pathname.indexOf("/w/") < 0) { - return; - } - - const EmberObject = requirejs("@ember/object").default; - const Router = requirejs("wizard/router").default; - const ApplicationRoute = requirejs("wizard/routes/application").default; - const getUrl = requirejs("discourse-common/lib/get-url").default; - const Store = requirejs("discourse/services/store").default; - const registerRawHelpers = requirejs( - "discourse-common/lib/raw-handlebars-helpers" - ).registerRawHelpers; - const createHelperContext = requirejs("discourse-common/lib/helpers") - .createHelperContext; - const RawHandlebars = requirejs("discourse-common/lib/raw-handlebars") - .default; - const Handlebars = requirejs("handlebars").default; - const Site = requirejs( - "discourse/plugins/discourse-custom-wizard/wizard/models/site" - ).default; - const RestAdapter = requirejs("discourse/adapters/rest").default; - const Session = requirejs("discourse/models/session").default; - const setDefaultOwner = requirejs("discourse-common/lib/get-owner") - .setDefaultOwner; - const messageBus = requirejs("message-bus-client").default; - const getToken = requirejs("wizard/lib/ajax").getToken; - const setEnvironment = requirejs("discourse-common/config/environment") - .setEnvironment; - const container = app.__container__; - Discourse.Model = EmberObject.extend(); - Discourse.__container__ = container; - setDefaultOwner(container); - registerRawHelpers(RawHandlebars, Handlebars); - - // IE11 Polyfill - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries#Polyfill - if (!Object.entries) { - Object.entries = function (obj) { - let ownProps = Object.keys(obj), - i = ownProps.length, - resArray = new Array(i); // preallocate the Array - while (i--) { - resArray[i] = [ownProps[i], obj[ownProps[i]]]; - } - - return resArray; - }; - } - - Object.keys(Ember.TEMPLATES).forEach((k) => { - if (k.indexOf("select-kit") === 0) { - let template = Ember.TEMPLATES[k]; - define(k, () => template); - } - }); - - const targets = ["controller", "component", "route", "model", "adapter"]; - /*eslint no-undef: 0*/ - const siteSettings = Wizard.SiteSettings; - app.register("site-settings:main", siteSettings, { instantiate: false }); - createHelperContext({ siteSettings }); - targets.forEach((t) => app.inject(t, "siteSettings", "site-settings:main")); - - app.register("message-bus:main", messageBus, { instantiate: false }); - targets.forEach((t) => app.inject(t, "messageBus", "message-bus:main")); - - app.register("service:store", Store); - targets.forEach((t) => app.inject(t, "store", "service:store")); - targets.forEach((t) => app.inject(t, "appEvents", "service:app-events")); - - app.register("adapter:rest", RestAdapter); - - const site = Site.current(); - app.register("site:main", site, { instantiate: false }); - targets.forEach((t) => app.inject(t, "site", "site:main")); - - site.set("can_create_tag", false); - app.register("session:main", Session.current(), { instantiate: false }); - targets.forEach((t) => app.inject(t, "session", "session:main")); - - createHelperContext({ - siteSettings: container.lookup("site-settings:main"), - currentUser: container.lookup("current-user:main"), - site: container.lookup("site:main"), - session: container.lookup("session:main"), - capabilities: container.lookup("capabilities:main"), - }); - - const session = container.lookup("session:main"); - const setupData = document.getElementById("data-discourse-setup").dataset; - session.set("highlightJsPath", setupData.highlightJsPath); - setEnvironment(setupData.environment); - - Router.reopen({ - rootURL: getUrl("/w/"), - }); - - Router.map(function () { - this.route("custom", { path: "/:wizard_id" }, function () { - this.route("steps"); - this.route("step", { path: "/steps/:step_id" }); - }); - }); - - ApplicationRoute.reopen({ - redirect() { - this.transitionTo("custom"); - }, - model() {}, - }); - - // Add a CSRF token to all AJAX requests - let token = getToken(); - session.set("csrfToken", token); - let callbacks = $.Callbacks(); - $.ajaxPrefilter(callbacks.fire); - - callbacks.add(function (options, originalOptions, xhr) { - if (!options.crossDomain) { - xhr.setRequestHeader("X-CSRF-Token", session.get("csrfToken")); - } - }); - }, -}; diff --git a/assets/javascripts/wizard/lib/initialize/create-contexts.js.es6 b/assets/javascripts/wizard/lib/initialize/create-contexts.js.es6 new file mode 100644 index 00000000..022ac48e --- /dev/null +++ b/assets/javascripts/wizard/lib/initialize/create-contexts.js.es6 @@ -0,0 +1,12 @@ +export default { + run(app, container) { + const { createHelperContext } = requirejs("discourse-common/lib/helpers"); + + createHelperContext({ + siteSettings: container.lookup('site-settings:main'), + site: container.lookup("site:main"), + session: container.lookup("session:main"), + capabilities: container.lookup("capabilities:main"), + }); + } +} diff --git a/assets/javascripts/wizard/lib/initialize/inject-objects.js.es6 b/assets/javascripts/wizard/lib/initialize/inject-objects.js.es6 new file mode 100644 index 00000000..d31efb4d --- /dev/null +++ b/assets/javascripts/wizard/lib/initialize/inject-objects.js.es6 @@ -0,0 +1,49 @@ +export default { + run(app, container) { + const Store = requirejs("discourse/services/store").default; + const Site = requirejs( + "discourse/plugins/discourse-custom-wizard/wizard/models/site" + ).default; + const Session = requirejs("discourse/models/session").default; + const RestAdapter = requirejs("discourse/adapters/rest").default; + const messageBus = requirejs("message-bus-client").default; + const sniffCapabilites = requirejs("discourse/pre-initializers/sniff-capabilities").default; + const site = Site.current(); + const session = Session.current(); + + const registrations = [ + ["site-settings:main", app.SiteSettings, false], + ["message-bus:main", messageBus, false], + ["site:main", site, false], + ["session:main", session, false], + ["service:store", Store, true], + ["adapter:rest", RestAdapter, true] + ]; + + registrations.forEach(registration => { + if (!app.hasRegistration(registration[0])) { + app.register(registration[0], registration[1], { instantiate: registration[2] }); + } + }); + + const targets = ["controller", "component", "route", "model", "adapter", "mixin"]; + const injections = [ + ["siteSettings", "site-settings:main"], + ["messageBus", "message-bus:main"], + ["site", "site:main"], + ["session", "session:main"], + ["store", "service:store"], + ["appEvents", "service:app-events"] + ]; + + injections.forEach(injection => { + targets.forEach((t) => app.inject(t, injection[0], injection[1])); + }); + + if (!app.hasRegistration("capabilities:main")) { + sniffCapabilites.initialize(null, app); + } + + site.set("can_create_tag", false); + } +} diff --git a/assets/javascripts/wizard/initializers/custom-wizard-field.js.es6 b/assets/javascripts/wizard/lib/initialize/patch-components.js.es6 similarity index 52% rename from assets/javascripts/wizard/initializers/custom-wizard-field.js.es6 rename to assets/javascripts/wizard/lib/initialize/patch-components.js.es6 index 6f2a80b7..65086e53 100644 --- a/assets/javascripts/wizard/initializers/custom-wizard-field.js.es6 +++ b/assets/javascripts/wizard/lib/initialize/patch-components.js.es6 @@ -1,117 +1,25 @@ -import { dasherize } from "@ember/string"; -import discourseComputed from "discourse-common/utils/decorators"; - export default { - name: "custom-wizard-field", - initialize() { - if (window.location.pathname.indexOf("/w/") < 0) { - return; + run(app, container) { + const getToken = requirejs("wizard/lib/ajax").getToken; + const isTesting = requirejs("discourse-common/config/environment").isTesting; + + if (!isTesting) { + // Add a CSRF token to all AJAX requests + let token = getToken(); + session.set("csrfToken", token); + let callbacks = $.Callbacks(); + $.ajaxPrefilter(callbacks.fire); + + callbacks.add(function (options, originalOptions, xhr) { + if (!options.crossDomain) { + xhr.setRequestHeader("X-CSRF-Token", session.get("csrfToken")); + } + }); } - const FieldComponent = requirejs("wizard/components/wizard-field").default; - const FieldModel = requirejs("wizard/models/wizard-field").default; - const { cook } = requirejs( - "discourse/plugins/discourse-custom-wizard/wizard/lib/text-lite" - ); const DEditor = requirejs("discourse/components/d-editor").default; const { clipboardHelpers } = requirejs("discourse/lib/utilities"); const toMarkdown = requirejs("discourse/lib/to-markdown").default; - const { translatedText } = requirejs( - "discourse/plugins/discourse-custom-wizard/wizard/lib/wizard-i18n" - ); - - FieldComponent.reopen({ - classNameBindings: ["field.id"], - - @discourseComputed("field.translatedDescription") - cookedDescription(description) { - return cook(description); - }, - - @discourseComputed("field.type") - textType(fieldType) { - return ["text", "textarea"].includes(fieldType); - }, - - inputComponentName: function () { - const type = this.get("field.type"); - const id = this.get("field.id"); - if (["text_only"].includes(type)) { - return false; - } - return dasherize(type === "component" ? id : `wizard-field-${type}`); - }.property("field.type", "field.id"), - }); - - const StandardFieldValidation = [ - "text", - "number", - "textarea", - "dropdown", - "tag", - "image", - "user_selector", - "text_only", - "composer", - "category", - "group", - "date", - "time", - "date_time", - ]; - - FieldModel.reopen({ - @discourseComputed("wizardId", "stepId", "id") - i18nKey(wizardId, stepId, id) { - return `${wizardId}.${stepId}.${id}`; - }, - - @discourseComputed("i18nKey", "label") - translatedLabel(i18nKey, label) { - return translatedText(`${i18nKey}.label`, label); - }, - - @discourseComputed("i18nKey", "placeholder") - translatedPlaceholder(i18nKey, placeholder) { - return translatedText(`${i18nKey}.placeholder`, placeholder); - }, - - @discourseComputed("i18nKey", "description") - translatedDescription(i18nKey, description) { - return translatedText(`${i18nKey}.description`, description); - }, - - check() { - if (this.customCheck) { - return this.customCheck(); - } - - let valid = this.valid; - - if (!this.required) { - this.setValid(true); - return true; - } - - const val = this.get("value"); - const type = this.get("type"); - - if (type === "checkbox") { - valid = val; - } else if (type === "upload") { - valid = val && val.id > 0; - } else if (StandardFieldValidation.indexOf(type) > -1) { - valid = val && val.toString().length > 0; - } else if (type === "url") { - valid = true; - } - - this.setValid(valid); - - return valid; - }, - }); - const isInside = (text, regex) => { const matches = text.match(regex); return matches && matches.length % 2; @@ -218,5 +126,19 @@ export default { } }, }); - }, + + // IE11 Polyfill - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries#Polyfill + if (!Object.entries) { + Object.entries = function (obj) { + let ownProps = Object.keys(obj), + i = ownProps.length, + resArray = new Array(i); // preallocate the Array + while (i--) { + resArray[i] = [ownProps[i], obj[ownProps[i]]]; + } + + return resArray; + }; + } + } }; diff --git a/assets/javascripts/wizard/lib/initialize/register-files.js.es6 b/assets/javascripts/wizard/lib/initialize/register-files.js.es6 new file mode 100644 index 00000000..8d4b850e --- /dev/null +++ b/assets/javascripts/wizard/lib/initialize/register-files.js.es6 @@ -0,0 +1,26 @@ +export default { + run(app, container) { + const RawHandlebars = requirejs("discourse-common/lib/raw-handlebars").default; + const Handlebars = requirejs("handlebars").default; + const registerRawHelpers = requirejs("discourse-common/lib/raw-handlebars-helpers").registerRawHelpers; + const { registerHelpers } = requirejs("discourse-common/lib/helpers"); + const jqueryPlugins = requirejs("discourse/initializers/jquery-plugins").default; + + Object.keys(Ember.TEMPLATES).forEach((k) => { + if (k.indexOf("select-kit") === 0) { + let template = Ember.TEMPLATES[k]; + define(k, () => template); + } + }); + + Object.keys(requirejs.entries).forEach((entry) => { + if (/\/helpers\//.test(entry)) { + requirejs(entry, null, null, true); + } + }); + + registerRawHelpers(RawHandlebars, Handlebars); + registerHelpers(app); + jqueryPlugins.initialize(container, app); + } +} diff --git a/assets/javascripts/wizard/lib/initialize/wizard.js.es6 b/assets/javascripts/wizard/lib/initialize/wizard.js.es6 new file mode 100644 index 00000000..134257a1 --- /dev/null +++ b/assets/javascripts/wizard/lib/initialize/wizard.js.es6 @@ -0,0 +1,49 @@ +export default { + name: "custom-wizard", + initialize(app) { + const isTesting = requirejs("discourse-common/config/environment").isTesting; + const isWizard = window.location.pathname.indexOf("/w/") > -1; + + if (!isWizard && !isTesting()) { + return; + } + + const container = app.__container__; + const setDefaultOwner = requirejs("discourse-common/lib/get-owner").setDefaultOwner; + setDefaultOwner(container); + + if (!isTesting()) { + const PreloadStore = requirejs("discourse/lib/preload-store").default; + + let preloaded; + const preloadedDataElement = document.getElementById("data-preloaded-wizard"); + if (preloadedDataElement) { + preloaded = JSON.parse(preloadedDataElement.dataset.preloadedWizard); + } + + Object.keys(preloaded).forEach(function (key) { + PreloadStore.store(key, JSON.parse(preloaded[key])); + }); + + app.SiteSettings = PreloadStore.get("siteSettings"); + } + + const setEnvironment = requirejs("discourse-common/config/environment").setEnvironment; + const setupData = document.getElementById("data-discourse-setup").dataset; + setEnvironment(setupData.environment); + + const Session = requirejs("discourse/models/session").default; + const session = Session.current(); + session.set("highlightJsPath", setupData.highlightJsPath); + + [ + 'register-files', + 'inject-objects', + 'create-contexts', + 'patch-components' + ].forEach(fileName => { + const initializer = requirejs(`discourse/plugins/discourse-custom-wizard/wizard/lib/initialize/${fileName}`).default; + initializer.run(app, container); + }); + } +}; diff --git a/assets/javascripts/wizard/lib/utilities-lite.js.es6 b/assets/javascripts/wizard/lib/utilities-lite.js.es6 deleted file mode 100644 index 19517b2b..00000000 --- a/assets/javascripts/wizard/lib/utilities-lite.js.es6 +++ /dev/null @@ -1,71 +0,0 @@ -// lite version of discourse/lib/utilities - -export function determinePostReplaceSelection({ - selection, - needle, - replacement, -}) { - const diff = - replacement.end - replacement.start - (needle.end - needle.start); - - if (selection.end <= needle.start) { - // Selection ends (and starts) before needle. - return { start: selection.start, end: selection.end }; - } else if (selection.start <= needle.start) { - // Selection starts before needle... - if (selection.end < needle.end) { - // ... and ends inside needle. - return { start: selection.start, end: needle.start }; - } else { - // ... and spans needle completely. - return { start: selection.start, end: selection.end + diff }; - } - } else if (selection.start < needle.end) { - // Selection starts inside needle... - if (selection.end <= needle.end) { - // ... and ends inside needle. - return { start: replacement.end, end: replacement.end }; - } else { - // ... and spans end of needle. - return { start: replacement.end, end: selection.end + diff }; - } - } else { - // Selection starts (and ends) behind needle. - return { start: selection.start + diff, end: selection.end + diff }; - } -} - -const toArray = (items) => { - items = items || []; - - if (!Array.isArray(items)) { - return Array.from(items); - } - - return items; -}; - -export function clipboardData(e, canUpload) { - const clipboard = - e.clipboardData || - e.originalEvent.clipboardData || - e.delegatedEvent.originalEvent.clipboardData; - - const types = toArray(clipboard.types); - let files = toArray(clipboard.files); - - if (types.includes("Files") && files.length === 0) { - // for IE - files = toArray(clipboard.items).filter((i) => i.kind === "file"); - } - - canUpload = files && canUpload && !types.includes("text/plain"); - const canUploadImage = - canUpload && files.filter((f) => f.type.match("^image/"))[0]; - const canPasteHtml = - Discourse.SiteSettings.enable_rich_text_paste && - types.includes("text/html") && - !canUploadImage; - - return { clipboard, types, canUpload, canPasteHtml }; -} diff --git a/assets/javascripts/wizard/models/field.js.es6 b/assets/javascripts/wizard/models/field.js.es6 new file mode 100644 index 00000000..5f76074e --- /dev/null +++ b/assets/javascripts/wizard/models/field.js.es6 @@ -0,0 +1,79 @@ +import EmberObject from "@ember/object"; +import ValidState from "wizard/mixins/valid-state"; +import discourseComputed from "discourse-common/utils/decorators"; +import { translatedText } from "discourse/plugins/discourse-custom-wizard/wizard/lib/wizard-i18n"; + +const StandardFieldValidation = [ + "text", + "number", + "textarea", + "dropdown", + "tag", + "image", + "user_selector", + "text_only", + "composer", + "category", + "group", + "date", + "time", + "date_time", +]; + +export default EmberObject.extend(ValidState, { + id: null, + type: null, + value: null, + required: null, + warning: null, + + @discourseComputed("wizardId", "stepId", "id") + i18nKey(wizardId, stepId, id) { + return `${wizardId}.${stepId}.${id}`; + }, + + @discourseComputed("i18nKey", "label") + translatedLabel(i18nKey, label) { + return translatedText(`${i18nKey}.label`, label); + }, + + @discourseComputed("i18nKey", "placeholder") + translatedPlaceholder(i18nKey, placeholder) { + return translatedText(`${i18nKey}.placeholder`, placeholder); + }, + + @discourseComputed("i18nKey", "description") + translatedDescription(i18nKey, description) { + return translatedText(`${i18nKey}.description`, description); + }, + + check() { + if (this.customCheck) { + return this.customCheck(); + } + + let valid = this.valid; + + if (!this.required) { + this.setValid(true); + return true; + } + + const val = this.get("value"); + const type = this.get("type"); + + if (type === "checkbox") { + valid = val; + } else if (type === "upload") { + valid = val && val.id > 0; + } else if (StandardFieldValidation.indexOf(type) > -1) { + valid = val && val.toString().length > 0; + } else if (type === "url") { + valid = true; + } + + this.setValid(valid); + + return valid; + } +}); diff --git a/assets/javascripts/wizard/models/site.js.es6 b/assets/javascripts/wizard/models/site.js.es6 index c15ce98f..96837ff2 100644 --- a/assets/javascripts/wizard/models/site.js.es6 +++ b/assets/javascripts/wizard/models/site.js.es6 @@ -1,10 +1,11 @@ import Site from "discourse/models/site"; +import { getOwner } from "discourse-common/lib/get-owner"; export default Site.reopenClass({ // There is no site data actually loaded by the CW yet. This placeholder is // needed by imported classes createCurrent() { - const store = Discourse.__container__.lookup("service:store"); + const store = getOwner(this).lookup("service:store"); return store.createRecord("site", {}); }, }); diff --git a/assets/javascripts/wizard/models/step.js.es6 b/assets/javascripts/wizard/models/step.js.es6 new file mode 100644 index 00000000..e18657b5 --- /dev/null +++ b/assets/javascripts/wizard/models/step.js.es6 @@ -0,0 +1,114 @@ +import EmberObject from "@ember/object"; +import ValidState from "wizard/mixins/valid-state"; +import { ajax } from "wizard/lib/ajax"; +import discourseComputed from "discourse-common/utils/decorators"; +import { translatedText } from "discourse/plugins/discourse-custom-wizard/wizard/lib/wizard-i18n"; + +export default EmberObject.extend(ValidState, { + id: null, + + @discourseComputed("wizardId", "id") + i18nKey(wizardId, stepId) { + return `${wizardId}.${stepId}`; + }, + + @discourseComputed("i18nKey", "title") + translatedTitle(i18nKey, title) { + return translatedText(`${i18nKey}.title`, title); + }, + + @discourseComputed("i18nKey", "description") + translatedDescription(i18nKey, description) { + return translatedText(`${i18nKey}.description`, description); + }, + + @discourseComputed("index") + displayIndex: (index) => index + 1, + + @discourseComputed("fields.[]") + fieldsById(fields) { + const lookup = {}; + fields.forEach((field) => (lookup[field.get("id")] = field)); + return lookup; + }, + + validate() { + let allValid = true; + const result = { warnings: [] }; + + this.fields.forEach((field) => { + allValid = allValid && field.check(); + const warning = field.get("warning"); + if (warning) { + result.warnings.push(warning); + } + }); + + this.setValid(allValid); + + return result; + }, + + fieldError(id, description) { + const field = this.fields.findBy("id", id); + if (field) { + field.setValid(false, description); + } + }, + + save() { + const wizardId = this.get("wizardId"); + const fields = {}; + + this.get("fields").forEach((f) => { + if (f.type !== "text_only") { + fields[f.id] = f.value; + } + }); + + return ajax({ + url: `/w/${wizardId}/steps/${this.get("id")}`, + type: "PUT", + data: { fields }, + }).catch((response) => { + if ( + response && + response.responseJSON && + response.responseJSON.errors + ) { + let wizardErrors = []; + response.responseJSON.errors.forEach((err) => { + if (err.field === wizardId) { + wizardErrors.push(err.description); + } else if (err.field) { + this.fieldError(err.field, err.description); + } else if (err) { + wizardErrors.push(err); + } + }); + if (wizardErrors.length) { + this.handleWizardError(wizardErrors.join("\n")); + } + this.animateInvalidFields(); + throw response; + } + + if (response && response.responseText) { + const responseText = response.responseText; + const start = responseText.indexOf(">") + 1; + const end = responseText.indexOf("plugins"); + const message = responseText.substring(start, end); + this.handleWizardError(message); + throw message; + } + }); + }, + + handleWizardError(message) { + this.set("message", { + state: "error", + text: message, + }); + Ember.run.later(() => this.set("message", null), 6000); + }, +}); diff --git a/assets/javascripts/wizard/models/custom.js.es6 b/assets/javascripts/wizard/models/wizard.js.es6 similarity index 96% rename from assets/javascripts/wizard/models/custom.js.es6 rename to assets/javascripts/wizard/models/wizard.js.es6 index 4cbd5706..b2e26b25 100644 --- a/assets/javascripts/wizard/models/custom.js.es6 +++ b/assets/javascripts/wizard/models/wizard.js.es6 @@ -1,9 +1,9 @@ import { default as computed } from "discourse-common/utils/decorators"; import getUrl from "discourse-common/lib/get-url"; -import WizardField from "wizard/models/wizard-field"; +import Field from "./field"; import { ajax } from "wizard/lib/ajax"; import { popupAjaxError } from "discourse/lib/ajax-error"; -import Step from "wizard/models/step"; +import Step from "./step"; import EmberObject from "@ember/object"; import Site from "./site"; @@ -77,7 +77,7 @@ CustomWizard.reopenClass({ stepObj.fields = stepObj.fields.map((f) => { f.wizardId = wizardJson.id; f.stepId = stepObj.id; - return WizardField.create(f); + return Field.create(f); }); return stepObj; diff --git a/assets/javascripts/wizard/router.js.es6 b/assets/javascripts/wizard/router.js.es6 new file mode 100644 index 00000000..5178c91f --- /dev/null +++ b/assets/javascripts/wizard/router.js.es6 @@ -0,0 +1,17 @@ +import EmberRouter from "@ember/routing/router"; +import getUrl from "discourse-common/lib/get-url"; +import { isTesting } from "discourse-common/config/environment"; + +const Router = EmberRouter.extend({ + rootURL: isTesting() ? getUrl("/") : getUrl("/w/"), + location: isTesting() ? "none" : "history", +}); + +Router.map(function () { + this.route("wizard", { path: "/:wizard_id" }, function () { + this.route("steps", { path: "/steps", resetNamespace: true }); + this.route("step", { path: "/steps/:step_id", resetNamespace: true }); + }); +}); + +export default Router; diff --git a/assets/javascripts/wizard/routes/application.js.es6 b/assets/javascripts/wizard/routes/application.js.es6 new file mode 100644 index 00000000..0051f5ce --- /dev/null +++ b/assets/javascripts/wizard/routes/application.js.es6 @@ -0,0 +1,3 @@ +import Route from "@ember/routing/route"; + +export default Route.extend(); diff --git a/assets/javascripts/wizard/routes/custom-steps.js.es6 b/assets/javascripts/wizard/routes/custom-steps.js.es6 deleted file mode 100644 index c58d1251..00000000 --- a/assets/javascripts/wizard/routes/custom-steps.js.es6 +++ /dev/null @@ -1,5 +0,0 @@ -export default Ember.Route.extend({ - redirect() { - this.transitionTo("custom.index"); - }, -}); diff --git a/assets/javascripts/wizard/routes/index.js.es6 b/assets/javascripts/wizard/routes/index.js.es6 new file mode 100644 index 00000000..b24b3631 --- /dev/null +++ b/assets/javascripts/wizard/routes/index.js.es6 @@ -0,0 +1,9 @@ +import Route from "@ember/routing/route"; + +export default Route.extend({ + beforeModel(transition) { + if (transition.intent.params) { + this.transitionTo("wizard"); + } + }, +}); diff --git a/assets/javascripts/wizard/routes/custom-step.js.es6 b/assets/javascripts/wizard/routes/step.js.es6 similarity index 78% rename from assets/javascripts/wizard/routes/custom-step.js.es6 rename to assets/javascripts/wizard/routes/step.js.es6 index 8088727a..2454fc95 100644 --- a/assets/javascripts/wizard/routes/custom-step.js.es6 +++ b/assets/javascripts/wizard/routes/step.js.es6 @@ -1,7 +1,8 @@ import WizardI18n from "../lib/wizard-i18n"; -import { getCachedWizard } from "../models/custom"; +import { getCachedWizard } from "../models/wizard"; +import Route from "@ember/routing/route"; -export default Ember.Route.extend({ +export default Route.extend({ beforeModel() { this.set("wizard", getCachedWizard()); }, @@ -19,11 +20,15 @@ export default Ember.Route.extend({ afterModel(model) { if (model.completed) { - return this.transitionTo("index"); + return this.transitionTo("wizard.index"); } return model.set("wizardId", this.wizard.id); }, + renderTemplate() { + this.render('wizard/templates/step'); + }, + setupController(controller, model) { let props = { step: model, diff --git a/assets/javascripts/wizard/routes/steps.js.es6 b/assets/javascripts/wizard/routes/steps.js.es6 new file mode 100644 index 00000000..6f35d152 --- /dev/null +++ b/assets/javascripts/wizard/routes/steps.js.es6 @@ -0,0 +1,7 @@ +import Route from "@ember/routing/route"; + +export default Route.extend({ + redirect() { + this.transitionTo("wizard.index"); + }, +}); diff --git a/assets/javascripts/wizard/routes/custom-index.js.es6 b/assets/javascripts/wizard/routes/wizard-index.js.es6 similarity index 50% rename from assets/javascripts/wizard/routes/custom-index.js.es6 rename to assets/javascripts/wizard/routes/wizard-index.js.es6 index a8abc152..16b1140a 100644 --- a/assets/javascripts/wizard/routes/custom-index.js.es6 +++ b/assets/javascripts/wizard/routes/wizard-index.js.es6 @@ -1,10 +1,11 @@ -import { getCachedWizard } from "../models/custom"; +import { getCachedWizard } from "../models/wizard"; +import Route from "@ember/routing/route"; -export default Ember.Route.extend({ +export default Route.extend({ beforeModel() { const wizard = getCachedWizard(); - if (wizard && wizard.permitted && !wizard.completed && wizard.start) { - this.replaceWith("custom.step", wizard.start); + if (wizard && wizard.user && wizard.permitted && !wizard.completed && wizard.start) { + this.replaceWith("step", wizard.start); } }, @@ -12,6 +13,10 @@ export default Ember.Route.extend({ return getCachedWizard(); }, + renderTemplate() { + this.render('wizard/templates/wizard-index'); + }, + setupController(controller, model) { if (model && model.id) { const completed = model.get("completed"); @@ -19,17 +24,20 @@ export default Ember.Route.extend({ const wizardId = model.get("id"); const user = model.get("user"); const name = model.get("name"); + const requiresLogin = !user; + const notPermitted = !permitted; - controller.setProperties({ - requiresLogin: !user, + const props = { + requiresLogin, user, name, completed, - notPermitted: !permitted, + notPermitted, wizardId, - }); + }; + controller.setProperties(props); } else { controller.set("noWizard", true); } - }, + } }); diff --git a/assets/javascripts/wizard/routes/custom.js.es6 b/assets/javascripts/wizard/routes/wizard.js.es6 similarity index 59% rename from assets/javascripts/wizard/routes/custom.js.es6 rename to assets/javascripts/wizard/routes/wizard.js.es6 index a312db3a..2910ee6d 100644 --- a/assets/javascripts/wizard/routes/custom.js.es6 +++ b/assets/javascripts/wizard/routes/wizard.js.es6 @@ -1,37 +1,26 @@ -/* eslint no-undef: 0*/ - -import { findCustomWizard, updateCachedWizard } from "../models/custom"; +import { findCustomWizard, updateCachedWizard } from "../models/wizard"; import { ajax } from "wizard/lib/ajax"; import WizardI18n from "../lib/wizard-i18n"; +import Route from "@ember/routing/route"; +import { scheduleOnce } from "@ember/runloop"; +import { getOwner } from "discourse-common/lib/get-owner"; -export default Ember.Route.extend({ +export default Route.extend({ beforeModel(transition) { - this.set("queryParams", transition.intent.queryParams); + if (transition.intent.queryParams) { + this.set("queryParams", transition.intent.queryParams); + } }, model(params) { return findCustomWizard(params.wizard_id, this.get("queryParams")); }, - renderTemplate() { - this.render("custom"); - const wizardModel = this.modelFor("custom"); - const stepModel = this.modelFor("custom.step"); - - if ( - wizardModel.resume_on_revisit && - wizardModel.submission_last_updated_at && - stepModel.index > 0 - ) { - this.showDialog(wizardModel); - } - }, - showDialog(wizardModel) { const title = WizardI18n("wizard.incomplete_submission.title", { date: moment(wizardModel.submission_last_updated_at).format( "MMMM Do YYYY" - ), + ) }); const buttons = [ @@ -57,28 +46,36 @@ export default Ember.Route.extend({ afterModel(model) { updateCachedWizard(model); + }, - return ajax({ - url: `/site/settings`, - type: "GET", - }).then((result) => { - $.extend(Wizard.SiteSettings, result); - }); + renderTemplate() { + this.render('wizard/templates/wizard'); }, setupController(controller, model) { - const background = model ? model.get("background") : "AliceBlue"; - Ember.run.scheduleOnce("afterRender", this, function () { - $("body.custom-wizard").css("background", background); + const background = model ? model.get("background") : ""; + + scheduleOnce("afterRender", this, function () { + $("body").css("background", background); if (model && model.id) { - $("#custom-wizard-main").addClass(model.id.dasherize()); + $(getOwner(this).rootElement).addClass(model.id.dasherize()); } }); + controller.setProperties({ customWizard: true, - logoUrl: Wizard.SiteSettings.logo_small, + logoUrl: this.siteSettings.logo_small, reset: null, }); + + const stepModel = this.modelFor("step"); + if ( + model.resume_on_revisit && + model.submission_last_updated_at && + stepModel.index > 0 + ) { + this.showDialog(model); + } }, }); diff --git a/assets/javascripts/wizard/templates/components/wizard-field-dropdown.hbs b/assets/javascripts/wizard/templates/components/wizard-field-dropdown.hbs index 7ce4c298..42fc63e8 100644 --- a/assets/javascripts/wizard/templates/components/wizard-field-dropdown.hbs +++ b/assets/javascripts/wizard/templates/components/wizard-field-dropdown.hbs @@ -3,6 +3,7 @@ value=field.value content=field.content tabindex=field.tabindex + onChange=(action "onChangeValue") options=(hash none="select_kit.default_header_text" )}} diff --git a/assets/javascripts/wizard/templates/custom.index.hbs b/assets/javascripts/wizard/templates/custom.index.hbs deleted file mode 100644 index 3cd02e05..00000000 --- a/assets/javascripts/wizard/templates/custom.index.hbs +++ /dev/null @@ -1,15 +0,0 @@ -{{#if noWizard}} - {{wizard-no-access text=(wizard-i18n "wizard.none") wizardId=wizardId}} -{{else}} - {{#if requiresLogin}} - {{wizard-no-access text=(wizard-i18n "wizard.requires_login") wizardId=wizardId}} - {{else}} - {{#if notPermitted}} - {{wizard-no-access text=(wizard-i18n "wizard.not_permitted") wizardId=wizardId}} - {{else}} - {{#if completed}} - {{wizard-no-access text=(wizard-i18n "wizard.completed") wizardId=wizardId}} - {{/if}} - {{/if}} - {{/if}} -{{/if}} diff --git a/assets/javascripts/wizard/templates/index.hbs b/assets/javascripts/wizard/templates/index.hbs new file mode 100644 index 00000000..c24cd689 --- /dev/null +++ b/assets/javascripts/wizard/templates/index.hbs @@ -0,0 +1 @@ +{{outlet}} diff --git a/assets/javascripts/wizard/templates/custom.step.hbs b/assets/javascripts/wizard/templates/step.hbs similarity index 100% rename from assets/javascripts/wizard/templates/custom.step.hbs rename to assets/javascripts/wizard/templates/step.hbs diff --git a/assets/javascripts/wizard/templates/wizard-index.hbs b/assets/javascripts/wizard/templates/wizard-index.hbs new file mode 100644 index 00000000..a8f1b9ba --- /dev/null +++ b/assets/javascripts/wizard/templates/wizard-index.hbs @@ -0,0 +1,3 @@ +{{#if noAccess}} + {{wizard-no-access text=(wizard-i18n noAccessI18nKey) wizardId=wizardId reason=noAccessReason}} +{{/if}} diff --git a/assets/javascripts/wizard/templates/custom.hbs b/assets/javascripts/wizard/templates/wizard.hbs similarity index 86% rename from assets/javascripts/wizard/templates/custom.hbs rename to assets/javascripts/wizard/templates/wizard.hbs index 4701fec2..f6d6127e 100644 --- a/assets/javascripts/wizard/templates/custom.hbs +++ b/assets/javascripts/wizard/templates/wizard.hbs @@ -1,7 +1,3 @@ -{{#if showCanvas}} - {{wizard-canvas}} -{{/if}} -
{{outlet}} diff --git a/assets/javascripts/wizard/tests/acceptance/field-test.js.es6 b/assets/javascripts/wizard/tests/acceptance/field-test.js.es6 new file mode 100644 index 00000000..f73d1ab7 --- /dev/null +++ b/assets/javascripts/wizard/tests/acceptance/field-test.js.es6 @@ -0,0 +1,135 @@ +import { + visit, + click, + fillIn, + triggerKeyEvent +} from "@ember/test-helpers"; +import { test } from "qunit"; +import { exists } from "../helpers/test"; +import acceptance, { + query, + count, + visible, + server +} from "../helpers/acceptance"; +import { + allFieldsWizard, + getWizard +} from "../helpers/wizard"; +import tagsJson from "../fixtures/tags"; +import usersJson from "../fixtures/users"; +import { response } from "../pretender"; + +acceptance("Field | Fields", [ getWizard(allFieldsWizard) ], + function(hooks) { + test("Text", async function (assert) { + await visit("/wizard"); + assert.ok(exists(".wizard-field.text-field input.wizard-focusable")); + }); + + test("Textarea", async function (assert) { + await visit("/wizard"); + assert.ok(visible(".wizard-field.textarea-field textarea.wizard-focusable")); + }); + + test("Composer", async function (assert) { + await visit("/wizard"); + assert.ok(visible(".wizard-field.composer-field .wizard-field-composer textarea")); + assert.strictEqual(count(".wizard-field.composer-field .d-editor-button-bar button"), 8); + assert.ok(visible(".wizard-btn.toggle-preview")); + + await fillIn(".wizard-field.composer-field .wizard-field-composer textarea", "Input in composer"); + await click(".wizard-btn.toggle-preview"); + assert.strictEqual(query('.wizard-field.composer-field .wizard-field-composer .d-editor-preview-wrapper p').textContent.trim(), "Input in composer"); + }); + + test("Text Only", async function (assert) { + await visit("/wizard"); + assert.ok(visible(".wizard-field.text-only-field label.field-label")); + }); + + test("Date", async function (assert) { + await visit("/wizard"); + assert.ok(visible(".wizard-field.date-field input.date-picker")); + await click(".wizard-field.date-field input.date-picker"); + assert.ok(visible(".wizard-field.date-field .pika-single")); + }); + + test("Time", async function (assert) { + await visit("/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-collection")); + }); + + test("Date Time", async function (assert) { + await visit("/wizard"); + assert.ok(visible(".wizard-field.date-time-field .d-date-time-input .select-kit")); + await click(".wizard-field.date-time-field .d-date-input input.date-picker"); + assert.ok(visible(".wizard-field.date-time-field .d-date-input .pika-single")); + await click(".wizard-field.date-time-field .d-time-input .select-kit .select-kit-header"); + assert.ok(visible(".wizard-field.date-time-field .select-kit-collection")); + }); + + test("Number", async function (assert) { + await visit("/wizard"); + assert.ok(visible(".wizard-field.number-field input[type='number']")); + }); + + test("Checkbox", async function (assert) { + await visit("/wizard"); + assert.ok(visible(".wizard-field.checkbox-field input[type='checkbox']")); + }); + + test("Url", async function (assert) { + await visit("/wizard"); + assert.ok(visible(".wizard-field.url-field input[type='text']")); + }); + + test("Upload", async function (assert) { + await visit("/wizard"); + assert.ok(visible(".wizard-field.upload-field label.wizard-btn-upload-file")); + assert.ok(exists(".wizard-field.upload-field input.hidden-upload-field")); + }); + + test("Dropdown", async function (assert) { + await visit("/wizard"); + assert.ok(visible(".wizard-field.dropdown-field .single-select-header")); + await click(".wizard-field.dropdown-field .select-kit-header"); + assert.strictEqual(count(".wizard-field.dropdown-field .select-kit-collection li"), 3); + }); + + test("Tag", async function (assert) { + server.get("/tags/filter/search", () => (response(200, { results: tagsJson['tags']}))); + await visit("/wizard"); + assert.ok(visible(".wizard-field.tag-field .multi-select-header")); + await click(".wizard-field.tag-field .select-kit-header"); + assert.strictEqual(count(".wizard-field.tag-field .select-kit-collection li"), 2); + }); + + test("Category", async function (assert) { + await visit("/wizard"); + assert.ok(visible(".wizard-field.category-field .multi-select-header")); + await click(".wizard-field.category-field .select-kit-header"); + assert.strictEqual(count(".wizard-field.category-field .select-kit-collection li"), 5); + }); + + test("Group", async function (assert) { + await visit("/wizard"); + assert.ok(visible(".wizard-field.group-field .single-select-header")); + await click(".wizard-field.group-field .select-kit-header"); + assert.strictEqual(count(".wizard-field.group-field .select-kit-collection li"), 10); + }); + + test("User", async function (assert) { + server.get("/u/search/users", () => (response(200, usersJson))); + + await visit("/wizard"); + await fillIn(".wizard-field.user-selector-field input.ember-text-field", "a"); + await triggerKeyEvent(".wizard-field.user-selector-field input.ember-text-field", "keyup", "a".charCodeAt(0)); + + assert.ok(visible(".wizard-field.user-selector-field .ac-wrap")); + // TODO: add assertion for ac results. autocomplete does not appear in time. + }); + } +); diff --git a/assets/javascripts/wizard/tests/acceptance/step-test.js.es6 b/assets/javascripts/wizard/tests/acceptance/step-test.js.es6 new file mode 100644 index 00000000..731c9b76 --- /dev/null +++ b/assets/javascripts/wizard/tests/acceptance/step-test.js.es6 @@ -0,0 +1,47 @@ +import { visit, click } from "@ember/test-helpers"; +import { test } from "qunit"; +import { exists } from "../helpers/test"; +import acceptance, { + query, + count, + visible +} from "../helpers/acceptance"; +import { + stepNotPermitted, + wizard, + getWizard +} from "../helpers/wizard"; +import { + saveStep, + update +} from "../helpers/step"; + +acceptance("Step | Not permitted", [ getWizard(stepNotPermitted) ], + function(hooks) { + test("Shows not permitted message", async function (assert) { + await visit("/wizard"); + assert.ok(exists(".step-message.not-permitted")); + }); + } +); + +acceptance("Step | Step", [ getWizard(wizard), saveStep(update) ], + function(hooks) { + test("Renders the step", async function (assert) { + await visit("/wizard"); + assert.strictEqual(query('.wizard-step-title p').textContent.trim(), "Text"); + assert.strictEqual(query('.wizard-step-description p').textContent.trim(), "Text inputs!"); + assert.strictEqual(query('.wizard-step-description p').textContent.trim(), "Text inputs!"); + assert.strictEqual(count('.wizard-step-form .wizard-field'), 6); + assert.ok(visible('.wizard-step-footer .wizard-progress'), true); + assert.ok(visible('.wizard-step-footer .wizard-buttons'), true); + }); + + test("Goes to the next step", async function (assert) { + await visit("/wizard"); + assert.ok(visible('.wizard-step.step_1'), true); + await click('.wizard-btn.next'); + assert.ok(visible('.wizard-step.step_2'), true); + }); + } +); diff --git a/assets/javascripts/wizard/tests/acceptance/wizard-test.js.es6 b/assets/javascripts/wizard/tests/acceptance/wizard-test.js.es6 new file mode 100644 index 00000000..e68f59ae --- /dev/null +++ b/assets/javascripts/wizard/tests/acceptance/wizard-test.js.es6 @@ -0,0 +1,72 @@ +import { visit } from "@ember/test-helpers"; +import { test } from "qunit"; +import { exists } from "../helpers/test"; +import acceptance, { + query, + count, + visible +} from "../helpers/acceptance"; +import { + wizardNoUser, + wizardNotPermitted, + wizardCompleted, + wizard, + getWizard +} from "../helpers/wizard"; + +acceptance("Wizard | Not logged in", [ getWizard(wizardNoUser) ], + function(hooks) { + test("Wizard no access requires login", async function (assert) { + await visit("/wizard"); + assert.ok(exists(".wizard-no-access.requires-login")); + }); + } +); + +acceptance("Wizard | Not permitted", [ getWizard(wizardNotPermitted) ], + function(hooks) { + test("Wizard no access not permitted", async function (assert) { + await visit("/wizard"); + assert.ok(exists(".wizard-no-access.not-permitted")); + }); + } +); + +acceptance("Wizard | Completed", [ getWizard(wizardCompleted) ], + function(hooks) { + test("Wizard no access completed", async function (assert) { + await visit("/wizard"); + assert.ok(exists(".wizard-no-access.completed")); + }); + } +); + +acceptance("Wizard | Wizard", [ getWizard(wizard) ], + function(hooks) { + test("Starts", async function (assert) { + await visit("/wizard"); + assert.ok(query('.wizard-column'), true); + }); + + test("Applies the body background color", async function (assert) { + await visit("/wizard"); + assert.ok($("body")[0].style.background); + }); + + test("Renders the wizard form", async function (assert) { + await visit("/wizard"); + assert.ok(visible('.wizard-column-contents .wizard-step'), true); + assert.ok(visible('.wizard-footer img'), true); + }); + + test("Renders the first step", async function (assert) { + await visit("/wizard"); + assert.strictEqual(query('.wizard-step-title p').textContent.trim(), "Text"); + assert.strictEqual(query('.wizard-step-description p').textContent.trim(), "Text inputs!"); + assert.strictEqual(query('.wizard-step-description p').textContent.trim(), "Text inputs!"); + assert.strictEqual(count('.wizard-step-form .wizard-field'), 6); + assert.ok(visible('.wizard-step-footer .wizard-progress'), true); + assert.ok(visible('.wizard-step-footer .wizard-buttons'), true); + }); + } +); diff --git a/assets/javascripts/wizard/tests/bootstrap.js.es6 b/assets/javascripts/wizard/tests/bootstrap.js.es6 new file mode 100644 index 00000000..d2c503ad --- /dev/null +++ b/assets/javascripts/wizard/tests/bootstrap.js.es6 @@ -0,0 +1,17 @@ +// discourse-skip-module + +document.addEventListener("DOMContentLoaded", function () { + document.body.insertAdjacentHTML( + "afterbegin", + ` +
+ + ` + ); +}); + +Object.keys(requirejs.entries).forEach(function (entry) { + if (/\-test/.test(entry)) { + requirejs(entry); + } +}); diff --git a/assets/javascripts/wizard/tests/fixtures/categories.js.es6 b/assets/javascripts/wizard/tests/fixtures/categories.js.es6 new file mode 100644 index 00000000..e862f54a --- /dev/null +++ b/assets/javascripts/wizard/tests/fixtures/categories.js.es6 @@ -0,0 +1,209 @@ +export default { + "categories": [ + { + "id": 1, + "name": "Uncategorized", + "color": "0088CC", + "text_color": "FFFFFF", + "slug": "uncategorized", + "topic_count": 1, + "post_count": 1, + "position": 0, + "description": "Topics that don't need a category, or don't fit into any other existing category.", + "description_text": "Topics that don't need a category, or don't fit into any other existing category.", + "description_excerpt": "Topics that don't need a category, or don't fit into any other existing category.", + "topic_url": "/t/", + "read_restricted": false, + "permission": 1, + "notification_level": 0, + "topic_template": null, + "has_children": false, + "sort_order": null, + "sort_ascending": null, + "show_subcategory_list": false, + "num_featured_topics": 3, + "default_view": null, + "subcategory_list_style": "rows_with_featured_topics", + "default_top_period": "all", + "default_list_filter": "all", + "minimum_required_tags": 0, + "navigate_to_first_post_after_read": false, + "custom_fields": { + "create_topic_wizard": null + }, + "allowed_tags": [], + "allowed_tag_groups": [], + "allow_global_tags": false, + "min_tags_from_required_group": 1, + "required_tag_group_name": null, + "read_only_banner": null, + "uploaded_logo": null, + "uploaded_background": null, + "can_edit": true + }, + { + "id": 2, + "name": "Site Feedback", + "color": "808281", + "text_color": "FFFFFF", + "slug": "site-feedback", + "topic_count": 20, + "post_count": 21, + "position": 1, + "description": "

Discussion about this site, its organization, how it works, and how we can improve it.

", + "description_text": "Discussion about this site, its organization, how it works, and how we can improve it.", + "description_excerpt": "Discussion about this site, its organization, how it works, and how we can improve it.", + "topic_url": "/t/about-the-site-feedback-category/1", + "read_restricted": false, + "permission": 1, + "notification_level": 0, + "topic_template": null, + "has_children": false, + "sort_order": null, + "sort_ascending": null, + "show_subcategory_list": false, + "num_featured_topics": 3, + "default_view": null, + "subcategory_list_style": "rows_with_featured_topics", + "default_top_period": "all", + "default_list_filter": "all", + "minimum_required_tags": 0, + "navigate_to_first_post_after_read": false, + "custom_fields": { + "create_topic_wizard": null + }, + "allowed_tags": [], + "allowed_tag_groups": [], + "allow_global_tags": false, + "min_tags_from_required_group": 1, + "required_tag_group_name": null, + "read_only_banner": null, + "uploaded_logo": null, + "uploaded_background": null, + "can_edit": true + }, + { + "id": 3, + "name": "Staff", + "color": "E45735", + "text_color": "FFFFFF", + "slug": "staff", + "topic_count": 4, + "post_count": 7, + "position": 2, + "description": "

Private category for staff discussions. Topics are only visible to admins and moderators.

", + "description_text": "Private category for staff discussions. Topics are only visible to admins and moderators.", + "description_excerpt": "Private category for staff discussions. Topics are only visible to admins and moderators.", + "topic_url": "/t/about-the-staff-category/2", + "read_restricted": true, + "permission": 1, + "notification_level": 0, + "topic_template": null, + "has_children": false, + "sort_order": null, + "sort_ascending": null, + "show_subcategory_list": false, + "num_featured_topics": 3, + "default_view": null, + "subcategory_list_style": "rows_with_featured_topics", + "default_top_period": "all", + "default_list_filter": "all", + "minimum_required_tags": 0, + "navigate_to_first_post_after_read": false, + "custom_fields": { + "create_topic_wizard": null + }, + "allowed_tags": [], + "allowed_tag_groups": [], + "allow_global_tags": false, + "min_tags_from_required_group": 1, + "required_tag_group_name": null, + "read_only_banner": null, + "uploaded_logo": null, + "uploaded_background": null, + "can_edit": true + }, + { + "id": 4, + "name": "Lounge", + "color": "A461EF", + "text_color": "652D90", + "slug": "lounge", + "topic_count": 1, + "post_count": 1, + "position": 3, + "description": "

A category exclusive to members with trust level 3 and higher.

", + "description_text": "A category exclusive to members with trust level 3 and higher.", + "description_excerpt": "A category exclusive to members with trust level 3 and higher.", + "topic_url": "/t/about-the-lounge-category/3", + "read_restricted": true, + "permission": 1, + "notification_level": 0, + "topic_template": null, + "has_children": false, + "sort_order": null, + "sort_ascending": null, + "show_subcategory_list": false, + "num_featured_topics": 3, + "default_view": null, + "subcategory_list_style": "rows_with_featured_topics", + "default_top_period": "all", + "default_list_filter": "all", + "minimum_required_tags": 0, + "navigate_to_first_post_after_read": false, + "custom_fields": { + "create_topic_wizard": null + }, + "allowed_tags": [], + "allowed_tag_groups": [], + "allow_global_tags": false, + "min_tags_from_required_group": 1, + "required_tag_group_name": null, + "read_only_banner": null, + "uploaded_logo": null, + "uploaded_background": null, + "can_edit": true + }, + { + "id": 5, + "name": "Custom Categories", + "color": "0088CC", + "text_color": "FFFFFF", + "slug": "custom-category", + "topic_count": 0, + "post_count": 0, + "position": 10, + "description": "Description of custom category", + "description_text": "Description of custom category", + "description_excerpt": "Description of custom category", + "topic_url": "/t/about-the-custom-category/5", + "read_restricted": false, + "permission": 1, + "notification_level": 0, + "topic_template": null, + "has_children": false, + "sort_order": null, + "sort_ascending": null, + "show_subcategory_list": false, + "num_featured_topics": 3, + "default_view": null, + "subcategory_list_style": "rows_with_featured_topics", + "default_top_period": "all", + "default_list_filter": "all", + "minimum_required_tags": 0, + "navigate_to_first_post_after_read": false, + "custom_fields": { + "create_topic_wizard": null + }, + "allowed_tags": [], + "allowed_tag_groups": [], + "allow_global_tags": false, + "min_tags_from_required_group": 1, + "required_tag_group_name": null, + "read_only_banner": null, + "uploaded_logo": null, + "uploaded_background": null, + "can_edit": true + } + ] +} diff --git a/assets/javascripts/wizard/tests/fixtures/groups.js.es6 b/assets/javascripts/wizard/tests/fixtures/groups.js.es6 new file mode 100644 index 00000000..16f8150d --- /dev/null +++ b/assets/javascripts/wizard/tests/fixtures/groups.js.es6 @@ -0,0 +1,313 @@ +export default { + "groups": [ + { + "id": 1, + "automatic": true, + "name": "admins", + "display_name": "admins", + "user_count": 1, + "mentionable_level": 0, + "messageable_level": 0, + "visibility_level": 1, + "primary_group": false, + "title": null, + "grant_trust_level": null, + "incoming_email": null, + "has_messages": false, + "flair_url": null, + "flair_bg_color": null, + "flair_color": null, + "bio_raw": null, + "bio_cooked": null, + "bio_excerpt": null, + "public_admission": false, + "public_exit": false, + "allow_membership_requests": false, + "full_name": null, + "default_notification_level": 3, + "membership_request_template": null, + "members_visibility_level": 0, + "can_see_members": true, + "can_admin_group": true, + "publish_read_state": false + }, + { + "id": 0, + "automatic": true, + "name": "everyone", + "display_name": "everyone", + "user_count": 0, + "mentionable_level": 0, + "messageable_level": 0, + "visibility_level": 3, + "primary_group": false, + "title": null, + "grant_trust_level": null, + "incoming_email": null, + "has_messages": false, + "flair_url": null, + "flair_bg_color": null, + "flair_color": null, + "bio_raw": null, + "bio_cooked": null, + "bio_excerpt": null, + "public_admission": false, + "public_exit": false, + "allow_membership_requests": false, + "full_name": null, + "default_notification_level": 3, + "membership_request_template": null, + "members_visibility_level": 0, + "can_see_members": true, + "can_admin_group": true, + "publish_read_state": false + }, + { + "id": 15, + "automatic": false, + "name": "custom_group", + "user_count": 1, + "mentionable_level": 1, + "messageable_level": 2, + "visibility_level": 3, + "primary_group": false, + "title": "Custom Group", + "grant_trust_level": 3, + "incoming_email": null, + "has_messages": false, + "flair_url": null, + "flair_bg_color": null, + "flair_color": null, + "bio_raw": null, + "bio_cooked": null, + "bio_excerpt": null, + "public_admission": false, + "public_exit": false, + "allow_membership_requests": false, + "full_name": "I am prefilled", + "default_notification_level": 3, + "membership_request_template": null, + "members_visibility_level": 99, + "can_see_members": true, + "can_admin_group": true, + "publish_read_state": false + }, + { + "id": 2, + "automatic": true, + "name": "moderators", + "display_name": "moderators", + "user_count": 0, + "mentionable_level": 0, + "messageable_level": 99, + "visibility_level": 1, + "primary_group": false, + "title": null, + "grant_trust_level": null, + "incoming_email": null, + "has_messages": false, + "flair_url": null, + "flair_bg_color": null, + "flair_color": null, + "bio_raw": null, + "bio_cooked": null, + "bio_excerpt": null, + "public_admission": false, + "public_exit": false, + "allow_membership_requests": false, + "full_name": null, + "default_notification_level": 2, + "membership_request_template": null, + "members_visibility_level": 0, + "can_see_members": true, + "can_admin_group": true, + "publish_read_state": false + }, + { + "id": 3, + "automatic": true, + "name": "staff", + "display_name": "staff", + "user_count": 1, + "mentionable_level": 0, + "messageable_level": 0, + "visibility_level": 1, + "primary_group": false, + "title": null, + "grant_trust_level": null, + "incoming_email": null, + "has_messages": false, + "flair_url": null, + "flair_bg_color": null, + "flair_color": null, + "bio_raw": null, + "bio_cooked": null, + "bio_excerpt": null, + "public_admission": false, + "public_exit": false, + "allow_membership_requests": false, + "full_name": null, + "default_notification_level": 3, + "membership_request_template": null, + "members_visibility_level": 0, + "can_see_members": true, + "can_admin_group": true, + "publish_read_state": false + }, + { + "id": 10, + "automatic": true, + "name": "trust_level_0", + "display_name": "trust_level_0", + "user_count": 2, + "mentionable_level": 0, + "messageable_level": 0, + "visibility_level": 1, + "primary_group": false, + "title": null, + "grant_trust_level": null, + "incoming_email": null, + "has_messages": false, + "flair_url": null, + "flair_bg_color": null, + "flair_color": null, + "bio_raw": null, + "bio_cooked": null, + "bio_excerpt": null, + "public_admission": false, + "public_exit": false, + "allow_membership_requests": false, + "full_name": null, + "default_notification_level": 3, + "membership_request_template": null, + "members_visibility_level": 0, + "can_see_members": true, + "can_admin_group": true, + "publish_read_state": false + }, + { + "id": 11, + "automatic": true, + "name": "trust_level_1", + "display_name": "trust_level_1", + "user_count": 2, + "mentionable_level": 0, + "messageable_level": 0, + "visibility_level": 1, + "primary_group": false, + "title": null, + "grant_trust_level": null, + "incoming_email": null, + "has_messages": false, + "flair_url": null, + "flair_bg_color": null, + "flair_color": null, + "bio_raw": null, + "bio_cooked": null, + "bio_excerpt": null, + "public_admission": false, + "public_exit": false, + "allow_membership_requests": false, + "full_name": null, + "default_notification_level": 3, + "membership_request_template": null, + "members_visibility_level": 0, + "can_see_members": true, + "can_admin_group": true, + "publish_read_state": false + }, + { + "id": 12, + "automatic": true, + "name": "trust_level_2", + "display_name": "trust_level_2", + "user_count": 1, + "mentionable_level": 0, + "messageable_level": 0, + "visibility_level": 1, + "primary_group": false, + "title": null, + "grant_trust_level": null, + "incoming_email": null, + "has_messages": false, + "flair_url": null, + "flair_bg_color": null, + "flair_color": null, + "bio_raw": null, + "bio_cooked": null, + "bio_excerpt": null, + "public_admission": false, + "public_exit": false, + "allow_membership_requests": false, + "full_name": null, + "default_notification_level": 3, + "membership_request_template": null, + "members_visibility_level": 0, + "can_see_members": true, + "can_admin_group": true, + "publish_read_state": false + }, + { + "id": 13, + "automatic": true, + "name": "trust_level_3", + "display_name": "trust_level_3", + "user_count": 1, + "mentionable_level": 0, + "messageable_level": 0, + "visibility_level": 1, + "primary_group": false, + "title": null, + "grant_trust_level": null, + "incoming_email": null, + "has_messages": false, + "flair_url": null, + "flair_bg_color": null, + "flair_color": null, + "bio_raw": null, + "bio_cooked": null, + "bio_excerpt": null, + "public_admission": false, + "public_exit": false, + "allow_membership_requests": false, + "full_name": null, + "default_notification_level": 3, + "membership_request_template": null, + "members_visibility_level": 0, + "can_see_members": true, + "can_admin_group": true, + "publish_read_state": false + }, + { + "id": 14, + "automatic": true, + "name": "trust_level_4", + "display_name": "trust_level_4", + "user_count": 0, + "mentionable_level": 0, + "messageable_level": 0, + "visibility_level": 1, + "primary_group": false, + "title": null, + "grant_trust_level": null, + "incoming_email": null, + "has_messages": false, + "flair_url": null, + "flair_bg_color": null, + "flair_color": null, + "bio_raw": null, + "bio_cooked": null, + "bio_excerpt": null, + "public_admission": false, + "public_exit": false, + "allow_membership_requests": false, + "full_name": null, + "default_notification_level": 3, + "membership_request_template": null, + "members_visibility_level": 0, + "can_see_members": true, + "can_admin_group": true, + "publish_read_state": false + } + ] +} diff --git a/assets/javascripts/wizard/tests/fixtures/site-settings.js.es6 b/assets/javascripts/wizard/tests/fixtures/site-settings.js.es6 new file mode 100644 index 00000000..f71ace8e --- /dev/null +++ b/assets/javascripts/wizard/tests/fixtures/site-settings.js.es6 @@ -0,0 +1,283 @@ +export default { + "default_locale": "en", + "title": "Discourse", + "short_site_description": "", + "exclude_rel_nofollow_domains": "", + "logo": "/images/discourse-logo-sketch.png", + "logo_small": "/images/discourse-logo-sketch-small.png", + "digest_logo": "", + "mobile_logo": "", + "logo_dark": "", + "logo_small_dark": "", + "mobile_logo_dark": "", + "large_icon": "", + "favicon": "", + "apple_touch_icon": "", + "display_local_time_in_user_card": false, + "allow_user_locale": false, + "set_locale_from_accept_language_header": false, + "support_mixed_text_direction": false, + "suggested_topics": 5, + "ga_universal_tracking_code": "", + "ga_universal_domain_name": "auto", + "gtm_container_id": "", + "top_menu": "categories|latest", + "post_menu": "read|like|share|flag|edit|bookmark|delete|admin|reply", + "post_menu_hidden_items": "flag|bookmark|edit|delete|admin", + "share_links": "twitter|facebook|email", + "share_quote_visibility": "anonymous", + "share_quote_buttons": "twitter|email", + "desktop_category_page_style": "categories_and_latest_topics", + "category_colors": "BF1E2E|F1592A|F7941D|9EB83B|3AB54A|12A89D|25AAE2|0E76BD|652D90|92278F|ED207B|8C6238|231F20|808281|B3B5B4|E45735", + "category_style": "bullet", + "max_category_nesting": 2, + "enable_mobile_theme": true, + "enable_direct_s3_uploads": false, + "enable_upload_debug_mode": false, + "default_dark_mode_color_scheme_id": 1, + "relative_date_duration": 30, + "fixed_category_positions": false, + "fixed_category_positions_on_create": false, + "enable_badges": true, + "enable_badge_sql": true, + "max_favorite_badges": 2, + "enable_whispers": false, + "enable_bookmarks_with_reminders": true, + "push_notifications_prompt": true, + "vapid_public_key_bytes": "4|29|219|88|202|66|198|62|182|204|66|176|229|200|131|26|141|21|178|231|150|161|2|128|228|200|179|126|118|232|196|19|232|76|108|189|54|211|210|155|55|228|173|112|38|158|114|127|18|95|7|56|110|183|192|92|43|0|243|249|233|89|9|207|255", + "invite_only": false, + "login_required": false, + "must_approve_users": false, + "enable_local_logins": true, + "enable_local_logins_via_email": true, + "allow_new_registrations": true, + "enable_signup_cta": true, + "facebook_app_id": "", + "auth_skip_create_confirm": false, + "auth_overrides_email": false, + "enable_discourse_connect": true, + "discourse_connect_overrides_avatar": false, + "hide_email_address_taken": false, + "min_username_length": 3, + "max_username_length": 20, + "unicode_usernames": false, + "min_password_length": 10, + "min_admin_password_length": 15, + "email_editable": true, + "logout_redirect": "", + "full_name_required": false, + "enable_names": true, + "invite_expiry_days": 90, + "invites_per_page": 40, + "delete_user_max_post_age": 60, + "delete_all_posts_max": 15, + "prioritize_username_in_ux": true, + "enable_user_directory": true, + "allow_anonymous_posting": false, + "anonymous_posting_min_trust_level": 1, + "allow_users_to_hide_profile": true, + "hide_user_profiles_from_public": false, + "allow_featured_topic_on_user_profiles": true, + "hide_suspension_reasons": false, + "ignored_users_count_message_threshold": 5, + "ignored_users_message_gap_days": 365, + "user_selected_primary_groups": false, + "gravatar_name": "Gravatar", + "gravatar_base_url": "www.gravatar.com", + "gravatar_login_url": "/emails", + "enable_group_directory": true, + "enable_category_group_moderation": false, + "min_post_length": 20, + "min_first_post_length": 20, + "min_personal_message_post_length": 10, + "max_post_length": 32000, + "topic_featured_link_enabled": true, + "min_topic_views_for_delete_confirm": 5000, + "min_topic_title_length": 15, + "max_topic_title_length": 255, + "enable_filtered_replies_view": false, + "min_personal_message_title_length": 2, + "allow_uncategorized_topics": true, + "min_title_similar_length": 10, + "enable_personal_messages": true, + "edit_history_visible_to_public": true, + "delete_removed_posts_after": 24, + "traditional_markdown_linebreaks": false, + "enable_markdown_typographer": true, + "enable_markdown_linkify": true, + "markdown_linkify_tlds": "com|net|org|io|onion|co|tv|ru|cn|us|uk|me|de|fr|fi|gov", + "markdown_typographer_quotation_marks": "“|”|‘|’", + "enable_rich_text_paste": true, + "suppress_reply_directly_below": true, + "suppress_reply_directly_above": true, + "max_reply_history": 1, + "enable_mentions": true, + "here_mention": "here", + "newuser_max_embedded_media": 1, + "newuser_max_attachments": 0, + "show_pinned_excerpt_mobile": true, + "show_pinned_excerpt_desktop": true, + "display_name_on_posts": false, + "show_time_gap_days": 7, + "short_progress_text_threshold": 10000, + "default_code_lang": "auto", + "autohighlight_all_code": false, + "highlighted_languages": "apache|bash|cs|cpp|css|coffeescript|diff|xml|http|ini|json|java|javascript|makefile|markdown|nginx|objectivec|ruby|perl|php|python|sql|handlebars", + "show_copy_button_on_codeblocks": false, + "enable_emoji": true, + "enable_emoji_shortcuts": true, + "emoji_set": "twitter", + "emoji_autocomplete_min_chars": 0, + "enable_inline_emoji_translation": false, + "code_formatting_style": "code-fences", + "allowed_href_schemes": "", + "watched_words_regular_expressions": false, + "enable_diffhtml_preview": false, + "enable_fast_edit": true, + "old_post_notice_days": 14, + "blur_tl0_flagged_posts_media": true, + "email_time_window_mins": 10, + "disable_digest_emails": false, + "email_in": false, + "enable_imap": false, + "enable_smtp": false, + "disable_emails": "no", + "bounce_score_threshold": 4, + "enable_secondary_emails": true, + "max_image_size_kb": 4096, + "max_attachment_size_kb": 4096, + "authorized_extensions": "jpg|jpeg|png|gif|heic|heif|webp", + "authorized_extensions_for_staff": "", + "max_image_width": 690, + "max_image_height": 500, + "prevent_anons_from_downloading_files": false, + "secure_media": false, + "enable_s3_uploads": false, + "allow_profile_backgrounds": true, + "allow_uploaded_avatars": "0", + "default_avatars": "", + "external_system_avatars_enabled": true, + "external_system_avatars_url": "/letter_avatar_proxy/v4/letter/{first_letter}/{color}/{size}.png", + "external_emoji_url": "", + "selectable_avatars_mode": "disabled", + "selectable_avatars": "", + "allow_staff_to_upload_any_file_in_pm": true, + "simultaneous_uploads": 5, + "composer_media_optimization_image_enabled": true, + "composer_media_optimization_image_bytes_optimization_threshold": 524288, + "composer_media_optimization_image_resize_dimensions_threshold": 1920, + "composer_media_optimization_image_resize_width_target": 1920, + "composer_media_optimization_image_resize_pre_multiply": false, + "composer_media_optimization_image_resize_linear_rgb": false, + "composer_media_optimization_image_encode_quality": 75, + "composer_media_optimization_debug_mode": false, + "min_trust_level_to_allow_profile_background": 0, + "min_trust_level_to_allow_user_card_background": 0, + "min_trust_level_to_allow_ignore": 2, + "tl1_requires_read_posts": 30, + "tl3_links_no_follow": false, + "enforce_second_factor": "no", + "moderators_change_post_ownership": false, + "moderators_view_emails": false, + "use_admin_ip_allowlist": false, + "allowed_iframes": "https://www.google.com/maps/embed?|https://www.openstreetmap.org/export/embed.html?|https://calendar.google.com/calendar/embed?|https://codepen.io/|https://www.instagram.com|http://localhost:3000/discobot/certificate.svg", + "can_permanently_delete": false, + "max_oneboxes_per_post": 50, + "reviewable_claiming": "disabled", + "reviewable_default_topics": false, + "reviewable_default_visibility": "low", + "alert_admins_if_errors_per_minute": 0, + "alert_admins_if_errors_per_hour": 0, + "max_prints_per_hour_per_user": 5, + "invite_link_max_redemptions_limit": 5000, + "invite_link_max_redemptions_limit_users": 10, + "enable_long_polling": true, + "enable_chunked_encoding": true, + "long_polling_base_url": "/", + "background_polling_interval": 60000, + "polling_interval": 3000, + "anon_polling_interval": 25000, + "flush_timings_secs": 60, + "verbose_localization": false, + "max_new_topics": 500, + "enable_safe_mode": true, + "tos_url": "", + "privacy_policy_url": "", + "faq_url": "", + "enable_backups": true, + "backup_location": "local", + "maximum_backups": 5, + "use_pg_headlines_for_excerpt": false, + "min_search_term_length": 3, + "log_search_queries": true, + "version_checks": true, + "suppress_uncategorized_badge": true, + "header_dropdown_category_count": 8, + "slug_generation_method": "ascii", + "summary_timeline_button": false, + "topic_views_heat_low": 1000, + "topic_views_heat_medium": 2000, + "topic_views_heat_high": 3500, + "topic_post_like_heat_low": 0.5, + "topic_post_like_heat_medium": 1, + "topic_post_like_heat_high": 2, + "history_hours_low": 12, + "history_hours_medium": 24, + "history_hours_high": 48, + "cold_age_days_low": 14, + "cold_age_days_medium": 90, + "cold_age_days_high": 180, + "global_notice": "", + "show_create_topics_notice": true, + "bootstrap_mode_min_users": 50, + "bootstrap_mode_enabled": true, + "automatically_unpin_topics": true, + "read_time_word_count": 500, + "topic_page_title_includes_category": true, + "svg_icon_subset": "", + "allow_bulk_invite": true, + "disable_mailing_list_mode": true, + "default_topics_automatic_unpin": true, + "mute_all_categories_by_default": false, + "tagging_enabled": true, + "tag_style": "simple", + "max_tags_per_topic": 5, + "max_tag_length": 20, + "min_trust_level_to_tag_topics": "0", + "max_tag_search_results": 5, + "max_tags_in_filter_list": 30, + "tags_sort_alphabetically": false, + "tags_listed_by_group": false, + "suppress_overlapping_tags_in_list": false, + "remove_muted_tags_from_latest": "always", + "force_lowercase_tags": true, + "dashboard_hidden_reports": "", + "dashboard_visible_tabs": "moderation|security|reports", + "dashboard_general_tab_activity_metrics": "page_view_total_reqs|visits|time_to_first_response|likes|flags|user_to_user_private_messages_with_replies", + "discourse_narrative_bot_enabled": true, + "details_enabled": true, + "custom_wizard_enabled": true, + "wizard_redirect_exclude_paths": "admin", + "wizard_recognised_image_upload_formats": "jpg|jpeg|png|gif", + "wizard_important_notices_on_dashboard": true, + "discourse_local_dates_email_format": "YYYY-MM-DDTHH:mm:ss[Z]", + "discourse_local_dates_enabled": true, + "discourse_local_dates_default_formats": "LLL|LTS|LL|LLLL", + "discourse_local_dates_default_timezones": "Europe/Paris|America/Los_Angeles", + "poll_enabled": true, + "poll_maximum_options": 20, + "poll_minimum_trust_level_to_create": 1, + "poll_groupable_user_fields": "", + "poll_export_data_explorer_query_id": -16, + "presence_enabled": true, + "presence_max_users_shown": 5, + "available_locales": "[{\"name\":\"اللغة العربية\",\"value\":\"ar\"},{\"name\":\"беларуская мова\",\"value\":\"be\"},{\"name\":\"български език\",\"value\":\"bg\"},{\"name\":\"bosanski jezik\",\"value\":\"bs_BA\"},{\"name\":\"català\",\"value\":\"ca\"},{\"name\":\"čeština\",\"value\":\"cs\"},{\"name\":\"dansk\",\"value\":\"da\"},{\"name\":\"Deutsch\",\"value\":\"de\"},{\"name\":\"ελληνικά\",\"value\":\"el\"},{\"name\":\"English (US)\",\"value\":\"en\"},{\"name\":\"English (UK)\",\"value\":\"en_GB\"},{\"name\":\"Español\",\"value\":\"es\"},{\"name\":\"eesti\",\"value\":\"et\"},{\"name\":\"فارسی\",\"value\":\"fa_IR\"},{\"name\":\"suomi\",\"value\":\"fi\"},{\"name\":\"Français\",\"value\":\"fr\"},{\"name\":\"galego\",\"value\":\"gl\"},{\"name\":\"עברית\",\"value\":\"he\"},{\"name\":\"magyar\",\"value\":\"hu\"},{\"name\":\"Հայերեն\",\"value\":\"hy\"},{\"name\":\"Indonesian\",\"value\":\"id\"},{\"name\":\"Italiano\",\"value\":\"it\"},{\"name\":\"日本語\",\"value\":\"ja\"},{\"name\":\"한국어\",\"value\":\"ko\"},{\"name\":\"lietuvių kalba\",\"value\":\"lt\"},{\"name\":\"latviešu valoda\",\"value\":\"lv\"},{\"name\":\"Norsk bokmål\",\"value\":\"nb_NO\"},{\"name\":\"Nederlands\",\"value\":\"nl\"},{\"name\":\"polski\",\"value\":\"pl_PL\"},{\"name\":\"Português\",\"value\":\"pt\"},{\"name\":\"Português (BR)\",\"value\":\"pt_BR\"},{\"name\":\"limba română\",\"value\":\"ro\"},{\"name\":\"Русский\",\"value\":\"ru\"},{\"name\":\"slovenčina\",\"value\":\"sk\"},{\"name\":\"slovenščina\",\"value\":\"sl\"},{\"name\":\"Shqip\",\"value\":\"sq\"},{\"name\":\"српски језик\",\"value\":\"sr\"},{\"name\":\"svenska\",\"value\":\"sv\"},{\"name\":\"Kiswahili\",\"value\":\"sw\"},{\"name\":\"తెలుగు\",\"value\":\"te\"},{\"name\":\"ไทย\",\"value\":\"th\"},{\"name\":\"Türkçe\",\"value\":\"tr_TR\"},{\"name\":\"українська мова\",\"value\":\"uk\"},{\"name\":\"اردو\",\"value\":\"ur\"},{\"name\":\"Việt Nam\",\"value\":\"vi\"},{\"name\":\"简体中文\",\"value\":\"zh_CN\"},{\"name\":\"繁體中文\",\"value\":\"zh_TW\"}]", + "require_invite_code": false, + "site_logo_url": "http://localhost:3000/images/discourse-logo-sketch.png", + "site_logo_small_url": "http://localhost:3000/images/discourse-logo-sketch-small.png", + "site_mobile_logo_url": "http://localhost:3000/images/discourse-logo-sketch.png", + "site_favicon_url": "http://localhost:3000/uploads/default/optimized/1X/_129430568242d1b7f853bb13ebea28b3f6af4e7_2_32x32.png", + "site_logo_dark_url": "", + "site_logo_small_dark_url": "", + "site_mobile_logo_dark_url": "" +} diff --git a/assets/javascripts/wizard/tests/fixtures/tags.js.es6 b/assets/javascripts/wizard/tests/fixtures/tags.js.es6 new file mode 100644 index 00000000..a072772e --- /dev/null +++ b/assets/javascripts/wizard/tests/fixtures/tags.js.es6 @@ -0,0 +1,22 @@ +export default { + "tags": [ + { + "id": "tag1", + "text": "tag1", + "name": "tag1", + "description": null, + "count": 1, + "pm_count": 0, + "target_tag": null + }, + { + "id": "tag2", + "text": "tag2", + "name": "tag2", + "description": null, + "count": 1, + "pm_count": 0, + "target_tag": null + } + ] +} diff --git a/assets/javascripts/wizard/tests/fixtures/update.js.es6 b/assets/javascripts/wizard/tests/fixtures/update.js.es6 new file mode 100644 index 00000000..3908525c --- /dev/null +++ b/assets/javascripts/wizard/tests/fixtures/update.js.es6 @@ -0,0 +1,5 @@ +export default { + "final": false, + "next_step_id": "step_2", + "wizard": {} +} diff --git a/assets/javascripts/wizard/tests/fixtures/user.js.es6 b/assets/javascripts/wizard/tests/fixtures/user.js.es6 new file mode 100644 index 00000000..8acd7392 --- /dev/null +++ b/assets/javascripts/wizard/tests/fixtures/user.js.es6 @@ -0,0 +1,34 @@ +export default { + id: 19, + username: "angus", + uploaded_avatar_id: 5275, + avatar_template: "/user_avatar/localhost/angus/{size}/5275.png", + name: "Angus McLeod", + unread_notifications: 0, + unread_private_messages: 0, + unread_high_priority_notifications: 0, + admin: true, + notification_channel_position: null, + site_flagged_posts_count: 1, + moderator: true, + staff: true, + can_create_group: true, + title: "", + reply_count: 859, + topic_count: 36, + enable_quoting: true, + external_links_in_new_tab: false, + dynamic_favicon: true, + trust_level: 4, + can_edit: true, + can_invite_to_forum: true, + should_be_redirected_to_top: false, + custom_fields: {}, + muted_category_ids: [], + dismissed_banner_key: null, + akismet_review_count: 0, + title_count_mode: "notifications", + timezone: "Australia/Perth", + skip_new_user_tips: false, + can_review: true +} diff --git a/assets/javascripts/wizard/tests/fixtures/users.js.es6 b/assets/javascripts/wizard/tests/fixtures/users.js.es6 new file mode 100644 index 00000000..267d4909 --- /dev/null +++ b/assets/javascripts/wizard/tests/fixtures/users.js.es6 @@ -0,0 +1,14 @@ +export default { + "users": [ + { + "username": "angus", + "name": "Angus", + "avatar_template": "/user_avatar/localhost/angus/{size}/12_2.png" + }, + { + "username": "angus_2", + "name": "Angus 2", + "avatar_template": "/letter_avatar_proxy/v4/letter/a/e9a140/{size}.png" + } + ] +} diff --git a/assets/javascripts/wizard/tests/fixtures/wizard.js.es6 b/assets/javascripts/wizard/tests/fixtures/wizard.js.es6 new file mode 100644 index 00000000..be4fa8b2 --- /dev/null +++ b/assets/javascripts/wizard/tests/fixtures/wizard.js.es6 @@ -0,0 +1,469 @@ +export default { + "id": "wizard", + "name": "Wizard", + "start": "step_1", + "background": "#333333", + "submission_last_updated_at": "2022-03-15T21:11:01+01:00", + "theme_id": 2, + "required": false, + "permitted": true, + "uncategorized_category_id": 1, + "categories": [], + "subscribed": false, + "resume_on_revisit": false, + "steps": [ + { + "id": "step_1", + "index": 0, + "next": "step_2", + "description": "

Text inputs!

", + "title": "Text", + "permitted": true, + "permitted_message": null, + "final": false, + "fields": [ + { + "id": "step_1_field_1", + "index": 0, + "type": "text", + "required": false, + "value": "I am prefilled", + "label": "

Text

", + "description": "Text field description.", + "file_types": null, + "format": null, + "limit": null, + "property": null, + "content": null, + "validations": {}, + "max_length": null, + "char_counter": null, + "preview_template": null, + "tabindex": 1, + "wizardId": "super_mega_fun_wizard", + "stepId": "step_1", + "_validState": 0 + }, + { + "id": "step_1_field_2", + "index": 0, + "type": "textarea", + "required": false, + "value": "", + "label": "

Textarea

", + "file_types": null, + "format": null, + "limit": null, + "property": null, + "content": null, + "validations": {}, + "max_length": null, + "char_counter": null, + "preview_template": null, + "tabindex": 2, + "wizardId": "super_mega_fun_wizard", + "stepId": "step_1", + "_validState": 0 + }, + { + "id": "step_1_field_3", + "index": 2, + "type": "composer", + "required": false, + "value": "", + "label": "

Composer

", + "file_types": null, + "format": null, + "limit": null, + "property": null, + "content": null, + "validations": {}, + "max_length": null, + "char_counter": null, + "preview_template": null, + "tabindex": 3, + "wizardId": "super_mega_fun_wizard", + "stepId": "step_1", + "_validState": 0 + }, + { + "id": "step_1_field_4", + "index": 3, + "type": "text_only", + "required": false, + "value": null, + "label": "

I’m only text

", + "file_types": null, + "format": null, + "limit": null, + "property": null, + "content": null, + "validations": {}, + "max_length": null, + "char_counter": null, + "preview_template": null, + "tabindex": 4, + "wizardId": "super_mega_fun_wizard", + "stepId": "step_1", + "_validState": 0 + }, + { + "id": "step_1_field_5", + "index": 4, + "type": "composer_preview", + "required": false, + "value": "", + "label": "

I’m a preview

", + "file_types": null, + "format": null, + "limit": null, + "property": null, + "content": null, + "validations": {}, + "max_length": null, + "char_counter": null, + "preview_template": "

I am prefilled

", + "tabindex": 5, + "wizardId": "super_mega_fun_wizard", + "stepId": "step_1", + "_validState": 0 + }, + { + "id": "step_1_field_6", + "index": 5, + "type": "composer_preview", + "required": false, + "value": "", + "file_types": null, + "format": null, + "limit": null, + "property": null, + "content": null, + "validations": {}, + "max_length": null, + "char_counter": null, + "preview_template": "

This is the preview of the composer

", + "tabindex": 6, + "wizardId": "super_mega_fun_wizard", + "stepId": "step_1", + "_validState": 0 + } + ], + "_validState": 0, + "wizardId": "super_mega_fun_wizard" + }, + { + "id": "step_2", + "index": 1, + "next": "step_3", + "previous": "step_1", + "description": "

Because I couldn’t think of another name for this step \":slight_smile:\"

", + "title": "Values", + "permitted": true, + "permitted_message": null, + "final": false, + "fields": [ + { + "id": "step_2_field_1", + "index": 0, + "type": "date", + "required": false, + "value": "", + "label": "

Date

", + "file_types": null, + "format": "YYYY-MM-DD", + "limit": null, + "property": null, + "content": null, + "validations": {}, + "max_length": null, + "char_counter": null, + "preview_template": null, + "tabindex": 1, + "wizardId": "super_mega_fun_wizard", + "stepId": "step_2", + "_validState": 0 + }, + { + "id": "step_2_field_2", + "index": 0, + "type": "time", + "required": false, + "value": "", + "label": "

Time

", + "file_types": null, + "format": "HH:mm", + "limit": null, + "property": null, + "content": null, + "validations": {}, + "max_length": null, + "char_counter": null, + "preview_template": null, + "tabindex": 2, + "wizardId": "super_mega_fun_wizard", + "stepId": "step_2", + "_validState": 0 + }, + { + "id": "step_2_field_3", + "index": 2, + "type": "date_time", + "required": false, + "value": "", + "label": "

Date & Time

", + "file_types": null, + "format": "", + "limit": null, + "property": null, + "content": null, + "validations": {}, + "max_length": null, + "char_counter": null, + "preview_template": null, + "tabindex": 3, + "wizardId": "super_mega_fun_wizard", + "stepId": "step_2", + "_validState": 0 + }, + { + "id": "step_2_field_4", + "index": 3, + "type": "number", + "required": false, + "value": "", + "label": "

Number

", + "file_types": null, + "format": null, + "limit": null, + "property": null, + "content": null, + "validations": {}, + "max_length": null, + "char_counter": null, + "preview_template": null, + "tabindex": 5, + "wizardId": "super_mega_fun_wizard", + "stepId": "step_2", + "_validState": 0 + }, + { + "id": "step_2_field_5", + "index": 4, + "type": "checkbox", + "required": false, + "value": false, + "label": "

Checkbox

", + "file_types": null, + "format": null, + "limit": null, + "property": null, + "content": null, + "validations": {}, + "max_length": null, + "char_counter": null, + "preview_template": null, + "tabindex": 6, + "wizardId": "super_mega_fun_wizard", + "stepId": "step_2", + "_validState": 0 + }, + { + "id": "step_2_field_6", + "index": 5, + "type": "url", + "required": false, + "value": "", + "label": "

Url

", + "file_types": null, + "format": null, + "limit": null, + "property": null, + "content": null, + "validations": {}, + "max_length": null, + "char_counter": null, + "preview_template": null, + "tabindex": 7, + "wizardId": "super_mega_fun_wizard", + "stepId": "step_2", + "_validState": 0 + }, + { + "id": "step_2_field_7", + "index": 6, + "type": "upload", + "required": false, + "value": "", + "label": "

Upload

", + "file_types": ".jpg,.jpeg,.png", + "format": null, + "limit": null, + "property": null, + "content": null, + "validations": {}, + "max_length": null, + "char_counter": null, + "preview_template": null, + "tabindex": 8, + "wizardId": "super_mega_fun_wizard", + "stepId": "step_2", + "_validState": 0 + } + ], + "_validState": 0, + "wizardId": "super_mega_fun_wizard" + }, + { + "id": "step_3", + "index": 2, + "previous": "step_2", + "description": "

Unfortunately not the edible type \":sushi:\"

", + "title": "Combo-boxes", + "permitted": true, + "permitted_message": null, + "final": true, + "fields": [ + { + "id": "step_3_field_1", + "index": 0, + "type": "dropdown", + "required": false, + "value": "choice1", + "label": "

Custom Dropdown

", + "file_types": null, + "format": null, + "limit": null, + "property": null, + "content": [ + { + "id": "one", + "name": "One" + }, + { + "id": "two", + "name": "Two" + } + ], + "validations": {}, + "max_length": null, + "char_counter": null, + "preview_template": null, + "tabindex": 1, + "wizardId": "super_mega_fun_wizard", + "stepId": "step_3", + "_validState": 0 + }, + { + "id": "step_3_field_2", + "index": 0, + "type": "tag", + "required": false, + "value": null, + "label": "

Tag

", + "file_types": null, + "format": null, + "limit": null, + "property": null, + "content": null, + "validations": {}, + "max_length": null, + "char_counter": null, + "preview_template": null, + "tabindex": 2, + "wizardId": "super_mega_fun_wizard", + "stepId": "step_3", + "_validState": 0 + }, + { + "id": "step_3_field_3", + "index": 2, + "type": "category", + "required": false, + "value": null, + "label": "

Category

", + "file_types": null, + "format": null, + "limit": 1, + "property": "id", + "content": null, + "validations": {}, + "max_length": null, + "char_counter": null, + "preview_template": null, + "tabindex": 3, + "wizardId": "super_mega_fun_wizard", + "stepId": "step_3", + "_validState": 0 + }, + { + "id": "step_3_field_4", + "index": 3, + "type": "group", + "required": false, + "value": null, + "label": "

Group

", + "file_types": null, + "format": null, + "limit": null, + "property": null, + "content": null, + "validations": {}, + "max_length": null, + "char_counter": null, + "preview_template": null, + "tabindex": 4, + "wizardId": "super_mega_fun_wizard", + "stepId": "step_3", + "_validState": 0 + }, + { + "id": "step_3_field_5", + "index": 4, + "type": "user_selector", + "required": false, + "value": null, + "label": "

User Selector

", + "file_types": null, + "format": null, + "limit": null, + "property": null, + "content": null, + "validations": {}, + "max_length": null, + "char_counter": null, + "preview_template": null, + "tabindex": 5, + "wizardId": "super_mega_fun_wizard", + "stepId": "step_3", + "_validState": 0 + }, + { + "id": "step_3_field_6", + "index": 5, + "type": "user_selector", + "required": false, + "value": null, + "label": "

Conditional User Selector

", + "description": "Shown when checkbox in step_2_field_5 is true", + "file_types": null, + "format": null, + "limit": null, + "property": null, + "content": null, + "validations": {}, + "max_length": null, + "char_counter": null, + "preview_template": null, + "tabindex": 6, + "wizardId": "super_mega_fun_wizard", + "stepId": "step_3", + "_validState": 0 + } + ], + "_validState": 0, + "wizardId": "super_mega_fun_wizard" + } + ], + "groups": [] +} diff --git a/assets/javascripts/wizard/tests/helpers/acceptance.js.es6 b/assets/javascripts/wizard/tests/helpers/acceptance.js.es6 new file mode 100644 index 00000000..f5c1175f --- /dev/null +++ b/assets/javascripts/wizard/tests/helpers/acceptance.js.es6 @@ -0,0 +1,53 @@ +import { module } from "qunit"; +import setupPretender, { response } from "../pretender"; +import startApp from "../helpers/start-app"; + +let server; +let app; + +function acceptance(name, requests, cb) { + module(`Acceptance: ${name}`, function(hooks) { + hooks.beforeEach(function() { + server = setupPretender(function(pretender) { + requests.forEach(req => { + pretender[req.verb](req.path, () => (response(req.status, req.response))); + }); + return pretender; + }); + app = startApp(); + }); + + hooks.afterEach(function() { + app.destroy(); + server.shutdown(); + }); + + cb(hooks); + }); +} + +export default acceptance; + +export { + server +}; + +// The discourse/test/helpers/qunit-helpers file has many functions and imports +// we don't need, so there will be some duplciation here. + +export function queryAll(selector, context) { + context = context || "#ember-testing"; + return $(selector, context); +} + +export function query() { + return document.querySelector("#ember-testing").querySelector(...arguments); +} + +export function visible(selector) { + return queryAll(selector + ":visible").length > 0; +} + +export function count(selector) { + return queryAll(selector).length; +} diff --git a/assets/javascripts/wizard/tests/helpers/start-app.js.es6 b/assets/javascripts/wizard/tests/helpers/start-app.js.es6 new file mode 100644 index 00000000..6afe6eb9 --- /dev/null +++ b/assets/javascripts/wizard/tests/helpers/start-app.js.es6 @@ -0,0 +1,19 @@ +const CustomWizard = requirejs("discourse/plugins/discourse-custom-wizard/wizard/application").default; +const initializer = requirejs("discourse/plugins/discourse-custom-wizard/wizard/lib/initialize/wizard").default; +const siteSettings = requirejs("discourse/plugins/discourse-custom-wizard/wizard/tests/fixtures/site-settings").default; +const { cloneJSON } = requirejs("discourse-common/lib/object").default; + +let app; + +export default function () { + app = CustomWizard.create({ rootElement: "#ember-testing" }); + app.start(); + + app.SiteSettings = cloneJSON(siteSettings); + initializer.initialize(app); + + app.setupForTesting(); + app.injectTestHelpers(); + + return app; +} diff --git a/assets/javascripts/wizard/tests/helpers/step.js.es6 b/assets/javascripts/wizard/tests/helpers/step.js.es6 new file mode 100644 index 00000000..a24e04e1 --- /dev/null +++ b/assets/javascripts/wizard/tests/helpers/step.js.es6 @@ -0,0 +1,20 @@ +import updateJson from "../fixtures/update"; +import { cloneJSON } from "discourse-common/lib/object"; +import wizardJson from "../fixtures/wizard"; + +const update = cloneJSON(updateJson); +update.wizard = cloneJSON(wizardJson); + +const saveStep = function(response) { + return { + verb: "put", + path: '/w/wizard/steps/:step_id', + status: 200, + response + } +} + +export { + saveStep, + update +} diff --git a/assets/javascripts/wizard/tests/helpers/test.js.es6 b/assets/javascripts/wizard/tests/helpers/test.js.es6 new file mode 100644 index 00000000..c7401fd5 --- /dev/null +++ b/assets/javascripts/wizard/tests/helpers/test.js.es6 @@ -0,0 +1,7 @@ +function exists(selector) { + return document.querySelector(selector) !== null; +} + +export { + exists +} diff --git a/assets/javascripts/wizard/tests/helpers/wizard.js.es6 b/assets/javascripts/wizard/tests/helpers/wizard.js.es6 new file mode 100644 index 00000000..997f6c36 --- /dev/null +++ b/assets/javascripts/wizard/tests/helpers/wizard.js.es6 @@ -0,0 +1,52 @@ +import wizardJson from "../fixtures/wizard"; +import userJson from "../fixtures/user"; +import categoriesJson from "../fixtures/categories"; +import groupsJson from "../fixtures/groups"; +import { cloneJSON } from "discourse-common/lib/object"; + +const wizardNoUser = cloneJSON(wizardJson); +const wizard = cloneJSON(wizardJson); +wizard.user = cloneJSON(userJson); + +const wizardNotPermitted = cloneJSON(wizard); +wizardNotPermitted.permitted = false; + +const wizardCompleted = cloneJSON(wizard); +wizardCompleted.completed = true; + +wizard.start = "step_1"; +wizard.resume_on_revisit = false; +wizard.submission_last_updated_at = "2022-03-11T20:00:18+01:00"; +wizard.subscribed = false; + +const stepNotPermitted = cloneJSON(wizard); +stepNotPermitted.steps[0].permitted = false; + +const allFieldsWizard = cloneJSON(wizard); +allFieldsWizard.steps[0].fields = [ + ...allFieldsWizard.steps[0].fields, + ...allFieldsWizard.steps[1].fields, + ...allFieldsWizard.steps[2].fields +]; +allFieldsWizard.steps = [cloneJSON(allFieldsWizard.steps[0])]; +allFieldsWizard.categories = cloneJSON(categoriesJson['categories']); +allFieldsWizard.groups = cloneJSON(groupsJson['groups']); + +const getWizard = function(response) { + return { + verb: "get", + path: "/w/wizard", + status: 200, + response + } +} + +export { + getWizard, + wizardNoUser, + wizardNotPermitted, + wizardCompleted, + stepNotPermitted, + allFieldsWizard, + wizard +} diff --git a/views/wizard-pretender.js b/assets/javascripts/wizard/tests/pretender.js.es6 similarity index 56% rename from views/wizard-pretender.js rename to assets/javascripts/wizard/tests/pretender.js.es6 index ac0ef1e8..88eae666 100644 --- a/views/wizard-pretender.js +++ b/assets/javascripts/wizard/tests/pretender.js.es6 @@ -1,8 +1,4 @@ import Pretender from "pretender"; -import WizardJson from "./fixtures/wizard"; - -// TODO: This file has some copied and pasted functions from `create-pretender` - would be good -// to centralize that code at some point. function parsePostData(query) { const result = {}; @@ -30,24 +26,14 @@ function response(code, obj) { return [code, { "Content-Type": "application/json" }, obj]; } -export default function () { - const server = new Pretender(function () { - this.get("/w/wizard.json", () => { - return response(200, cloneJSON(WizardJson); - }); +export { response }; - this.put("/wizard/steps/:id", (request) => { - const body = parsePostData(request.requestBody); +export default function (cb) { + let server = new Pretender(); - if (body.fields.full_name === "Server Fail") { - return response(422, { - errors: [{ field: "full_name", description: "Invalid name" }], - }); - } else { - return response(200, { success: true }); - } - }); - }); + if (cb) { + server = cb(server); + } server.prepareBody = function (body) { if (body && typeof body === "object") { @@ -56,7 +42,7 @@ export default function () { return body; }; - server.unhandledRequest = function (verb, path) { + server.unhandledRequest = function (verb, path, request) { const error = "Unhandled request in test environment: " + path + " (" + verb + ")"; window.console.error(error); diff --git a/assets/stylesheets/wizard/custom/composer.scss b/assets/stylesheets/wizard/custom/composer.scss index be866b8a..aea17d63 100644 --- a/assets/stylesheets/wizard/custom/composer.scss +++ b/assets/stylesheets/wizard/custom/composer.scss @@ -46,7 +46,7 @@ position: relative; } -.wizard-field-composer.show-preview .d-editor-textarea-wrapper { +.wizard-field-composer.show-preview .d-editor-textarea-column { display: none; } diff --git a/config/routes.rb b/config/routes.rb index 3b5f8ca6..514605de 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,5 +1,6 @@ # frozen_string_literal: true CustomWizard::Engine.routes.draw do + get 'qunit' => 'wizard#qunit' get ':wizard_id' => 'wizard#index' put ':wizard_id/skip' => 'wizard#skip' get ':wizard_id/steps' => 'wizard#index' diff --git a/controllers/custom_wizard/wizard.rb b/controllers/custom_wizard/wizard.rb index 1e20adf9..d877f035 100644 --- a/controllers/custom_wizard/wizard.rb +++ b/controllers/custom_wizard/wizard.rb @@ -1,55 +1,40 @@ # frozen_string_literal: true -class CustomWizard::WizardController < ::ApplicationController - include ApplicationHelper - prepend_view_path(Rails.root.join('plugins', 'discourse-custom-wizard', 'views')) - layout 'wizard' +class CustomWizard::WizardController < ::ActionController::Base + helper ApplicationHelper + include CurrentUser + include CanonicalURL::ControllerExtensions + include GlobalPath + + prepend_view_path(Rails.root.join('plugins', 'discourse-custom-wizard', 'views')) + layout :set_wizard_layout + + before_action :preload_wizard_json before_action :ensure_plugin_enabled before_action :update_subscription, only: [:index] before_action :ensure_logged_in, only: [:skip] + helper_method :wizard_page_title helper_method :wizard_theme_id helper_method :wizard_theme_lookup helper_method :wizard_theme_translations_lookup - def wizard - @builder = CustomWizard::Builder.new(params[:wizard_id].underscore, current_user) - @wizard ||= @builder.build - @wizard - end - - def wizard_page_title - wizard ? (wizard.name || wizard.id) : I18n.t('wizard.custom_title') - end - - def wizard_theme_id - wizard ? wizard.theme_id : nil - end - - def wizard_theme_lookup(name) - Theme.lookup_field(wizard_theme_id, mobile_view? ? :mobile : :desktop, name) - end - - def wizard_theme_translations_lookup - Theme.lookup_field(wizard_theme_id, :translations, I18n.locale) + def set_wizard_layout + action_name === 'qunit' ? 'qunit' : 'wizard' end def index respond_to do |format| format.json do - builder = CustomWizard::Builder.new(params[:wizard_id].underscore, current_user) - - if builder.wizard.present? - builder_opts = {} - builder_opts[:reset] = params[:reset] - built_wizard = builder.build(builder_opts, params) - - render_serialized(built_wizard, ::CustomWizard::WizardSerializer, root: false) + if wizard.present? + render json: CustomWizard::WizardSerializer.new(wizard, scope: guardian, root: false).as_json, status: 200 else render json: { error: I18n.t('wizard.none') } end end - format.html {} + format.html do + render "default/empty" + end end end @@ -61,9 +46,8 @@ class CustomWizard::WizardController < ::ApplicationController end result = success_json - user = current_user - if user && wizard.can_access? + if current_user && wizard.can_access? submission = wizard.current_submission if submission.present? && submission.redirect_to @@ -77,6 +61,64 @@ class CustomWizard::WizardController < ::ApplicationController render json: result end + def qunit + raise Discourse::InvalidAccess.new if Rails.env.production? + + respond_to do |format| + format.html do + render "default/empty" + end + end + end + + protected + + def ensure_logged_in + raise Discourse::NotLoggedIn.new unless current_user.present? + end + + def guardian + @guardian ||= Guardian.new(current_user, request) + end + + def wizard + @wizard ||= begin + builder = CustomWizard::Builder.new(params[:wizard_id].underscore, current_user) + return nil unless builder.present? + opts = {} + opts[:reset] = params[:reset] + builder.build(opts, params) + end + end + + def wizard_page_title + wizard ? (wizard.name || wizard.id) : I18n.t('wizard.custom_title') + end + + def wizard_theme_id + wizard ? wizard.theme_id : nil + end + + def wizard_theme_lookup(name) + Theme.lookup_field(wizard_theme_id, view_context.mobile_view? ? :mobile : :desktop, name) + end + + def wizard_theme_translations_lookup + Theme.lookup_field(wizard_theme_id, :translations, I18n.locale) + end + + def preload_wizard_json + return if request.xhr? || request.format.json? + return if request.method != "GET" + + store_preloaded("siteSettings", SiteSetting.client_settings_json) + end + + def store_preloaded(key, json) + @preloaded ||= {} + @preloaded[key] = json.gsub(" (app = CustomWizard.create({ rootElement: "#ember-testing" }))); - - if (!started) { - wizardInitializer.initialize(app); - stepInitializer.initialize(app); - fieldInitializer.initialize(app); - app.start(); - started = true; - } - app.setupForTesting(); - app.injectTestHelpers(); - return app; -} diff --git a/test/javascripts/plugin_helper.js b/test/javascripts/plugin_helper.js deleted file mode 100644 index 9b1558cd..00000000 --- a/test/javascripts/plugin_helper.js +++ /dev/null @@ -1,70 +0,0 @@ -// discourse-skip-module -/*global document, Logster, QUnit */ -console.log('starting test_helper') -window.Discourse = {}; -window.Wizard = {}; -Wizard.SiteSettings = {}; -Discourse.__widget_helpers = {}; -Discourse.SiteSettings = Wizard.SiteSettings; - -//= require env -//= require jquery.debug -//= require ember.debug -//= require locales/i18n -//= require locales/en -//= require route-recognizer -//= require fake_xml_http_request -//= require pretender -//= require qunit -//= require ember-qunit -//= require discourse-loader -//= require jquery.debug -//= require handlebars -//= require ember-template-compiler -//= require wizard-custom -//= require wizard-vendor -//= require_tree ./helpers -//= require_tree ./acceptance -//= require_tree ./models -//= require_tree ./components -//= require ./wizard-pretender -//= require test-shims -console.log ("end of require") -document.addEventListener("DOMContentLoaded", function () { - document.body.insertAdjacentHTML( - "afterbegin", - ` -
- - ` - ); -}); - -if (window.Logster) { - Logster.enabled = false; -} else { - window.Logster = { enabled: false }; -} -Ember.Test.adapter = window.QUnitAdapter.create(); - -/*let createPretendServer = requirejs("./wizard-pretender", null, null, false).default; - -let server; -QUnit.testStart(function () { - server = createPretendServer(); -}); - -QUnit.testDone(function () { - server.shutdown(); -});*/ - -let _testApp = requirejs("./helpers/start-app").default(); -let _buildResolver = requirejs("discourse-common/resolver").buildResolver; -window.setResolver(_buildResolver("wizard").create({ namespace: _testApp })); - -Object.keys(requirejs.entries).forEach(function (entry) { - if (/\-test/.test(entry)) { - requirejs(entry, null, null, true); - } -}); -console.log ("end of helper") diff --git a/views/layouts/qunit.html.erb b/views/layouts/qunit.html.erb new file mode 100644 index 00000000..c2f5fb5e --- /dev/null +++ b/views/layouts/qunit.html.erb @@ -0,0 +1,28 @@ + + + + Custom Wizard QUnit Test Runner + <%= discourse_stylesheet_link_tag(:test_helper, theme_id: nil) %> + <%= discourse_stylesheet_link_tag :wizard, theme_id: nil %> + <%= discourse_stylesheet_link_tag :wizard_custom %> + <%= preload_script "locales/en" %> + <%= preload_script "ember_jquery" %> + <%= preload_script "wizard-vendor" %> + <%= preload_script "wizard-custom" %> + <%= preload_script "wizard-raw-templates" %> + <%= preload_script "wizard-plugin" %> + <%= preload_script "pretty-text-bundle" %> + <%= preload_script "wizard-qunit" %> + <%= csrf_meta_tags %> + + + + <%= tag.meta id: 'data-discourse-setup', data: client_side_setup_data %> + + + + +
+
+ + diff --git a/views/layouts/wizard.html.erb b/views/layouts/wizard.html.erb index 16d119b7..f909ae84 100644 --- a/views/layouts/wizard.html.erb +++ b/views/layouts/wizard.html.erb @@ -11,10 +11,8 @@ <%= preload_script "locales/#{I18n.locale}" %> <%= preload_script "ember_jquery" %> <%= preload_script "wizard-vendor" %> - <%= preload_script "wizard-application" %> - <%= preload_script "wizard-custom-globals" %> - <%= preload_script "wizard-raw-templates" %> <%= preload_script "wizard-custom" %> + <%= preload_script "wizard-raw-templates" %> <%= preload_script "wizard-plugin" %> <%= preload_script "pretty-text-bundle" %> @@ -58,5 +56,7 @@ <%= raw SvgSprite.bundle %>
+ + From 835600c05498eed68a8f80b33c282aeaee4c8644 Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Wed, 16 Mar 2022 12:46:16 +0100 Subject: [PATCH 107/160] Fix linting --- .../initializers/custom-wizard-edits.js.es6 | 2 +- .../components/custom-user-selector.js.es6 | 2 +- .../wizard/components/field-validators.js.es6 | 2 +- .../similar-topics-validator.js.es6 | 2 +- .../wizard/components/validator.js.es6 | 2 +- .../components/wizard-composer-editor.js.es6 | 2 +- .../wizard-composer-hyperlink.js.es6 | 2 +- .../components/wizard-date-input.js.es6 | 2 +- .../components/wizard-date-time-input.js.es6 | 2 +- .../components/wizard-field-category.js.es6 | 2 +- .../components/wizard-field-checkbox.js.es6 | 2 +- .../wizard-field-composer-preview.js.es6 | 2 +- .../components/wizard-field-composer.js.es6 | 2 +- .../components/wizard-field-date-time.js.es6 | 2 +- .../components/wizard-field-date.js.es6 | 2 +- .../components/wizard-field-dropdown.js.es6 | 2 +- .../components/wizard-field-group.js.es6 | 2 +- .../components/wizard-field-number.js.es6 | 2 +- .../wizard/components/wizard-field-tag.js.es6 | 2 +- .../components/wizard-field-text.js.es6 | 2 +- .../components/wizard-field-textarea.js.es6 | 2 +- .../components/wizard-field-time.js.es6 | 2 +- .../components/wizard-field-upload.js.es6 | 2 +- .../wizard/components/wizard-field-url.js.es6 | 2 +- .../wizard-field-user-selector.js.es6 | 2 +- .../wizard/components/wizard-field.js.es6 | 9 +- .../components/wizard-group-selector.js.es6 | 2 +- .../wizard/components/wizard-no-access.js.es6 | 8 +- .../components/wizard-similar-topics.js.es6 | 2 +- .../wizard/components/wizard-step.js.es6 | 4 +- .../components/wizard-time-input.js.es6 | 2 +- .../wizard/controllers/wizard-index.js.es6 | 14 +- .../lib/initialize/create-contexts.js.es6 | 6 +- .../lib/initialize/inject-objects.js.es6 | 31 +- .../lib/initialize/patch-components.js.es6 | 8 +- .../lib/initialize/register-files.js.es6 | 14 +- .../wizard/lib/initialize/wizard.js.es6 | 29 +- assets/javascripts/wizard/models/field.js.es6 | 2 +- assets/javascripts/wizard/models/step.js.es6 | 6 +- assets/javascripts/wizard/routes/step.js.es6 | 2 +- .../wizard/routes/wizard-index.js.es6 | 12 +- .../javascripts/wizard/routes/wizard.js.es6 | 5 +- .../wizard/tests/acceptance/field-test.js.es6 | 252 +++--- .../wizard/tests/acceptance/step-test.js.es6 | 78 +- .../tests/acceptance/wizard-test.js.es6 | 105 +-- .../wizard/tests/fixtures/categories.js.es6 | 400 +++++---- .../wizard/tests/fixtures/groups.js.es6 | 586 ++++++------ .../tests/fixtures/site-settings.js.es6 | 575 ++++++------ .../wizard/tests/fixtures/tags.js.es6 | 36 +- .../wizard/tests/fixtures/update.js.es6 | 8 +- .../wizard/tests/fixtures/user.js.es6 | 4 +- .../wizard/tests/fixtures/users.js.es6 | 20 +- .../wizard/tests/fixtures/wizard.js.es6 | 842 +++++++++--------- .../wizard/tests/helpers/acceptance.js.es6 | 18 +- .../wizard/tests/helpers/start-app.js.es6 | 12 +- .../wizard/tests/helpers/step.js.es6 | 15 +- .../wizard/tests/helpers/test.js.es6 | 4 +- .../wizard/tests/helpers/wizard.js.es6 | 18 +- .../javascripts/wizard/tests/pretender.js.es6 | 20 +- 59 files changed, 1635 insertions(+), 1564 deletions(-) diff --git a/assets/javascripts/discourse/initializers/custom-wizard-edits.js.es6 b/assets/javascripts/discourse/initializers/custom-wizard-edits.js.es6 index 035fa6f2..6a027dee 100644 --- a/assets/javascripts/discourse/initializers/custom-wizard-edits.js.es6 +++ b/assets/javascripts/discourse/initializers/custom-wizard-edits.js.es6 @@ -64,7 +64,7 @@ export default { this.set("customWizardCriticalNotices", criticalNotices); } }); - } + }, }); api.modifyClass("component:d-navigation", { diff --git a/assets/javascripts/wizard/components/custom-user-selector.js.es6 b/assets/javascripts/wizard/components/custom-user-selector.js.es6 index 1698a008..c53c3bf4 100644 --- a/assets/javascripts/wizard/components/custom-user-selector.js.es6 +++ b/assets/javascripts/wizard/components/custom-user-selector.js.es6 @@ -1,6 +1,6 @@ import { default as computed, - observes + observes, } from "discourse-common/utils/decorators"; import { renderAvatar } from "discourse/helpers/user-avatar"; import userSearch from "../lib/user-search"; diff --git a/assets/javascripts/wizard/components/field-validators.js.es6 b/assets/javascripts/wizard/components/field-validators.js.es6 index 15cfc181..7284241c 100644 --- a/assets/javascripts/wizard/components/field-validators.js.es6 +++ b/assets/javascripts/wizard/components/field-validators.js.es6 @@ -1,7 +1,7 @@ import Component from "@ember/component"; export default Component.extend({ - layoutName: 'wizard/templates/components/field-validators', + layoutName: "wizard/templates/components/field-validators", actions: { perform() { diff --git a/assets/javascripts/wizard/components/similar-topics-validator.js.es6 b/assets/javascripts/wizard/components/similar-topics-validator.js.es6 index e5133d4f..4f722123 100644 --- a/assets/javascripts/wizard/components/similar-topics-validator.js.es6 +++ b/assets/javascripts/wizard/components/similar-topics-validator.js.es6 @@ -10,7 +10,7 @@ import { dasherize } from "@ember/string"; export default WizardFieldValidator.extend({ classNames: ["similar-topics-validator"], - layoutName: 'wizard/templates/components/similar-topics-validator', + layoutName: "wizard/templates/components/similar-topics-validator", similarTopics: null, hasInput: notEmpty("field.value"), hasSimilarTopics: notEmpty("similarTopics"), diff --git a/assets/javascripts/wizard/components/validator.js.es6 b/assets/javascripts/wizard/components/validator.js.es6 index ab442d97..dafbf8c0 100644 --- a/assets/javascripts/wizard/components/validator.js.es6 +++ b/assets/javascripts/wizard/components/validator.js.es6 @@ -6,7 +6,7 @@ import { getToken } from "wizard/lib/ajax"; export default Component.extend({ classNames: ["validator"], classNameBindings: ["isValid", "isInvalid"], - layoutName: 'wizard/templates/components/validator', + layoutName: "wizard/templates/components/validator", validMessageKey: null, invalidMessageKey: null, isValid: null, diff --git a/assets/javascripts/wizard/components/wizard-composer-editor.js.es6 b/assets/javascripts/wizard/components/wizard-composer-editor.js.es6 index 0bd003f5..4f44d439 100644 --- a/assets/javascripts/wizard/components/wizard-composer-editor.js.es6 +++ b/assets/javascripts/wizard/components/wizard-composer-editor.js.es6 @@ -13,7 +13,7 @@ import { uploadIcon } from "discourse/lib/uploads"; import { dasherize } from "@ember/string"; export default ComposerEditor.extend({ - layoutName: 'wizard/templates/components/wizard-composer-editor', + layoutName: "wizard/templates/components/wizard-composer-editor", classNameBindings: ["fieldClass"], allowUpload: true, showLink: false, diff --git a/assets/javascripts/wizard/components/wizard-composer-hyperlink.js.es6 b/assets/javascripts/wizard/components/wizard-composer-hyperlink.js.es6 index c700d3ce..0eeeb176 100644 --- a/assets/javascripts/wizard/components/wizard-composer-hyperlink.js.es6 +++ b/assets/javascripts/wizard/components/wizard-composer-hyperlink.js.es6 @@ -2,7 +2,7 @@ import Component from "@ember/component"; export default Component.extend({ classNames: ["wizard-composer-hyperlink"], - layoutName: 'wizard/templates/components/wizard-composer-hyperlink', + layoutName: "wizard/templates/components/wizard-composer-hyperlink", actions: { addLink() { diff --git a/assets/javascripts/wizard/components/wizard-date-input.js.es6 b/assets/javascripts/wizard/components/wizard-date-input.js.es6 index 375b7195..da2711c7 100644 --- a/assets/javascripts/wizard/components/wizard-date-input.js.es6 +++ b/assets/javascripts/wizard/components/wizard-date-input.js.es6 @@ -3,7 +3,7 @@ import discourseComputed from "discourse-common/utils/decorators"; export default DateInput.extend({ useNativePicker: false, - layoutName: 'wizard/templates/components/wizard-date-input', + layoutName: "wizard/templates/components/wizard-date-input", @discourseComputed() placeholder() { diff --git a/assets/javascripts/wizard/components/wizard-date-time-input.js.es6 b/assets/javascripts/wizard/components/wizard-date-time-input.js.es6 index 0fe1e68a..84a2b03e 100644 --- a/assets/javascripts/wizard/components/wizard-date-time-input.js.es6 +++ b/assets/javascripts/wizard/components/wizard-date-time-input.js.es6 @@ -2,7 +2,7 @@ import DateTimeInput from "discourse/components/date-time-input"; import discourseComputed from "discourse-common/utils/decorators"; export default DateTimeInput.extend({ - layoutName: 'wizard/templates/components/wizard-date-time-input', + layoutName: "wizard/templates/components/wizard-date-time-input", @discourseComputed("timeFirst", "tabindex") timeTabindex(timeFirst, tabindex) { diff --git a/assets/javascripts/wizard/components/wizard-field-category.js.es6 b/assets/javascripts/wizard/components/wizard-field-category.js.es6 index dc20538e..9f4b65ba 100644 --- a/assets/javascripts/wizard/components/wizard-field-category.js.es6 +++ b/assets/javascripts/wizard/components/wizard-field-category.js.es6 @@ -2,7 +2,7 @@ import { observes } from "discourse-common/utils/decorators"; import Category from "discourse/models/category"; export default Ember.Component.extend({ - layoutName: 'wizard/templates/components/wizard-field-category', + layoutName: "wizard/templates/components/wizard-field-category", didInsertElement() { const property = this.field.property || "id"; diff --git a/assets/javascripts/wizard/components/wizard-field-checkbox.js.es6 b/assets/javascripts/wizard/components/wizard-field-checkbox.js.es6 index f9653cd2..6f9daba2 100644 --- a/assets/javascripts/wizard/components/wizard-field-checkbox.js.es6 +++ b/assets/javascripts/wizard/components/wizard-field-checkbox.js.es6 @@ -1,5 +1,5 @@ import Component from "@ember/component"; export default Component.extend({ - layoutName: 'wizard/templates/components/wizard-field-checkbox' + layoutName: "wizard/templates/components/wizard-field-checkbox", }); diff --git a/assets/javascripts/wizard/components/wizard-field-composer-preview.js.es6 b/assets/javascripts/wizard/components/wizard-field-composer-preview.js.es6 index 0aee0d13..a2056a86 100644 --- a/assets/javascripts/wizard/components/wizard-field-composer-preview.js.es6 +++ b/assets/javascripts/wizard/components/wizard-field-composer-preview.js.es6 @@ -7,7 +7,7 @@ import { ajax } from "discourse/lib/ajax"; import { on } from "discourse-common/utils/decorators"; export default Component.extend({ - layoutName: 'wizard/templates/components/wizard-field-composer-preview', + layoutName: "wizard/templates/components/wizard-field-composer-preview", @on("init") updatePreview() { diff --git a/assets/javascripts/wizard/components/wizard-field-composer.js.es6 b/assets/javascripts/wizard/components/wizard-field-composer.js.es6 index 0ef5fe20..07829e8a 100644 --- a/assets/javascripts/wizard/components/wizard-field-composer.js.es6 +++ b/assets/javascripts/wizard/components/wizard-field-composer.js.es6 @@ -5,7 +5,7 @@ import { import EmberObject from "@ember/object"; export default Ember.Component.extend({ - layoutName: 'wizard/templates/components/wizard-field-composer', + layoutName: "wizard/templates/components/wizard-field-composer", showPreview: false, classNameBindings: [ diff --git a/assets/javascripts/wizard/components/wizard-field-date-time.js.es6 b/assets/javascripts/wizard/components/wizard-field-date-time.js.es6 index a916f18e..eee98892 100644 --- a/assets/javascripts/wizard/components/wizard-field-date-time.js.es6 +++ b/assets/javascripts/wizard/components/wizard-field-date-time.js.es6 @@ -2,7 +2,7 @@ import Component from "@ember/component"; import { observes } from "discourse-common/utils/decorators"; export default Component.extend({ - layoutName: 'wizard/templates/components/wizard-field-date-time', + layoutName: "wizard/templates/components/wizard-field-date-time", @observes("dateTime") setValue() { diff --git a/assets/javascripts/wizard/components/wizard-field-date.js.es6 b/assets/javascripts/wizard/components/wizard-field-date.js.es6 index a06d582a..df35638c 100644 --- a/assets/javascripts/wizard/components/wizard-field-date.js.es6 +++ b/assets/javascripts/wizard/components/wizard-field-date.js.es6 @@ -2,7 +2,7 @@ import Component from "@ember/component"; import { observes } from "discourse-common/utils/decorators"; export default Component.extend({ - layoutName: 'wizard/templates/components/wizard-field-date', + layoutName: "wizard/templates/components/wizard-field-date", @observes("date") setValue() { diff --git a/assets/javascripts/wizard/components/wizard-field-dropdown.js.es6 b/assets/javascripts/wizard/components/wizard-field-dropdown.js.es6 index 4b8b7e63..e6b08102 100644 --- a/assets/javascripts/wizard/components/wizard-field-dropdown.js.es6 +++ b/assets/javascripts/wizard/components/wizard-field-dropdown.js.es6 @@ -1,7 +1,7 @@ import Component from "@ember/component"; export default Component.extend({ - layoutName: 'wizard/templates/components/wizard-field-dropdown', + layoutName: "wizard/templates/components/wizard-field-dropdown", keyPress(e) { e.stopPropagation(); diff --git a/assets/javascripts/wizard/components/wizard-field-group.js.es6 b/assets/javascripts/wizard/components/wizard-field-group.js.es6 index 93538071..65a19719 100644 --- a/assets/javascripts/wizard/components/wizard-field-group.js.es6 +++ b/assets/javascripts/wizard/components/wizard-field-group.js.es6 @@ -1,5 +1,5 @@ import Component from "@ember/component"; export default Component.extend({ - layoutName: 'wizard/templates/components/wizard-field-group' + layoutName: "wizard/templates/components/wizard-field-group", }); diff --git a/assets/javascripts/wizard/components/wizard-field-number.js.es6 b/assets/javascripts/wizard/components/wizard-field-number.js.es6 index e7c4d77f..14e1bfcd 100644 --- a/assets/javascripts/wizard/components/wizard-field-number.js.es6 +++ b/assets/javascripts/wizard/components/wizard-field-number.js.es6 @@ -1,5 +1,5 @@ import Component from "@ember/component"; export default Component.extend({ - layoutName: 'wizard/templates/components/wizard-field-number' + layoutName: "wizard/templates/components/wizard-field-number", }); diff --git a/assets/javascripts/wizard/components/wizard-field-tag.js.es6 b/assets/javascripts/wizard/components/wizard-field-tag.js.es6 index 45343522..473bba08 100644 --- a/assets/javascripts/wizard/components/wizard-field-tag.js.es6 +++ b/assets/javascripts/wizard/components/wizard-field-tag.js.es6 @@ -1,5 +1,5 @@ import Component from "@ember/component"; export default Component.extend({ - layoutName: 'wizard/templates/components/wizard-field-tag' + layoutName: "wizard/templates/components/wizard-field-tag", }); diff --git a/assets/javascripts/wizard/components/wizard-field-text.js.es6 b/assets/javascripts/wizard/components/wizard-field-text.js.es6 index f9d5b056..d9e7cca8 100644 --- a/assets/javascripts/wizard/components/wizard-field-text.js.es6 +++ b/assets/javascripts/wizard/components/wizard-field-text.js.es6 @@ -1,7 +1,7 @@ import Component from "@ember/component"; export default Component.extend({ - layoutName: 'wizard/templates/components/wizard-field-text', + layoutName: "wizard/templates/components/wizard-field-text", keyPress(e) { e.stopPropagation(); diff --git a/assets/javascripts/wizard/components/wizard-field-textarea.js.es6 b/assets/javascripts/wizard/components/wizard-field-textarea.js.es6 index 54865d3c..e59a1707 100644 --- a/assets/javascripts/wizard/components/wizard-field-textarea.js.es6 +++ b/assets/javascripts/wizard/components/wizard-field-textarea.js.es6 @@ -1,7 +1,7 @@ import Component from "@ember/component"; export default Component.extend({ - layoutName: 'wizard/templates/components/wizard-field-textarea', + layoutName: "wizard/templates/components/wizard-field-textarea", keyPress(e) { e.stopPropagation(); diff --git a/assets/javascripts/wizard/components/wizard-field-time.js.es6 b/assets/javascripts/wizard/components/wizard-field-time.js.es6 index bf954ec4..a2f2a10d 100644 --- a/assets/javascripts/wizard/components/wizard-field-time.js.es6 +++ b/assets/javascripts/wizard/components/wizard-field-time.js.es6 @@ -2,7 +2,7 @@ import Component from "@ember/component"; import { observes } from "discourse-common/utils/decorators"; export default Component.extend({ - layoutName: 'wizard/templates/components/wizard-field-time', + layoutName: "wizard/templates/components/wizard-field-time", @observes("time") setValue() { diff --git a/assets/javascripts/wizard/components/wizard-field-upload.js.es6 b/assets/javascripts/wizard/components/wizard-field-upload.js.es6 index edadb4f0..4774e942 100644 --- a/assets/javascripts/wizard/components/wizard-field-upload.js.es6 +++ b/assets/javascripts/wizard/components/wizard-field-upload.js.es6 @@ -3,7 +3,7 @@ import Component from "@ember/component"; import { computed } from "@ember/object"; export default Component.extend(UppyUploadMixin, { - layoutName: 'wizard/templates/components/wizard-field-upload', + layoutName: "wizard/templates/components/wizard-field-upload", classNames: ["wizard-field-upload"], classNameBindings: ["isImage"], uploading: false, diff --git a/assets/javascripts/wizard/components/wizard-field-url.js.es6 b/assets/javascripts/wizard/components/wizard-field-url.js.es6 index 411b5fe9..96c10cc2 100644 --- a/assets/javascripts/wizard/components/wizard-field-url.js.es6 +++ b/assets/javascripts/wizard/components/wizard-field-url.js.es6 @@ -1,5 +1,5 @@ import Component from "@ember/component"; export default Component.extend({ - layoutName: 'wizard/templates/components/wizard-field-url' + layoutName: "wizard/templates/components/wizard-field-url", }); diff --git a/assets/javascripts/wizard/components/wizard-field-user-selector.js.es6 b/assets/javascripts/wizard/components/wizard-field-user-selector.js.es6 index c2a32f44..7cf5b446 100644 --- a/assets/javascripts/wizard/components/wizard-field-user-selector.js.es6 +++ b/assets/javascripts/wizard/components/wizard-field-user-selector.js.es6 @@ -1,5 +1,5 @@ import Component from "@ember/component"; export default Component.extend({ - layoutName: 'wizard/templates/components/wizard-field-user-selector' + layoutName: "wizard/templates/components/wizard-field-user-selector", }); diff --git a/assets/javascripts/wizard/components/wizard-field.js.es6 b/assets/javascripts/wizard/components/wizard-field.js.es6 index 372ee182..493d7676 100644 --- a/assets/javascripts/wizard/components/wizard-field.js.es6 +++ b/assets/javascripts/wizard/components/wizard-field.js.es6 @@ -4,8 +4,13 @@ import discourseComputed from "discourse-common/utils/decorators"; import { cook } from "discourse/plugins/discourse-custom-wizard/wizard/lib/text-lite"; export default Component.extend({ - layoutName: 'wizard/templates/components/wizard-field', - classNameBindings: [":wizard-field", "typeClasses", "field.invalid", "field.id"], + layoutName: "wizard/templates/components/wizard-field", + classNameBindings: [ + ":wizard-field", + "typeClasses", + "field.invalid", + "field.id", + ], @discourseComputed("field.type", "field.id") typeClasses: (type, id) => diff --git a/assets/javascripts/wizard/components/wizard-group-selector.js.es6 b/assets/javascripts/wizard/components/wizard-group-selector.js.es6 index b7523f9a..4ff56ec9 100644 --- a/assets/javascripts/wizard/components/wizard-group-selector.js.es6 +++ b/assets/javascripts/wizard/components/wizard-group-selector.js.es6 @@ -3,7 +3,7 @@ import { computed } from "@ember/object"; import { makeArray } from "discourse-common/lib/helpers"; export default ComboBox.extend({ - layoutName: 'wizard/templates/components/wizard-group-selector', + layoutName: "wizard/templates/components/wizard-group-selector", content: computed("groups.[]", "field.content.[]", function () { const whitelist = makeArray(this.field.content); return this.groups diff --git a/assets/javascripts/wizard/components/wizard-no-access.js.es6 b/assets/javascripts/wizard/components/wizard-no-access.js.es6 index 78a23aa8..492a41dc 100644 --- a/assets/javascripts/wizard/components/wizard-no-access.js.es6 +++ b/assets/javascripts/wizard/components/wizard-no-access.js.es6 @@ -4,17 +4,17 @@ import Component from "@ember/component"; import { dasherize } from "@ember/string"; export default Component.extend({ - classNameBindings: [':wizard-no-access', 'reasonClass'], - layoutName: 'wizard/templates/components/wizard-no-access', + classNameBindings: [":wizard-no-access", "reasonClass"], + layoutName: "wizard/templates/components/wizard-no-access", - @discourseComputed('reason') + @discourseComputed("reason") reasonClass(reason) { return dasherize(reason); }, @discourseComputed siteName() { - return (this.siteSettings.title || ''); + return this.siteSettings.title || ""; }, actions: { diff --git a/assets/javascripts/wizard/components/wizard-similar-topics.js.es6 b/assets/javascripts/wizard/components/wizard-similar-topics.js.es6 index c52bfeae..6a56873e 100644 --- a/assets/javascripts/wizard/components/wizard-similar-topics.js.es6 +++ b/assets/javascripts/wizard/components/wizard-similar-topics.js.es6 @@ -4,7 +4,7 @@ import { observes } from "discourse-common/utils/decorators"; export default Component.extend({ classNames: ["wizard-similar-topics"], - layoutName: 'wizard/templates/components/wizard-similar-topics', + layoutName: "wizard/templates/components/wizard-similar-topics", showTopics: true, didInsertElement() { diff --git a/assets/javascripts/wizard/components/wizard-step.js.es6 b/assets/javascripts/wizard/components/wizard-step.js.es6 index 4518afee..18b1a255 100644 --- a/assets/javascripts/wizard/components/wizard-step.js.es6 +++ b/assets/javascripts/wizard/components/wizard-step.js.es6 @@ -12,7 +12,7 @@ import CustomWizard from "../models/wizard"; const alreadyWarned = {}; export default Component.extend({ - layoutName: 'wizard/templates/components/wizard-step', + layoutName: "wizard/templates/components/wizard-step", classNameBindings: [":wizard-step", "step.id"], saving: null, @@ -27,7 +27,7 @@ export default Component.extend({ }, @discourseComputed("step.index", "wizard.required") - showQuitButton: (index, required) => (index === 0 && !required), + showQuitButton: (index, required) => index === 0 && !required, showNextButton: not("step.final"), showDoneButton: alias("step.final"), diff --git a/assets/javascripts/wizard/components/wizard-time-input.js.es6 b/assets/javascripts/wizard/components/wizard-time-input.js.es6 index ec121002..14b08288 100644 --- a/assets/javascripts/wizard/components/wizard-time-input.js.es6 +++ b/assets/javascripts/wizard/components/wizard-time-input.js.es6 @@ -1,5 +1,5 @@ import TimeInput from "discourse/components/time-input"; export default TimeInput.extend({ - layoutName: 'wizard/templates/components/wizard-time-input' + layoutName: "wizard/templates/components/wizard-time-input", }); diff --git a/assets/javascripts/wizard/controllers/wizard-index.js.es6 b/assets/javascripts/wizard/controllers/wizard-index.js.es6 index 2dfdee40..f56db02d 100644 --- a/assets/javascripts/wizard/controllers/wizard-index.js.es6 +++ b/assets/javascripts/wizard/controllers/wizard-index.js.es6 @@ -6,19 +6,19 @@ const reasons = { noWizard: "none", requiresLogin: "requires_login", notPermitted: "not_permitted", - completed: "completed" -} + completed: "completed", +}; export default Controller.extend({ - noAccess: or('noWizard', 'requiresLogin', 'notPermitted', 'completed'), + noAccess: or("noWizard", "requiresLogin", "notPermitted", "completed"), - @discourseComputed('noAccessReason') + @discourseComputed("noAccessReason") noAccessI18nKey(reason) { - return reason ? `wizard.${reasons[reason]}` : 'wizard.none'; + return reason ? `wizard.${reasons[reason]}` : "wizard.none"; }, @discourseComputed noAccessReason() { - return Object.keys(reasons).find(reason => this.get(reason)); - } + return Object.keys(reasons).find((reason) => this.get(reason)); + }, }); diff --git a/assets/javascripts/wizard/lib/initialize/create-contexts.js.es6 b/assets/javascripts/wizard/lib/initialize/create-contexts.js.es6 index 022ac48e..0e637f6c 100644 --- a/assets/javascripts/wizard/lib/initialize/create-contexts.js.es6 +++ b/assets/javascripts/wizard/lib/initialize/create-contexts.js.es6 @@ -3,10 +3,10 @@ export default { const { createHelperContext } = requirejs("discourse-common/lib/helpers"); createHelperContext({ - siteSettings: container.lookup('site-settings:main'), + siteSettings: container.lookup("site-settings:main"), site: container.lookup("site:main"), session: container.lookup("session:main"), capabilities: container.lookup("capabilities:main"), }); - } -} + }, +}; diff --git a/assets/javascripts/wizard/lib/initialize/inject-objects.js.es6 b/assets/javascripts/wizard/lib/initialize/inject-objects.js.es6 index d31efb4d..fb11c9e7 100644 --- a/assets/javascripts/wizard/lib/initialize/inject-objects.js.es6 +++ b/assets/javascripts/wizard/lib/initialize/inject-objects.js.es6 @@ -1,5 +1,5 @@ export default { - run(app, container) { + run(app) { const Store = requirejs("discourse/services/store").default; const Site = requirejs( "discourse/plugins/discourse-custom-wizard/wizard/models/site" @@ -7,7 +7,9 @@ export default { const Session = requirejs("discourse/models/session").default; const RestAdapter = requirejs("discourse/adapters/rest").default; const messageBus = requirejs("message-bus-client").default; - const sniffCapabilites = requirejs("discourse/pre-initializers/sniff-capabilities").default; + const sniffCapabilites = requirejs( + "discourse/pre-initializers/sniff-capabilities" + ).default; const site = Site.current(); const session = Session.current(); @@ -17,26 +19,35 @@ export default { ["site:main", site, false], ["session:main", session, false], ["service:store", Store, true], - ["adapter:rest", RestAdapter, true] + ["adapter:rest", RestAdapter, true], ]; - registrations.forEach(registration => { + registrations.forEach((registration) => { if (!app.hasRegistration(registration[0])) { - app.register(registration[0], registration[1], { instantiate: registration[2] }); + app.register(registration[0], registration[1], { + instantiate: registration[2], + }); } }); - const targets = ["controller", "component", "route", "model", "adapter", "mixin"]; + const targets = [ + "controller", + "component", + "route", + "model", + "adapter", + "mixin", + ]; const injections = [ ["siteSettings", "site-settings:main"], ["messageBus", "message-bus:main"], ["site", "site:main"], ["session", "session:main"], ["store", "service:store"], - ["appEvents", "service:app-events"] + ["appEvents", "service:app-events"], ]; - injections.forEach(injection => { + injections.forEach((injection) => { targets.forEach((t) => app.inject(t, injection[0], injection[1])); }); @@ -45,5 +56,5 @@ export default { } site.set("can_create_tag", false); - } -} + }, +}; diff --git a/assets/javascripts/wizard/lib/initialize/patch-components.js.es6 b/assets/javascripts/wizard/lib/initialize/patch-components.js.es6 index 65086e53..75cc63f1 100644 --- a/assets/javascripts/wizard/lib/initialize/patch-components.js.es6 +++ b/assets/javascripts/wizard/lib/initialize/patch-components.js.es6 @@ -1,11 +1,13 @@ export default { run(app, container) { const getToken = requirejs("wizard/lib/ajax").getToken; - const isTesting = requirejs("discourse-common/config/environment").isTesting; + const isTesting = requirejs("discourse-common/config/environment") + .isTesting; - if (!isTesting) { + if (!isTesting()) { // Add a CSRF token to all AJAX requests let token = getToken(); + const session = container.lookup("session:main"); session.set("csrfToken", token); let callbacks = $.Callbacks(); $.ajaxPrefilter(callbacks.fire); @@ -140,5 +142,5 @@ export default { return resArray; }; } - } + }, }; diff --git a/assets/javascripts/wizard/lib/initialize/register-files.js.es6 b/assets/javascripts/wizard/lib/initialize/register-files.js.es6 index 8d4b850e..85079270 100644 --- a/assets/javascripts/wizard/lib/initialize/register-files.js.es6 +++ b/assets/javascripts/wizard/lib/initialize/register-files.js.es6 @@ -1,10 +1,14 @@ export default { run(app, container) { - const RawHandlebars = requirejs("discourse-common/lib/raw-handlebars").default; + const RawHandlebars = requirejs("discourse-common/lib/raw-handlebars") + .default; const Handlebars = requirejs("handlebars").default; - const registerRawHelpers = requirejs("discourse-common/lib/raw-handlebars-helpers").registerRawHelpers; + const registerRawHelpers = requirejs( + "discourse-common/lib/raw-handlebars-helpers" + ).registerRawHelpers; const { registerHelpers } = requirejs("discourse-common/lib/helpers"); - const jqueryPlugins = requirejs("discourse/initializers/jquery-plugins").default; + const jqueryPlugins = requirejs("discourse/initializers/jquery-plugins") + .default; Object.keys(Ember.TEMPLATES).forEach((k) => { if (k.indexOf("select-kit") === 0) { @@ -22,5 +26,5 @@ export default { registerRawHelpers(RawHandlebars, Handlebars); registerHelpers(app); jqueryPlugins.initialize(container, app); - } -} + }, +}; diff --git a/assets/javascripts/wizard/lib/initialize/wizard.js.es6 b/assets/javascripts/wizard/lib/initialize/wizard.js.es6 index 134257a1..0020f7a8 100644 --- a/assets/javascripts/wizard/lib/initialize/wizard.js.es6 +++ b/assets/javascripts/wizard/lib/initialize/wizard.js.es6 @@ -1,7 +1,8 @@ export default { name: "custom-wizard", initialize(app) { - const isTesting = requirejs("discourse-common/config/environment").isTesting; + const isTesting = requirejs("discourse-common/config/environment") + .isTesting; const isWizard = window.location.pathname.indexOf("/w/") > -1; if (!isWizard && !isTesting()) { @@ -9,14 +10,17 @@ export default { } const container = app.__container__; - const setDefaultOwner = requirejs("discourse-common/lib/get-owner").setDefaultOwner; + const setDefaultOwner = requirejs("discourse-common/lib/get-owner") + .setDefaultOwner; setDefaultOwner(container); if (!isTesting()) { const PreloadStore = requirejs("discourse/lib/preload-store").default; let preloaded; - const preloadedDataElement = document.getElementById("data-preloaded-wizard"); + const preloadedDataElement = document.getElementById( + "data-preloaded-wizard" + ); if (preloadedDataElement) { preloaded = JSON.parse(preloadedDataElement.dataset.preloadedWizard); } @@ -28,7 +32,8 @@ export default { app.SiteSettings = PreloadStore.get("siteSettings"); } - const setEnvironment = requirejs("discourse-common/config/environment").setEnvironment; + const setEnvironment = requirejs("discourse-common/config/environment") + .setEnvironment; const setupData = document.getElementById("data-discourse-setup").dataset; setEnvironment(setupData.environment); @@ -37,13 +42,15 @@ export default { session.set("highlightJsPath", setupData.highlightJsPath); [ - 'register-files', - 'inject-objects', - 'create-contexts', - 'patch-components' - ].forEach(fileName => { - const initializer = requirejs(`discourse/plugins/discourse-custom-wizard/wizard/lib/initialize/${fileName}`).default; + "register-files", + "inject-objects", + "create-contexts", + "patch-components", + ].forEach((fileName) => { + const initializer = requirejs( + `discourse/plugins/discourse-custom-wizard/wizard/lib/initialize/${fileName}` + ).default; initializer.run(app, container); }); - } + }, }; diff --git a/assets/javascripts/wizard/models/field.js.es6 b/assets/javascripts/wizard/models/field.js.es6 index 5f76074e..2b88140e 100644 --- a/assets/javascripts/wizard/models/field.js.es6 +++ b/assets/javascripts/wizard/models/field.js.es6 @@ -75,5 +75,5 @@ export default EmberObject.extend(ValidState, { this.setValid(valid); return valid; - } + }, }); diff --git a/assets/javascripts/wizard/models/step.js.es6 b/assets/javascripts/wizard/models/step.js.es6 index e18657b5..2729e993 100644 --- a/assets/javascripts/wizard/models/step.js.es6 +++ b/assets/javascripts/wizard/models/step.js.es6 @@ -71,11 +71,7 @@ export default EmberObject.extend(ValidState, { type: "PUT", data: { fields }, }).catch((response) => { - if ( - response && - response.responseJSON && - response.responseJSON.errors - ) { + if (response && response.responseJSON && response.responseJSON.errors) { let wizardErrors = []; response.responseJSON.errors.forEach((err) => { if (err.field === wizardId) { diff --git a/assets/javascripts/wizard/routes/step.js.es6 b/assets/javascripts/wizard/routes/step.js.es6 index 2454fc95..a076951f 100644 --- a/assets/javascripts/wizard/routes/step.js.es6 +++ b/assets/javascripts/wizard/routes/step.js.es6 @@ -26,7 +26,7 @@ export default Route.extend({ }, renderTemplate() { - this.render('wizard/templates/step'); + this.render("wizard/templates/step"); }, setupController(controller, model) { diff --git a/assets/javascripts/wizard/routes/wizard-index.js.es6 b/assets/javascripts/wizard/routes/wizard-index.js.es6 index 16b1140a..264cb0a2 100644 --- a/assets/javascripts/wizard/routes/wizard-index.js.es6 +++ b/assets/javascripts/wizard/routes/wizard-index.js.es6 @@ -4,7 +4,13 @@ import Route from "@ember/routing/route"; export default Route.extend({ beforeModel() { const wizard = getCachedWizard(); - if (wizard && wizard.user && wizard.permitted && !wizard.completed && wizard.start) { + if ( + wizard && + wizard.user && + wizard.permitted && + !wizard.completed && + wizard.start + ) { this.replaceWith("step", wizard.start); } }, @@ -14,7 +20,7 @@ export default Route.extend({ }, renderTemplate() { - this.render('wizard/templates/wizard-index'); + this.render("wizard/templates/wizard-index"); }, setupController(controller, model) { @@ -39,5 +45,5 @@ export default Route.extend({ } else { controller.set("noWizard", true); } - } + }, }); diff --git a/assets/javascripts/wizard/routes/wizard.js.es6 b/assets/javascripts/wizard/routes/wizard.js.es6 index 2910ee6d..a2c34f13 100644 --- a/assets/javascripts/wizard/routes/wizard.js.es6 +++ b/assets/javascripts/wizard/routes/wizard.js.es6 @@ -1,5 +1,4 @@ import { findCustomWizard, updateCachedWizard } from "../models/wizard"; -import { ajax } from "wizard/lib/ajax"; import WizardI18n from "../lib/wizard-i18n"; import Route from "@ember/routing/route"; import { scheduleOnce } from "@ember/runloop"; @@ -20,7 +19,7 @@ export default Route.extend({ const title = WizardI18n("wizard.incomplete_submission.title", { date: moment(wizardModel.submission_last_updated_at).format( "MMMM Do YYYY" - ) + ), }); const buttons = [ @@ -49,7 +48,7 @@ export default Route.extend({ }, renderTemplate() { - this.render('wizard/templates/wizard'); + this.render("wizard/templates/wizard"); }, setupController(controller, model) { diff --git a/assets/javascripts/wizard/tests/acceptance/field-test.js.es6 b/assets/javascripts/wizard/tests/acceptance/field-test.js.es6 index f73d1ab7..9300fc09 100644 --- a/assets/javascripts/wizard/tests/acceptance/field-test.js.es6 +++ b/assets/javascripts/wizard/tests/acceptance/field-test.js.es6 @@ -1,135 +1,173 @@ -import { - visit, - click, - fillIn, - triggerKeyEvent -} from "@ember/test-helpers"; +import { click, fillIn, triggerKeyEvent, visit } from "@ember/test-helpers"; import { test } from "qunit"; import { exists } from "../helpers/test"; import acceptance, { - query, count, + query, + server, visible, - server } from "../helpers/acceptance"; -import { - allFieldsWizard, - getWizard -} from "../helpers/wizard"; +import { allFieldsWizard, getWizard } from "../helpers/wizard"; import tagsJson from "../fixtures/tags"; import usersJson from "../fixtures/users"; import { response } from "../pretender"; -acceptance("Field | Fields", [ getWizard(allFieldsWizard) ], - function(hooks) { - test("Text", async function (assert) { - await visit("/wizard"); - assert.ok(exists(".wizard-field.text-field input.wizard-focusable")); - }); +acceptance("Field | Fields", [getWizard(allFieldsWizard)], function () { + test("Text", async function (assert) { + await visit("/wizard"); + assert.ok(exists(".wizard-field.text-field input.wizard-focusable")); + }); - test("Textarea", async function (assert) { - await visit("/wizard"); - assert.ok(visible(".wizard-field.textarea-field textarea.wizard-focusable")); - }); + test("Textarea", async function (assert) { + await visit("/wizard"); + assert.ok( + visible(".wizard-field.textarea-field textarea.wizard-focusable") + ); + }); - test("Composer", async function (assert) { - await visit("/wizard"); - assert.ok(visible(".wizard-field.composer-field .wizard-field-composer textarea")); - assert.strictEqual(count(".wizard-field.composer-field .d-editor-button-bar button"), 8); - assert.ok(visible(".wizard-btn.toggle-preview")); + test("Composer", async function (assert) { + await visit("/wizard"); + assert.ok( + visible(".wizard-field.composer-field .wizard-field-composer textarea") + ); + assert.strictEqual( + count(".wizard-field.composer-field .d-editor-button-bar button"), + 8 + ); + assert.ok(visible(".wizard-btn.toggle-preview")); - await fillIn(".wizard-field.composer-field .wizard-field-composer textarea", "Input in composer"); - await click(".wizard-btn.toggle-preview"); - assert.strictEqual(query('.wizard-field.composer-field .wizard-field-composer .d-editor-preview-wrapper p').textContent.trim(), "Input in composer"); - }); + await fillIn( + ".wizard-field.composer-field .wizard-field-composer textarea", + "Input in composer" + ); + await click(".wizard-btn.toggle-preview"); + assert.strictEqual( + query( + ".wizard-field.composer-field .wizard-field-composer .d-editor-preview-wrapper p" + ).textContent.trim(), + "Input in composer" + ); + }); - test("Text Only", async function (assert) { - await visit("/wizard"); - assert.ok(visible(".wizard-field.text-only-field label.field-label")); - }); + test("Text Only", async function (assert) { + await visit("/wizard"); + assert.ok(visible(".wizard-field.text-only-field label.field-label")); + }); - test("Date", async function (assert) { - await visit("/wizard"); - assert.ok(visible(".wizard-field.date-field input.date-picker")); - await click(".wizard-field.date-field input.date-picker"); - assert.ok(visible(".wizard-field.date-field .pika-single")); - }); + test("Date", async function (assert) { + await visit("/wizard"); + assert.ok(visible(".wizard-field.date-field input.date-picker")); + await click(".wizard-field.date-field input.date-picker"); + assert.ok(visible(".wizard-field.date-field .pika-single")); + }); - test("Time", async function (assert) { - await visit("/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-collection")); - }); + test("Time", async function (assert) { + await visit("/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-collection")); + }); - test("Date Time", async function (assert) { - await visit("/wizard"); - assert.ok(visible(".wizard-field.date-time-field .d-date-time-input .select-kit")); - await click(".wizard-field.date-time-field .d-date-input input.date-picker"); - assert.ok(visible(".wizard-field.date-time-field .d-date-input .pika-single")); - await click(".wizard-field.date-time-field .d-time-input .select-kit .select-kit-header"); - assert.ok(visible(".wizard-field.date-time-field .select-kit-collection")); - }); + test("Date Time", async function (assert) { + await visit("/wizard"); + assert.ok( + visible(".wizard-field.date-time-field .d-date-time-input .select-kit") + ); + await click( + ".wizard-field.date-time-field .d-date-input input.date-picker" + ); + assert.ok( + visible(".wizard-field.date-time-field .d-date-input .pika-single") + ); + await click( + ".wizard-field.date-time-field .d-time-input .select-kit .select-kit-header" + ); + assert.ok(visible(".wizard-field.date-time-field .select-kit-collection")); + }); - test("Number", async function (assert) { - await visit("/wizard"); - assert.ok(visible(".wizard-field.number-field input[type='number']")); - }); + test("Number", async function (assert) { + await visit("/wizard"); + assert.ok(visible(".wizard-field.number-field input[type='number']")); + }); - test("Checkbox", async function (assert) { - await visit("/wizard"); - assert.ok(visible(".wizard-field.checkbox-field input[type='checkbox']")); - }); + test("Checkbox", async function (assert) { + await visit("/wizard"); + assert.ok(visible(".wizard-field.checkbox-field input[type='checkbox']")); + }); - test("Url", async function (assert) { - await visit("/wizard"); - assert.ok(visible(".wizard-field.url-field input[type='text']")); - }); + test("Url", async function (assert) { + await visit("/wizard"); + assert.ok(visible(".wizard-field.url-field input[type='text']")); + }); - test("Upload", async function (assert) { - await visit("/wizard"); - assert.ok(visible(".wizard-field.upload-field label.wizard-btn-upload-file")); - assert.ok(exists(".wizard-field.upload-field input.hidden-upload-field")); - }); + test("Upload", async function (assert) { + await visit("/wizard"); + assert.ok( + visible(".wizard-field.upload-field label.wizard-btn-upload-file") + ); + assert.ok(exists(".wizard-field.upload-field input.hidden-upload-field")); + }); - test("Dropdown", async function (assert) { - await visit("/wizard"); - assert.ok(visible(".wizard-field.dropdown-field .single-select-header")); - await click(".wizard-field.dropdown-field .select-kit-header"); - assert.strictEqual(count(".wizard-field.dropdown-field .select-kit-collection li"), 3); - }); + test("Dropdown", async function (assert) { + await visit("/wizard"); + assert.ok(visible(".wizard-field.dropdown-field .single-select-header")); + await click(".wizard-field.dropdown-field .select-kit-header"); + assert.strictEqual( + count(".wizard-field.dropdown-field .select-kit-collection li"), + 3 + ); + }); - test("Tag", async function (assert) { - server.get("/tags/filter/search", () => (response(200, { results: tagsJson['tags']}))); - await visit("/wizard"); - assert.ok(visible(".wizard-field.tag-field .multi-select-header")); - await click(".wizard-field.tag-field .select-kit-header"); - assert.strictEqual(count(".wizard-field.tag-field .select-kit-collection li"), 2); - }); + test("Tag", async function (assert) { + server.get("/tags/filter/search", () => + response(200, { results: tagsJson["tags"] }) + ); + await visit("/wizard"); + assert.ok(visible(".wizard-field.tag-field .multi-select-header")); + await click(".wizard-field.tag-field .select-kit-header"); + assert.strictEqual( + count(".wizard-field.tag-field .select-kit-collection li"), + 2 + ); + }); - test("Category", async function (assert) { - await visit("/wizard"); - assert.ok(visible(".wizard-field.category-field .multi-select-header")); - await click(".wizard-field.category-field .select-kit-header"); - assert.strictEqual(count(".wizard-field.category-field .select-kit-collection li"), 5); - }); + test("Category", async function (assert) { + await visit("/wizard"); + assert.ok(visible(".wizard-field.category-field .multi-select-header")); + await click(".wizard-field.category-field .select-kit-header"); + assert.strictEqual( + count(".wizard-field.category-field .select-kit-collection li"), + 5 + ); + }); - test("Group", async function (assert) { - await visit("/wizard"); - assert.ok(visible(".wizard-field.group-field .single-select-header")); - await click(".wizard-field.group-field .select-kit-header"); - assert.strictEqual(count(".wizard-field.group-field .select-kit-collection li"), 10); - }); + test("Group", async function (assert) { + await visit("/wizard"); + assert.ok(visible(".wizard-field.group-field .single-select-header")); + await click(".wizard-field.group-field .select-kit-header"); + assert.strictEqual( + count(".wizard-field.group-field .select-kit-collection li"), + 10 + ); + }); - test("User", async function (assert) { - server.get("/u/search/users", () => (response(200, usersJson))); + test("User", async function (assert) { + server.get("/u/search/users", () => response(200, usersJson)); - await visit("/wizard"); - await fillIn(".wizard-field.user-selector-field input.ember-text-field", "a"); - await triggerKeyEvent(".wizard-field.user-selector-field input.ember-text-field", "keyup", "a".charCodeAt(0)); + await visit("/wizard"); + await fillIn( + ".wizard-field.user-selector-field input.ember-text-field", + "a" + ); + await triggerKeyEvent( + ".wizard-field.user-selector-field input.ember-text-field", + "keyup", + "a".charCodeAt(0) + ); - assert.ok(visible(".wizard-field.user-selector-field .ac-wrap")); - // TODO: add assertion for ac results. autocomplete does not appear in time. - }); - } -); + assert.ok(visible(".wizard-field.user-selector-field .ac-wrap")); + // TODO: add assertion for ac results. autocomplete does not appear in time. + }); +}); diff --git a/assets/javascripts/wizard/tests/acceptance/step-test.js.es6 b/assets/javascripts/wizard/tests/acceptance/step-test.js.es6 index 731c9b76..1537f1c7 100644 --- a/assets/javascripts/wizard/tests/acceptance/step-test.js.es6 +++ b/assets/javascripts/wizard/tests/acceptance/step-test.js.es6 @@ -1,47 +1,41 @@ -import { visit, click } from "@ember/test-helpers"; +import { click, visit } from "@ember/test-helpers"; import { test } from "qunit"; import { exists } from "../helpers/test"; -import acceptance, { - query, - count, - visible -} from "../helpers/acceptance"; -import { - stepNotPermitted, - wizard, - getWizard -} from "../helpers/wizard"; -import { - saveStep, - update -} from "../helpers/step"; +import acceptance, { count, query, visible } from "../helpers/acceptance"; +import { getWizard, stepNotPermitted, wizard } from "../helpers/wizard"; +import { saveStep, update } from "../helpers/step"; -acceptance("Step | Not permitted", [ getWizard(stepNotPermitted) ], - function(hooks) { - test("Shows not permitted message", async function (assert) { - await visit("/wizard"); - assert.ok(exists(".step-message.not-permitted")); - }); - } -); +acceptance("Step | Not permitted", [getWizard(stepNotPermitted)], function () { + test("Shows not permitted message", async function (assert) { + await visit("/wizard"); + assert.ok(exists(".step-message.not-permitted")); + }); +}); -acceptance("Step | Step", [ getWizard(wizard), saveStep(update) ], - function(hooks) { - test("Renders the step", async function (assert) { - await visit("/wizard"); - assert.strictEqual(query('.wizard-step-title p').textContent.trim(), "Text"); - assert.strictEqual(query('.wizard-step-description p').textContent.trim(), "Text inputs!"); - assert.strictEqual(query('.wizard-step-description p').textContent.trim(), "Text inputs!"); - assert.strictEqual(count('.wizard-step-form .wizard-field'), 6); - assert.ok(visible('.wizard-step-footer .wizard-progress'), true); - assert.ok(visible('.wizard-step-footer .wizard-buttons'), true); - }); +acceptance("Step | Step", [getWizard(wizard), saveStep(update)], function () { + test("Renders the step", async function (assert) { + await visit("/wizard"); + assert.strictEqual( + query(".wizard-step-title p").textContent.trim(), + "Text" + ); + assert.strictEqual( + query(".wizard-step-description p").textContent.trim(), + "Text inputs!" + ); + assert.strictEqual( + query(".wizard-step-description p").textContent.trim(), + "Text inputs!" + ); + assert.strictEqual(count(".wizard-step-form .wizard-field"), 6); + assert.ok(visible(".wizard-step-footer .wizard-progress"), true); + assert.ok(visible(".wizard-step-footer .wizard-buttons"), true); + }); - test("Goes to the next step", async function (assert) { - await visit("/wizard"); - assert.ok(visible('.wizard-step.step_1'), true); - await click('.wizard-btn.next'); - assert.ok(visible('.wizard-step.step_2'), true); - }); - } -); + test("Goes to the next step", async function (assert) { + await visit("/wizard"); + assert.ok(visible(".wizard-step.step_1"), true); + await click(".wizard-btn.next"); + assert.ok(visible(".wizard-step.step_2"), true); + }); +}); diff --git a/assets/javascripts/wizard/tests/acceptance/wizard-test.js.es6 b/assets/javascripts/wizard/tests/acceptance/wizard-test.js.es6 index e68f59ae..8fd82d11 100644 --- a/assets/javascripts/wizard/tests/acceptance/wizard-test.js.es6 +++ b/assets/javascripts/wizard/tests/acceptance/wizard-test.js.es6 @@ -1,30 +1,26 @@ import { visit } from "@ember/test-helpers"; import { test } from "qunit"; import { exists } from "../helpers/test"; -import acceptance, { - query, - count, - visible -} from "../helpers/acceptance"; +import acceptance, { count, query, visible } from "../helpers/acceptance"; import { + getWizard, + wizard, + wizardCompleted, wizardNoUser, wizardNotPermitted, - wizardCompleted, - wizard, - getWizard } from "../helpers/wizard"; -acceptance("Wizard | Not logged in", [ getWizard(wizardNoUser) ], - function(hooks) { - test("Wizard no access requires login", async function (assert) { - await visit("/wizard"); - assert.ok(exists(".wizard-no-access.requires-login")); - }); - } -); +acceptance("Wizard | Not logged in", [getWizard(wizardNoUser)], function () { + test("Wizard no access requires login", async function (assert) { + await visit("/wizard"); + assert.ok(exists(".wizard-no-access.requires-login")); + }); +}); -acceptance("Wizard | Not permitted", [ getWizard(wizardNotPermitted) ], - function(hooks) { +acceptance( + "Wizard | Not permitted", + [getWizard(wizardNotPermitted)], + function () { test("Wizard no access not permitted", async function (assert) { await visit("/wizard"); assert.ok(exists(".wizard-no-access.not-permitted")); @@ -32,41 +28,46 @@ acceptance("Wizard | Not permitted", [ getWizard(wizardNotPermitted) ], } ); -acceptance("Wizard | Completed", [ getWizard(wizardCompleted) ], - function(hooks) { - test("Wizard no access completed", async function (assert) { - await visit("/wizard"); - assert.ok(exists(".wizard-no-access.completed")); - }); - } -); +acceptance("Wizard | Completed", [getWizard(wizardCompleted)], function () { + test("Wizard no access completed", async function (assert) { + await visit("/wizard"); + assert.ok(exists(".wizard-no-access.completed")); + }); +}); -acceptance("Wizard | Wizard", [ getWizard(wizard) ], - function(hooks) { - test("Starts", async function (assert) { - await visit("/wizard"); - assert.ok(query('.wizard-column'), true); - }); +acceptance("Wizard | Wizard", [getWizard(wizard)], function () { + test("Starts", async function (assert) { + await visit("/wizard"); + assert.ok(query(".wizard-column"), true); + }); - test("Applies the body background color", async function (assert) { - await visit("/wizard"); - assert.ok($("body")[0].style.background); - }); + test("Applies the body background color", async function (assert) { + await visit("/wizard"); + assert.ok($("body")[0].style.background); + }); - test("Renders the wizard form", async function (assert) { - await visit("/wizard"); - assert.ok(visible('.wizard-column-contents .wizard-step'), true); - assert.ok(visible('.wizard-footer img'), true); - }); + test("Renders the wizard form", async function (assert) { + await visit("/wizard"); + assert.ok(visible(".wizard-column-contents .wizard-step"), true); + assert.ok(visible(".wizard-footer img"), true); + }); - test("Renders the first step", async function (assert) { - await visit("/wizard"); - assert.strictEqual(query('.wizard-step-title p').textContent.trim(), "Text"); - assert.strictEqual(query('.wizard-step-description p').textContent.trim(), "Text inputs!"); - assert.strictEqual(query('.wizard-step-description p').textContent.trim(), "Text inputs!"); - assert.strictEqual(count('.wizard-step-form .wizard-field'), 6); - assert.ok(visible('.wizard-step-footer .wizard-progress'), true); - assert.ok(visible('.wizard-step-footer .wizard-buttons'), true); - }); - } -); + test("Renders the first step", async function (assert) { + await visit("/wizard"); + assert.strictEqual( + query(".wizard-step-title p").textContent.trim(), + "Text" + ); + assert.strictEqual( + query(".wizard-step-description p").textContent.trim(), + "Text inputs!" + ); + assert.strictEqual( + query(".wizard-step-description p").textContent.trim(), + "Text inputs!" + ); + assert.strictEqual(count(".wizard-step-form .wizard-field"), 6); + assert.ok(visible(".wizard-step-footer .wizard-progress"), true); + assert.ok(visible(".wizard-step-footer .wizard-buttons"), true); + }); +}); diff --git a/assets/javascripts/wizard/tests/fixtures/categories.js.es6 b/assets/javascripts/wizard/tests/fixtures/categories.js.es6 index e862f54a..e553f860 100644 --- a/assets/javascripts/wizard/tests/fixtures/categories.js.es6 +++ b/assets/javascripts/wizard/tests/fixtures/categories.js.es6 @@ -1,209 +1,221 @@ export default { - "categories": [ + categories: [ { - "id": 1, - "name": "Uncategorized", - "color": "0088CC", - "text_color": "FFFFFF", - "slug": "uncategorized", - "topic_count": 1, - "post_count": 1, - "position": 0, - "description": "Topics that don't need a category, or don't fit into any other existing category.", - "description_text": "Topics that don't need a category, or don't fit into any other existing category.", - "description_excerpt": "Topics that don't need a category, or don't fit into any other existing category.", - "topic_url": "/t/", - "read_restricted": false, - "permission": 1, - "notification_level": 0, - "topic_template": null, - "has_children": false, - "sort_order": null, - "sort_ascending": null, - "show_subcategory_list": false, - "num_featured_topics": 3, - "default_view": null, - "subcategory_list_style": "rows_with_featured_topics", - "default_top_period": "all", - "default_list_filter": "all", - "minimum_required_tags": 0, - "navigate_to_first_post_after_read": false, - "custom_fields": { - "create_topic_wizard": null + id: 1, + name: "Uncategorized", + color: "0088CC", + text_color: "FFFFFF", + slug: "uncategorized", + topic_count: 1, + post_count: 1, + position: 0, + description: + "Topics that don't need a category, or don't fit into any other existing category.", + description_text: + "Topics that don't need a category, or don't fit into any other existing category.", + description_excerpt: + "Topics that don't need a category, or don't fit into any other existing category.", + topic_url: "/t/", + read_restricted: false, + permission: 1, + notification_level: 0, + topic_template: null, + has_children: false, + sort_order: null, + sort_ascending: null, + show_subcategory_list: false, + num_featured_topics: 3, + default_view: null, + subcategory_list_style: "rows_with_featured_topics", + default_top_period: "all", + default_list_filter: "all", + minimum_required_tags: 0, + navigate_to_first_post_after_read: false, + custom_fields: { + create_topic_wizard: null, }, - "allowed_tags": [], - "allowed_tag_groups": [], - "allow_global_tags": false, - "min_tags_from_required_group": 1, - "required_tag_group_name": null, - "read_only_banner": null, - "uploaded_logo": null, - "uploaded_background": null, - "can_edit": true + allowed_tags: [], + allowed_tag_groups: [], + allow_global_tags: false, + min_tags_from_required_group: 1, + required_tag_group_name: null, + read_only_banner: null, + uploaded_logo: null, + uploaded_background: null, + can_edit: true, }, { - "id": 2, - "name": "Site Feedback", - "color": "808281", - "text_color": "FFFFFF", - "slug": "site-feedback", - "topic_count": 20, - "post_count": 21, - "position": 1, - "description": "

Discussion about this site, its organization, how it works, and how we can improve it.

", - "description_text": "Discussion about this site, its organization, how it works, and how we can improve it.", - "description_excerpt": "Discussion about this site, its organization, how it works, and how we can improve it.", - "topic_url": "/t/about-the-site-feedback-category/1", - "read_restricted": false, - "permission": 1, - "notification_level": 0, - "topic_template": null, - "has_children": false, - "sort_order": null, - "sort_ascending": null, - "show_subcategory_list": false, - "num_featured_topics": 3, - "default_view": null, - "subcategory_list_style": "rows_with_featured_topics", - "default_top_period": "all", - "default_list_filter": "all", - "minimum_required_tags": 0, - "navigate_to_first_post_after_read": false, - "custom_fields": { - "create_topic_wizard": null + id: 2, + name: "Site Feedback", + color: "808281", + text_color: "FFFFFF", + slug: "site-feedback", + topic_count: 20, + post_count: 21, + position: 1, + description: + "

Discussion about this site, its organization, how it works, and how we can improve it.

", + description_text: + "Discussion about this site, its organization, how it works, and how we can improve it.", + description_excerpt: + "Discussion about this site, its organization, how it works, and how we can improve it.", + topic_url: "/t/about-the-site-feedback-category/1", + read_restricted: false, + permission: 1, + notification_level: 0, + topic_template: null, + has_children: false, + sort_order: null, + sort_ascending: null, + show_subcategory_list: false, + num_featured_topics: 3, + default_view: null, + subcategory_list_style: "rows_with_featured_topics", + default_top_period: "all", + default_list_filter: "all", + minimum_required_tags: 0, + navigate_to_first_post_after_read: false, + custom_fields: { + create_topic_wizard: null, }, - "allowed_tags": [], - "allowed_tag_groups": [], - "allow_global_tags": false, - "min_tags_from_required_group": 1, - "required_tag_group_name": null, - "read_only_banner": null, - "uploaded_logo": null, - "uploaded_background": null, - "can_edit": true + allowed_tags: [], + allowed_tag_groups: [], + allow_global_tags: false, + min_tags_from_required_group: 1, + required_tag_group_name: null, + read_only_banner: null, + uploaded_logo: null, + uploaded_background: null, + can_edit: true, }, { - "id": 3, - "name": "Staff", - "color": "E45735", - "text_color": "FFFFFF", - "slug": "staff", - "topic_count": 4, - "post_count": 7, - "position": 2, - "description": "

Private category for staff discussions. Topics are only visible to admins and moderators.

", - "description_text": "Private category for staff discussions. Topics are only visible to admins and moderators.", - "description_excerpt": "Private category for staff discussions. Topics are only visible to admins and moderators.", - "topic_url": "/t/about-the-staff-category/2", - "read_restricted": true, - "permission": 1, - "notification_level": 0, - "topic_template": null, - "has_children": false, - "sort_order": null, - "sort_ascending": null, - "show_subcategory_list": false, - "num_featured_topics": 3, - "default_view": null, - "subcategory_list_style": "rows_with_featured_topics", - "default_top_period": "all", - "default_list_filter": "all", - "minimum_required_tags": 0, - "navigate_to_first_post_after_read": false, - "custom_fields": { - "create_topic_wizard": null + id: 3, + name: "Staff", + color: "E45735", + text_color: "FFFFFF", + slug: "staff", + topic_count: 4, + post_count: 7, + position: 2, + description: + "

Private category for staff discussions. Topics are only visible to admins and moderators.

", + description_text: + "Private category for staff discussions. Topics are only visible to admins and moderators.", + description_excerpt: + "Private category for staff discussions. Topics are only visible to admins and moderators.", + topic_url: "/t/about-the-staff-category/2", + read_restricted: true, + permission: 1, + notification_level: 0, + topic_template: null, + has_children: false, + sort_order: null, + sort_ascending: null, + show_subcategory_list: false, + num_featured_topics: 3, + default_view: null, + subcategory_list_style: "rows_with_featured_topics", + default_top_period: "all", + default_list_filter: "all", + minimum_required_tags: 0, + navigate_to_first_post_after_read: false, + custom_fields: { + create_topic_wizard: null, }, - "allowed_tags": [], - "allowed_tag_groups": [], - "allow_global_tags": false, - "min_tags_from_required_group": 1, - "required_tag_group_name": null, - "read_only_banner": null, - "uploaded_logo": null, - "uploaded_background": null, - "can_edit": true + allowed_tags: [], + allowed_tag_groups: [], + allow_global_tags: false, + min_tags_from_required_group: 1, + required_tag_group_name: null, + read_only_banner: null, + uploaded_logo: null, + uploaded_background: null, + can_edit: true, }, { - "id": 4, - "name": "Lounge", - "color": "A461EF", - "text_color": "652D90", - "slug": "lounge", - "topic_count": 1, - "post_count": 1, - "position": 3, - "description": "

A category exclusive to members with trust level 3 and higher.

", - "description_text": "A category exclusive to members with trust level 3 and higher.", - "description_excerpt": "A category exclusive to members with trust level 3 and higher.", - "topic_url": "/t/about-the-lounge-category/3", - "read_restricted": true, - "permission": 1, - "notification_level": 0, - "topic_template": null, - "has_children": false, - "sort_order": null, - "sort_ascending": null, - "show_subcategory_list": false, - "num_featured_topics": 3, - "default_view": null, - "subcategory_list_style": "rows_with_featured_topics", - "default_top_period": "all", - "default_list_filter": "all", - "minimum_required_tags": 0, - "navigate_to_first_post_after_read": false, - "custom_fields": { - "create_topic_wizard": null + id: 4, + name: "Lounge", + color: "A461EF", + text_color: "652D90", + slug: "lounge", + topic_count: 1, + post_count: 1, + position: 3, + description: + "

A category exclusive to members with trust level 3 and higher.

", + description_text: + "A category exclusive to members with trust level 3 and higher.", + description_excerpt: + "A category exclusive to members with trust level 3 and higher.", + topic_url: "/t/about-the-lounge-category/3", + read_restricted: true, + permission: 1, + notification_level: 0, + topic_template: null, + has_children: false, + sort_order: null, + sort_ascending: null, + show_subcategory_list: false, + num_featured_topics: 3, + default_view: null, + subcategory_list_style: "rows_with_featured_topics", + default_top_period: "all", + default_list_filter: "all", + minimum_required_tags: 0, + navigate_to_first_post_after_read: false, + custom_fields: { + create_topic_wizard: null, }, - "allowed_tags": [], - "allowed_tag_groups": [], - "allow_global_tags": false, - "min_tags_from_required_group": 1, - "required_tag_group_name": null, - "read_only_banner": null, - "uploaded_logo": null, - "uploaded_background": null, - "can_edit": true + allowed_tags: [], + allowed_tag_groups: [], + allow_global_tags: false, + min_tags_from_required_group: 1, + required_tag_group_name: null, + read_only_banner: null, + uploaded_logo: null, + uploaded_background: null, + can_edit: true, }, { - "id": 5, - "name": "Custom Categories", - "color": "0088CC", - "text_color": "FFFFFF", - "slug": "custom-category", - "topic_count": 0, - "post_count": 0, - "position": 10, - "description": "Description of custom category", - "description_text": "Description of custom category", - "description_excerpt": "Description of custom category", - "topic_url": "/t/about-the-custom-category/5", - "read_restricted": false, - "permission": 1, - "notification_level": 0, - "topic_template": null, - "has_children": false, - "sort_order": null, - "sort_ascending": null, - "show_subcategory_list": false, - "num_featured_topics": 3, - "default_view": null, - "subcategory_list_style": "rows_with_featured_topics", - "default_top_period": "all", - "default_list_filter": "all", - "minimum_required_tags": 0, - "navigate_to_first_post_after_read": false, - "custom_fields": { - "create_topic_wizard": null + id: 5, + name: "Custom Categories", + color: "0088CC", + text_color: "FFFFFF", + slug: "custom-category", + topic_count: 0, + post_count: 0, + position: 10, + description: "Description of custom category", + description_text: "Description of custom category", + description_excerpt: "Description of custom category", + topic_url: "/t/about-the-custom-category/5", + read_restricted: false, + permission: 1, + notification_level: 0, + topic_template: null, + has_children: false, + sort_order: null, + sort_ascending: null, + show_subcategory_list: false, + num_featured_topics: 3, + default_view: null, + subcategory_list_style: "rows_with_featured_topics", + default_top_period: "all", + default_list_filter: "all", + minimum_required_tags: 0, + navigate_to_first_post_after_read: false, + custom_fields: { + create_topic_wizard: null, }, - "allowed_tags": [], - "allowed_tag_groups": [], - "allow_global_tags": false, - "min_tags_from_required_group": 1, - "required_tag_group_name": null, - "read_only_banner": null, - "uploaded_logo": null, - "uploaded_background": null, - "can_edit": true - } - ] -} + allowed_tags: [], + allowed_tag_groups: [], + allow_global_tags: false, + min_tags_from_required_group: 1, + required_tag_group_name: null, + read_only_banner: null, + uploaded_logo: null, + uploaded_background: null, + can_edit: true, + }, + ], +}; diff --git a/assets/javascripts/wizard/tests/fixtures/groups.js.es6 b/assets/javascripts/wizard/tests/fixtures/groups.js.es6 index 16f8150d..a770cf58 100644 --- a/assets/javascripts/wizard/tests/fixtures/groups.js.es6 +++ b/assets/javascripts/wizard/tests/fixtures/groups.js.es6 @@ -1,313 +1,313 @@ export default { - "groups": [ + groups: [ { - "id": 1, - "automatic": true, - "name": "admins", - "display_name": "admins", - "user_count": 1, - "mentionable_level": 0, - "messageable_level": 0, - "visibility_level": 1, - "primary_group": false, - "title": null, - "grant_trust_level": null, - "incoming_email": null, - "has_messages": false, - "flair_url": null, - "flair_bg_color": null, - "flair_color": null, - "bio_raw": null, - "bio_cooked": null, - "bio_excerpt": null, - "public_admission": false, - "public_exit": false, - "allow_membership_requests": false, - "full_name": null, - "default_notification_level": 3, - "membership_request_template": null, - "members_visibility_level": 0, - "can_see_members": true, - "can_admin_group": true, - "publish_read_state": false + id: 1, + automatic: true, + name: "admins", + display_name: "admins", + user_count: 1, + mentionable_level: 0, + messageable_level: 0, + visibility_level: 1, + primary_group: false, + title: null, + grant_trust_level: null, + incoming_email: null, + has_messages: false, + flair_url: null, + flair_bg_color: null, + flair_color: null, + bio_raw: null, + bio_cooked: null, + bio_excerpt: null, + public_admission: false, + public_exit: false, + allow_membership_requests: false, + full_name: null, + default_notification_level: 3, + membership_request_template: null, + members_visibility_level: 0, + can_see_members: true, + can_admin_group: true, + publish_read_state: false, }, { - "id": 0, - "automatic": true, - "name": "everyone", - "display_name": "everyone", - "user_count": 0, - "mentionable_level": 0, - "messageable_level": 0, - "visibility_level": 3, - "primary_group": false, - "title": null, - "grant_trust_level": null, - "incoming_email": null, - "has_messages": false, - "flair_url": null, - "flair_bg_color": null, - "flair_color": null, - "bio_raw": null, - "bio_cooked": null, - "bio_excerpt": null, - "public_admission": false, - "public_exit": false, - "allow_membership_requests": false, - "full_name": null, - "default_notification_level": 3, - "membership_request_template": null, - "members_visibility_level": 0, - "can_see_members": true, - "can_admin_group": true, - "publish_read_state": false + id: 0, + automatic: true, + name: "everyone", + display_name: "everyone", + user_count: 0, + mentionable_level: 0, + messageable_level: 0, + visibility_level: 3, + primary_group: false, + title: null, + grant_trust_level: null, + incoming_email: null, + has_messages: false, + flair_url: null, + flair_bg_color: null, + flair_color: null, + bio_raw: null, + bio_cooked: null, + bio_excerpt: null, + public_admission: false, + public_exit: false, + allow_membership_requests: false, + full_name: null, + default_notification_level: 3, + membership_request_template: null, + members_visibility_level: 0, + can_see_members: true, + can_admin_group: true, + publish_read_state: false, }, { - "id": 15, - "automatic": false, - "name": "custom_group", - "user_count": 1, - "mentionable_level": 1, - "messageable_level": 2, - "visibility_level": 3, - "primary_group": false, - "title": "Custom Group", - "grant_trust_level": 3, - "incoming_email": null, - "has_messages": false, - "flair_url": null, - "flair_bg_color": null, - "flair_color": null, - "bio_raw": null, - "bio_cooked": null, - "bio_excerpt": null, - "public_admission": false, - "public_exit": false, - "allow_membership_requests": false, - "full_name": "I am prefilled", - "default_notification_level": 3, - "membership_request_template": null, - "members_visibility_level": 99, - "can_see_members": true, - "can_admin_group": true, - "publish_read_state": false + id: 15, + automatic: false, + name: "custom_group", + user_count: 1, + mentionable_level: 1, + messageable_level: 2, + visibility_level: 3, + primary_group: false, + title: "Custom Group", + grant_trust_level: 3, + incoming_email: null, + has_messages: false, + flair_url: null, + flair_bg_color: null, + flair_color: null, + bio_raw: null, + bio_cooked: null, + bio_excerpt: null, + public_admission: false, + public_exit: false, + allow_membership_requests: false, + full_name: "I am prefilled", + default_notification_level: 3, + membership_request_template: null, + members_visibility_level: 99, + can_see_members: true, + can_admin_group: true, + publish_read_state: false, }, { - "id": 2, - "automatic": true, - "name": "moderators", - "display_name": "moderators", - "user_count": 0, - "mentionable_level": 0, - "messageable_level": 99, - "visibility_level": 1, - "primary_group": false, - "title": null, - "grant_trust_level": null, - "incoming_email": null, - "has_messages": false, - "flair_url": null, - "flair_bg_color": null, - "flair_color": null, - "bio_raw": null, - "bio_cooked": null, - "bio_excerpt": null, - "public_admission": false, - "public_exit": false, - "allow_membership_requests": false, - "full_name": null, - "default_notification_level": 2, - "membership_request_template": null, - "members_visibility_level": 0, - "can_see_members": true, - "can_admin_group": true, - "publish_read_state": false + id: 2, + automatic: true, + name: "moderators", + display_name: "moderators", + user_count: 0, + mentionable_level: 0, + messageable_level: 99, + visibility_level: 1, + primary_group: false, + title: null, + grant_trust_level: null, + incoming_email: null, + has_messages: false, + flair_url: null, + flair_bg_color: null, + flair_color: null, + bio_raw: null, + bio_cooked: null, + bio_excerpt: null, + public_admission: false, + public_exit: false, + allow_membership_requests: false, + full_name: null, + default_notification_level: 2, + membership_request_template: null, + members_visibility_level: 0, + can_see_members: true, + can_admin_group: true, + publish_read_state: false, }, { - "id": 3, - "automatic": true, - "name": "staff", - "display_name": "staff", - "user_count": 1, - "mentionable_level": 0, - "messageable_level": 0, - "visibility_level": 1, - "primary_group": false, - "title": null, - "grant_trust_level": null, - "incoming_email": null, - "has_messages": false, - "flair_url": null, - "flair_bg_color": null, - "flair_color": null, - "bio_raw": null, - "bio_cooked": null, - "bio_excerpt": null, - "public_admission": false, - "public_exit": false, - "allow_membership_requests": false, - "full_name": null, - "default_notification_level": 3, - "membership_request_template": null, - "members_visibility_level": 0, - "can_see_members": true, - "can_admin_group": true, - "publish_read_state": false + id: 3, + automatic: true, + name: "staff", + display_name: "staff", + user_count: 1, + mentionable_level: 0, + messageable_level: 0, + visibility_level: 1, + primary_group: false, + title: null, + grant_trust_level: null, + incoming_email: null, + has_messages: false, + flair_url: null, + flair_bg_color: null, + flair_color: null, + bio_raw: null, + bio_cooked: null, + bio_excerpt: null, + public_admission: false, + public_exit: false, + allow_membership_requests: false, + full_name: null, + default_notification_level: 3, + membership_request_template: null, + members_visibility_level: 0, + can_see_members: true, + can_admin_group: true, + publish_read_state: false, }, { - "id": 10, - "automatic": true, - "name": "trust_level_0", - "display_name": "trust_level_0", - "user_count": 2, - "mentionable_level": 0, - "messageable_level": 0, - "visibility_level": 1, - "primary_group": false, - "title": null, - "grant_trust_level": null, - "incoming_email": null, - "has_messages": false, - "flair_url": null, - "flair_bg_color": null, - "flair_color": null, - "bio_raw": null, - "bio_cooked": null, - "bio_excerpt": null, - "public_admission": false, - "public_exit": false, - "allow_membership_requests": false, - "full_name": null, - "default_notification_level": 3, - "membership_request_template": null, - "members_visibility_level": 0, - "can_see_members": true, - "can_admin_group": true, - "publish_read_state": false + id: 10, + automatic: true, + name: "trust_level_0", + display_name: "trust_level_0", + user_count: 2, + mentionable_level: 0, + messageable_level: 0, + visibility_level: 1, + primary_group: false, + title: null, + grant_trust_level: null, + incoming_email: null, + has_messages: false, + flair_url: null, + flair_bg_color: null, + flair_color: null, + bio_raw: null, + bio_cooked: null, + bio_excerpt: null, + public_admission: false, + public_exit: false, + allow_membership_requests: false, + full_name: null, + default_notification_level: 3, + membership_request_template: null, + members_visibility_level: 0, + can_see_members: true, + can_admin_group: true, + publish_read_state: false, }, { - "id": 11, - "automatic": true, - "name": "trust_level_1", - "display_name": "trust_level_1", - "user_count": 2, - "mentionable_level": 0, - "messageable_level": 0, - "visibility_level": 1, - "primary_group": false, - "title": null, - "grant_trust_level": null, - "incoming_email": null, - "has_messages": false, - "flair_url": null, - "flair_bg_color": null, - "flair_color": null, - "bio_raw": null, - "bio_cooked": null, - "bio_excerpt": null, - "public_admission": false, - "public_exit": false, - "allow_membership_requests": false, - "full_name": null, - "default_notification_level": 3, - "membership_request_template": null, - "members_visibility_level": 0, - "can_see_members": true, - "can_admin_group": true, - "publish_read_state": false + id: 11, + automatic: true, + name: "trust_level_1", + display_name: "trust_level_1", + user_count: 2, + mentionable_level: 0, + messageable_level: 0, + visibility_level: 1, + primary_group: false, + title: null, + grant_trust_level: null, + incoming_email: null, + has_messages: false, + flair_url: null, + flair_bg_color: null, + flair_color: null, + bio_raw: null, + bio_cooked: null, + bio_excerpt: null, + public_admission: false, + public_exit: false, + allow_membership_requests: false, + full_name: null, + default_notification_level: 3, + membership_request_template: null, + members_visibility_level: 0, + can_see_members: true, + can_admin_group: true, + publish_read_state: false, }, { - "id": 12, - "automatic": true, - "name": "trust_level_2", - "display_name": "trust_level_2", - "user_count": 1, - "mentionable_level": 0, - "messageable_level": 0, - "visibility_level": 1, - "primary_group": false, - "title": null, - "grant_trust_level": null, - "incoming_email": null, - "has_messages": false, - "flair_url": null, - "flair_bg_color": null, - "flair_color": null, - "bio_raw": null, - "bio_cooked": null, - "bio_excerpt": null, - "public_admission": false, - "public_exit": false, - "allow_membership_requests": false, - "full_name": null, - "default_notification_level": 3, - "membership_request_template": null, - "members_visibility_level": 0, - "can_see_members": true, - "can_admin_group": true, - "publish_read_state": false + id: 12, + automatic: true, + name: "trust_level_2", + display_name: "trust_level_2", + user_count: 1, + mentionable_level: 0, + messageable_level: 0, + visibility_level: 1, + primary_group: false, + title: null, + grant_trust_level: null, + incoming_email: null, + has_messages: false, + flair_url: null, + flair_bg_color: null, + flair_color: null, + bio_raw: null, + bio_cooked: null, + bio_excerpt: null, + public_admission: false, + public_exit: false, + allow_membership_requests: false, + full_name: null, + default_notification_level: 3, + membership_request_template: null, + members_visibility_level: 0, + can_see_members: true, + can_admin_group: true, + publish_read_state: false, }, { - "id": 13, - "automatic": true, - "name": "trust_level_3", - "display_name": "trust_level_3", - "user_count": 1, - "mentionable_level": 0, - "messageable_level": 0, - "visibility_level": 1, - "primary_group": false, - "title": null, - "grant_trust_level": null, - "incoming_email": null, - "has_messages": false, - "flair_url": null, - "flair_bg_color": null, - "flair_color": null, - "bio_raw": null, - "bio_cooked": null, - "bio_excerpt": null, - "public_admission": false, - "public_exit": false, - "allow_membership_requests": false, - "full_name": null, - "default_notification_level": 3, - "membership_request_template": null, - "members_visibility_level": 0, - "can_see_members": true, - "can_admin_group": true, - "publish_read_state": false + id: 13, + automatic: true, + name: "trust_level_3", + display_name: "trust_level_3", + user_count: 1, + mentionable_level: 0, + messageable_level: 0, + visibility_level: 1, + primary_group: false, + title: null, + grant_trust_level: null, + incoming_email: null, + has_messages: false, + flair_url: null, + flair_bg_color: null, + flair_color: null, + bio_raw: null, + bio_cooked: null, + bio_excerpt: null, + public_admission: false, + public_exit: false, + allow_membership_requests: false, + full_name: null, + default_notification_level: 3, + membership_request_template: null, + members_visibility_level: 0, + can_see_members: true, + can_admin_group: true, + publish_read_state: false, }, { - "id": 14, - "automatic": true, - "name": "trust_level_4", - "display_name": "trust_level_4", - "user_count": 0, - "mentionable_level": 0, - "messageable_level": 0, - "visibility_level": 1, - "primary_group": false, - "title": null, - "grant_trust_level": null, - "incoming_email": null, - "has_messages": false, - "flair_url": null, - "flair_bg_color": null, - "flair_color": null, - "bio_raw": null, - "bio_cooked": null, - "bio_excerpt": null, - "public_admission": false, - "public_exit": false, - "allow_membership_requests": false, - "full_name": null, - "default_notification_level": 3, - "membership_request_template": null, - "members_visibility_level": 0, - "can_see_members": true, - "can_admin_group": true, - "publish_read_state": false - } - ] -} + id: 14, + automatic: true, + name: "trust_level_4", + display_name: "trust_level_4", + user_count: 0, + mentionable_level: 0, + messageable_level: 0, + visibility_level: 1, + primary_group: false, + title: null, + grant_trust_level: null, + incoming_email: null, + has_messages: false, + flair_url: null, + flair_bg_color: null, + flair_color: null, + bio_raw: null, + bio_cooked: null, + bio_excerpt: null, + public_admission: false, + public_exit: false, + allow_membership_requests: false, + full_name: null, + default_notification_level: 3, + membership_request_template: null, + members_visibility_level: 0, + can_see_members: true, + can_admin_group: true, + publish_read_state: false, + }, + ], +}; diff --git a/assets/javascripts/wizard/tests/fixtures/site-settings.js.es6 b/assets/javascripts/wizard/tests/fixtures/site-settings.js.es6 index f71ace8e..7568cd49 100644 --- a/assets/javascripts/wizard/tests/fixtures/site-settings.js.es6 +++ b/assets/javascripts/wizard/tests/fixtures/site-settings.js.es6 @@ -1,283 +1,294 @@ export default { - "default_locale": "en", - "title": "Discourse", - "short_site_description": "", - "exclude_rel_nofollow_domains": "", - "logo": "/images/discourse-logo-sketch.png", - "logo_small": "/images/discourse-logo-sketch-small.png", - "digest_logo": "", - "mobile_logo": "", - "logo_dark": "", - "logo_small_dark": "", - "mobile_logo_dark": "", - "large_icon": "", - "favicon": "", - "apple_touch_icon": "", - "display_local_time_in_user_card": false, - "allow_user_locale": false, - "set_locale_from_accept_language_header": false, - "support_mixed_text_direction": false, - "suggested_topics": 5, - "ga_universal_tracking_code": "", - "ga_universal_domain_name": "auto", - "gtm_container_id": "", - "top_menu": "categories|latest", - "post_menu": "read|like|share|flag|edit|bookmark|delete|admin|reply", - "post_menu_hidden_items": "flag|bookmark|edit|delete|admin", - "share_links": "twitter|facebook|email", - "share_quote_visibility": "anonymous", - "share_quote_buttons": "twitter|email", - "desktop_category_page_style": "categories_and_latest_topics", - "category_colors": "BF1E2E|F1592A|F7941D|9EB83B|3AB54A|12A89D|25AAE2|0E76BD|652D90|92278F|ED207B|8C6238|231F20|808281|B3B5B4|E45735", - "category_style": "bullet", - "max_category_nesting": 2, - "enable_mobile_theme": true, - "enable_direct_s3_uploads": false, - "enable_upload_debug_mode": false, - "default_dark_mode_color_scheme_id": 1, - "relative_date_duration": 30, - "fixed_category_positions": false, - "fixed_category_positions_on_create": false, - "enable_badges": true, - "enable_badge_sql": true, - "max_favorite_badges": 2, - "enable_whispers": false, - "enable_bookmarks_with_reminders": true, - "push_notifications_prompt": true, - "vapid_public_key_bytes": "4|29|219|88|202|66|198|62|182|204|66|176|229|200|131|26|141|21|178|231|150|161|2|128|228|200|179|126|118|232|196|19|232|76|108|189|54|211|210|155|55|228|173|112|38|158|114|127|18|95|7|56|110|183|192|92|43|0|243|249|233|89|9|207|255", - "invite_only": false, - "login_required": false, - "must_approve_users": false, - "enable_local_logins": true, - "enable_local_logins_via_email": true, - "allow_new_registrations": true, - "enable_signup_cta": true, - "facebook_app_id": "", - "auth_skip_create_confirm": false, - "auth_overrides_email": false, - "enable_discourse_connect": true, - "discourse_connect_overrides_avatar": false, - "hide_email_address_taken": false, - "min_username_length": 3, - "max_username_length": 20, - "unicode_usernames": false, - "min_password_length": 10, - "min_admin_password_length": 15, - "email_editable": true, - "logout_redirect": "", - "full_name_required": false, - "enable_names": true, - "invite_expiry_days": 90, - "invites_per_page": 40, - "delete_user_max_post_age": 60, - "delete_all_posts_max": 15, - "prioritize_username_in_ux": true, - "enable_user_directory": true, - "allow_anonymous_posting": false, - "anonymous_posting_min_trust_level": 1, - "allow_users_to_hide_profile": true, - "hide_user_profiles_from_public": false, - "allow_featured_topic_on_user_profiles": true, - "hide_suspension_reasons": false, - "ignored_users_count_message_threshold": 5, - "ignored_users_message_gap_days": 365, - "user_selected_primary_groups": false, - "gravatar_name": "Gravatar", - "gravatar_base_url": "www.gravatar.com", - "gravatar_login_url": "/emails", - "enable_group_directory": true, - "enable_category_group_moderation": false, - "min_post_length": 20, - "min_first_post_length": 20, - "min_personal_message_post_length": 10, - "max_post_length": 32000, - "topic_featured_link_enabled": true, - "min_topic_views_for_delete_confirm": 5000, - "min_topic_title_length": 15, - "max_topic_title_length": 255, - "enable_filtered_replies_view": false, - "min_personal_message_title_length": 2, - "allow_uncategorized_topics": true, - "min_title_similar_length": 10, - "enable_personal_messages": true, - "edit_history_visible_to_public": true, - "delete_removed_posts_after": 24, - "traditional_markdown_linebreaks": false, - "enable_markdown_typographer": true, - "enable_markdown_linkify": true, - "markdown_linkify_tlds": "com|net|org|io|onion|co|tv|ru|cn|us|uk|me|de|fr|fi|gov", - "markdown_typographer_quotation_marks": "“|”|‘|’", - "enable_rich_text_paste": true, - "suppress_reply_directly_below": true, - "suppress_reply_directly_above": true, - "max_reply_history": 1, - "enable_mentions": true, - "here_mention": "here", - "newuser_max_embedded_media": 1, - "newuser_max_attachments": 0, - "show_pinned_excerpt_mobile": true, - "show_pinned_excerpt_desktop": true, - "display_name_on_posts": false, - "show_time_gap_days": 7, - "short_progress_text_threshold": 10000, - "default_code_lang": "auto", - "autohighlight_all_code": false, - "highlighted_languages": "apache|bash|cs|cpp|css|coffeescript|diff|xml|http|ini|json|java|javascript|makefile|markdown|nginx|objectivec|ruby|perl|php|python|sql|handlebars", - "show_copy_button_on_codeblocks": false, - "enable_emoji": true, - "enable_emoji_shortcuts": true, - "emoji_set": "twitter", - "emoji_autocomplete_min_chars": 0, - "enable_inline_emoji_translation": false, - "code_formatting_style": "code-fences", - "allowed_href_schemes": "", - "watched_words_regular_expressions": false, - "enable_diffhtml_preview": false, - "enable_fast_edit": true, - "old_post_notice_days": 14, - "blur_tl0_flagged_posts_media": true, - "email_time_window_mins": 10, - "disable_digest_emails": false, - "email_in": false, - "enable_imap": false, - "enable_smtp": false, - "disable_emails": "no", - "bounce_score_threshold": 4, - "enable_secondary_emails": true, - "max_image_size_kb": 4096, - "max_attachment_size_kb": 4096, - "authorized_extensions": "jpg|jpeg|png|gif|heic|heif|webp", - "authorized_extensions_for_staff": "", - "max_image_width": 690, - "max_image_height": 500, - "prevent_anons_from_downloading_files": false, - "secure_media": false, - "enable_s3_uploads": false, - "allow_profile_backgrounds": true, - "allow_uploaded_avatars": "0", - "default_avatars": "", - "external_system_avatars_enabled": true, - "external_system_avatars_url": "/letter_avatar_proxy/v4/letter/{first_letter}/{color}/{size}.png", - "external_emoji_url": "", - "selectable_avatars_mode": "disabled", - "selectable_avatars": "", - "allow_staff_to_upload_any_file_in_pm": true, - "simultaneous_uploads": 5, - "composer_media_optimization_image_enabled": true, - "composer_media_optimization_image_bytes_optimization_threshold": 524288, - "composer_media_optimization_image_resize_dimensions_threshold": 1920, - "composer_media_optimization_image_resize_width_target": 1920, - "composer_media_optimization_image_resize_pre_multiply": false, - "composer_media_optimization_image_resize_linear_rgb": false, - "composer_media_optimization_image_encode_quality": 75, - "composer_media_optimization_debug_mode": false, - "min_trust_level_to_allow_profile_background": 0, - "min_trust_level_to_allow_user_card_background": 0, - "min_trust_level_to_allow_ignore": 2, - "tl1_requires_read_posts": 30, - "tl3_links_no_follow": false, - "enforce_second_factor": "no", - "moderators_change_post_ownership": false, - "moderators_view_emails": false, - "use_admin_ip_allowlist": false, - "allowed_iframes": "https://www.google.com/maps/embed?|https://www.openstreetmap.org/export/embed.html?|https://calendar.google.com/calendar/embed?|https://codepen.io/|https://www.instagram.com|http://localhost:3000/discobot/certificate.svg", - "can_permanently_delete": false, - "max_oneboxes_per_post": 50, - "reviewable_claiming": "disabled", - "reviewable_default_topics": false, - "reviewable_default_visibility": "low", - "alert_admins_if_errors_per_minute": 0, - "alert_admins_if_errors_per_hour": 0, - "max_prints_per_hour_per_user": 5, - "invite_link_max_redemptions_limit": 5000, - "invite_link_max_redemptions_limit_users": 10, - "enable_long_polling": true, - "enable_chunked_encoding": true, - "long_polling_base_url": "/", - "background_polling_interval": 60000, - "polling_interval": 3000, - "anon_polling_interval": 25000, - "flush_timings_secs": 60, - "verbose_localization": false, - "max_new_topics": 500, - "enable_safe_mode": true, - "tos_url": "", - "privacy_policy_url": "", - "faq_url": "", - "enable_backups": true, - "backup_location": "local", - "maximum_backups": 5, - "use_pg_headlines_for_excerpt": false, - "min_search_term_length": 3, - "log_search_queries": true, - "version_checks": true, - "suppress_uncategorized_badge": true, - "header_dropdown_category_count": 8, - "slug_generation_method": "ascii", - "summary_timeline_button": false, - "topic_views_heat_low": 1000, - "topic_views_heat_medium": 2000, - "topic_views_heat_high": 3500, - "topic_post_like_heat_low": 0.5, - "topic_post_like_heat_medium": 1, - "topic_post_like_heat_high": 2, - "history_hours_low": 12, - "history_hours_medium": 24, - "history_hours_high": 48, - "cold_age_days_low": 14, - "cold_age_days_medium": 90, - "cold_age_days_high": 180, - "global_notice": "", - "show_create_topics_notice": true, - "bootstrap_mode_min_users": 50, - "bootstrap_mode_enabled": true, - "automatically_unpin_topics": true, - "read_time_word_count": 500, - "topic_page_title_includes_category": true, - "svg_icon_subset": "", - "allow_bulk_invite": true, - "disable_mailing_list_mode": true, - "default_topics_automatic_unpin": true, - "mute_all_categories_by_default": false, - "tagging_enabled": true, - "tag_style": "simple", - "max_tags_per_topic": 5, - "max_tag_length": 20, - "min_trust_level_to_tag_topics": "0", - "max_tag_search_results": 5, - "max_tags_in_filter_list": 30, - "tags_sort_alphabetically": false, - "tags_listed_by_group": false, - "suppress_overlapping_tags_in_list": false, - "remove_muted_tags_from_latest": "always", - "force_lowercase_tags": true, - "dashboard_hidden_reports": "", - "dashboard_visible_tabs": "moderation|security|reports", - "dashboard_general_tab_activity_metrics": "page_view_total_reqs|visits|time_to_first_response|likes|flags|user_to_user_private_messages_with_replies", - "discourse_narrative_bot_enabled": true, - "details_enabled": true, - "custom_wizard_enabled": true, - "wizard_redirect_exclude_paths": "admin", - "wizard_recognised_image_upload_formats": "jpg|jpeg|png|gif", - "wizard_important_notices_on_dashboard": true, - "discourse_local_dates_email_format": "YYYY-MM-DDTHH:mm:ss[Z]", - "discourse_local_dates_enabled": true, - "discourse_local_dates_default_formats": "LLL|LTS|LL|LLLL", - "discourse_local_dates_default_timezones": "Europe/Paris|America/Los_Angeles", - "poll_enabled": true, - "poll_maximum_options": 20, - "poll_minimum_trust_level_to_create": 1, - "poll_groupable_user_fields": "", - "poll_export_data_explorer_query_id": -16, - "presence_enabled": true, - "presence_max_users_shown": 5, - "available_locales": "[{\"name\":\"اللغة العربية\",\"value\":\"ar\"},{\"name\":\"беларуская мова\",\"value\":\"be\"},{\"name\":\"български език\",\"value\":\"bg\"},{\"name\":\"bosanski jezik\",\"value\":\"bs_BA\"},{\"name\":\"català\",\"value\":\"ca\"},{\"name\":\"čeština\",\"value\":\"cs\"},{\"name\":\"dansk\",\"value\":\"da\"},{\"name\":\"Deutsch\",\"value\":\"de\"},{\"name\":\"ελληνικά\",\"value\":\"el\"},{\"name\":\"English (US)\",\"value\":\"en\"},{\"name\":\"English (UK)\",\"value\":\"en_GB\"},{\"name\":\"Español\",\"value\":\"es\"},{\"name\":\"eesti\",\"value\":\"et\"},{\"name\":\"فارسی\",\"value\":\"fa_IR\"},{\"name\":\"suomi\",\"value\":\"fi\"},{\"name\":\"Français\",\"value\":\"fr\"},{\"name\":\"galego\",\"value\":\"gl\"},{\"name\":\"עברית\",\"value\":\"he\"},{\"name\":\"magyar\",\"value\":\"hu\"},{\"name\":\"Հայերեն\",\"value\":\"hy\"},{\"name\":\"Indonesian\",\"value\":\"id\"},{\"name\":\"Italiano\",\"value\":\"it\"},{\"name\":\"日本語\",\"value\":\"ja\"},{\"name\":\"한국어\",\"value\":\"ko\"},{\"name\":\"lietuvių kalba\",\"value\":\"lt\"},{\"name\":\"latviešu valoda\",\"value\":\"lv\"},{\"name\":\"Norsk bokmål\",\"value\":\"nb_NO\"},{\"name\":\"Nederlands\",\"value\":\"nl\"},{\"name\":\"polski\",\"value\":\"pl_PL\"},{\"name\":\"Português\",\"value\":\"pt\"},{\"name\":\"Português (BR)\",\"value\":\"pt_BR\"},{\"name\":\"limba română\",\"value\":\"ro\"},{\"name\":\"Русский\",\"value\":\"ru\"},{\"name\":\"slovenčina\",\"value\":\"sk\"},{\"name\":\"slovenščina\",\"value\":\"sl\"},{\"name\":\"Shqip\",\"value\":\"sq\"},{\"name\":\"српски језик\",\"value\":\"sr\"},{\"name\":\"svenska\",\"value\":\"sv\"},{\"name\":\"Kiswahili\",\"value\":\"sw\"},{\"name\":\"తెలుగు\",\"value\":\"te\"},{\"name\":\"ไทย\",\"value\":\"th\"},{\"name\":\"Türkçe\",\"value\":\"tr_TR\"},{\"name\":\"українська мова\",\"value\":\"uk\"},{\"name\":\"اردو\",\"value\":\"ur\"},{\"name\":\"Việt Nam\",\"value\":\"vi\"},{\"name\":\"简体中文\",\"value\":\"zh_CN\"},{\"name\":\"繁體中文\",\"value\":\"zh_TW\"}]", - "require_invite_code": false, - "site_logo_url": "http://localhost:3000/images/discourse-logo-sketch.png", - "site_logo_small_url": "http://localhost:3000/images/discourse-logo-sketch-small.png", - "site_mobile_logo_url": "http://localhost:3000/images/discourse-logo-sketch.png", - "site_favicon_url": "http://localhost:3000/uploads/default/optimized/1X/_129430568242d1b7f853bb13ebea28b3f6af4e7_2_32x32.png", - "site_logo_dark_url": "", - "site_logo_small_dark_url": "", - "site_mobile_logo_dark_url": "" -} + default_locale: "en", + title: "Discourse", + short_site_description: "", + exclude_rel_nofollow_domains: "", + logo: "/images/discourse-logo-sketch.png", + logo_small: "/images/discourse-logo-sketch-small.png", + digest_logo: "", + mobile_logo: "", + logo_dark: "", + logo_small_dark: "", + mobile_logo_dark: "", + large_icon: "", + favicon: "", + apple_touch_icon: "", + display_local_time_in_user_card: false, + allow_user_locale: false, + set_locale_from_accept_language_header: false, + support_mixed_text_direction: false, + suggested_topics: 5, + ga_universal_tracking_code: "", + ga_universal_domain_name: "auto", + gtm_container_id: "", + top_menu: "categories|latest", + post_menu: "read|like|share|flag|edit|bookmark|delete|admin|reply", + post_menu_hidden_items: "flag|bookmark|edit|delete|admin", + share_links: "twitter|facebook|email", + share_quote_visibility: "anonymous", + share_quote_buttons: "twitter|email", + desktop_category_page_style: "categories_and_latest_topics", + category_colors: + "BF1E2E|F1592A|F7941D|9EB83B|3AB54A|12A89D|25AAE2|0E76BD|652D90|92278F|ED207B|8C6238|231F20|808281|B3B5B4|E45735", + category_style: "bullet", + max_category_nesting: 2, + enable_mobile_theme: true, + enable_direct_s3_uploads: false, + enable_upload_debug_mode: false, + default_dark_mode_color_scheme_id: 1, + relative_date_duration: 30, + fixed_category_positions: false, + fixed_category_positions_on_create: false, + enable_badges: true, + enable_badge_sql: true, + max_favorite_badges: 2, + enable_whispers: false, + enable_bookmarks_with_reminders: true, + push_notifications_prompt: true, + vapid_public_key_bytes: + "4|29|219|88|202|66|198|62|182|204|66|176|229|200|131|26|141|21|178|231|150|161|2|128|228|200|179|126|118|232|196|19|232|76|108|189|54|211|210|155|55|228|173|112|38|158|114|127|18|95|7|56|110|183|192|92|43|0|243|249|233|89|9|207|255", + invite_only: false, + login_required: false, + must_approve_users: false, + enable_local_logins: true, + enable_local_logins_via_email: true, + allow_new_registrations: true, + enable_signup_cta: true, + facebook_app_id: "", + auth_skip_create_confirm: false, + auth_overrides_email: false, + enable_discourse_connect: true, + discourse_connect_overrides_avatar: false, + hide_email_address_taken: false, + min_username_length: 3, + max_username_length: 20, + unicode_usernames: false, + min_password_length: 10, + min_admin_password_length: 15, + email_editable: true, + logout_redirect: "", + full_name_required: false, + enable_names: true, + invite_expiry_days: 90, + invites_per_page: 40, + delete_user_max_post_age: 60, + delete_all_posts_max: 15, + prioritize_username_in_ux: true, + enable_user_directory: true, + allow_anonymous_posting: false, + anonymous_posting_min_trust_level: 1, + allow_users_to_hide_profile: true, + hide_user_profiles_from_public: false, + allow_featured_topic_on_user_profiles: true, + hide_suspension_reasons: false, + ignored_users_count_message_threshold: 5, + ignored_users_message_gap_days: 365, + user_selected_primary_groups: false, + gravatar_name: "Gravatar", + gravatar_base_url: "www.gravatar.com", + gravatar_login_url: "/emails", + enable_group_directory: true, + enable_category_group_moderation: false, + min_post_length: 20, + min_first_post_length: 20, + min_personal_message_post_length: 10, + max_post_length: 32000, + topic_featured_link_enabled: true, + min_topic_views_for_delete_confirm: 5000, + min_topic_title_length: 15, + max_topic_title_length: 255, + enable_filtered_replies_view: false, + min_personal_message_title_length: 2, + allow_uncategorized_topics: true, + min_title_similar_length: 10, + enable_personal_messages: true, + edit_history_visible_to_public: true, + delete_removed_posts_after: 24, + traditional_markdown_linebreaks: false, + enable_markdown_typographer: true, + enable_markdown_linkify: true, + markdown_linkify_tlds: + "com|net|org|io|onion|co|tv|ru|cn|us|uk|me|de|fr|fi|gov", + markdown_typographer_quotation_marks: "“|”|‘|’", + enable_rich_text_paste: true, + suppress_reply_directly_below: true, + suppress_reply_directly_above: true, + max_reply_history: 1, + enable_mentions: true, + here_mention: "here", + newuser_max_embedded_media: 1, + newuser_max_attachments: 0, + show_pinned_excerpt_mobile: true, + show_pinned_excerpt_desktop: true, + display_name_on_posts: false, + show_time_gap_days: 7, + short_progress_text_threshold: 10000, + default_code_lang: "auto", + autohighlight_all_code: false, + highlighted_languages: + "apache|bash|cs|cpp|css|coffeescript|diff|xml|http|ini|json|java|javascript|makefile|markdown|nginx|objectivec|ruby|perl|php|python|sql|handlebars", + show_copy_button_on_codeblocks: false, + enable_emoji: true, + enable_emoji_shortcuts: true, + emoji_set: "twitter", + emoji_autocomplete_min_chars: 0, + enable_inline_emoji_translation: false, + code_formatting_style: "code-fences", + allowed_href_schemes: "", + watched_words_regular_expressions: false, + enable_diffhtml_preview: false, + enable_fast_edit: true, + old_post_notice_days: 14, + blur_tl0_flagged_posts_media: true, + email_time_window_mins: 10, + disable_digest_emails: false, + email_in: false, + enable_imap: false, + enable_smtp: false, + disable_emails: "no", + bounce_score_threshold: 4, + enable_secondary_emails: true, + max_image_size_kb: 4096, + max_attachment_size_kb: 4096, + authorized_extensions: "jpg|jpeg|png|gif|heic|heif|webp", + authorized_extensions_for_staff: "", + max_image_width: 690, + max_image_height: 500, + prevent_anons_from_downloading_files: false, + secure_media: false, + enable_s3_uploads: false, + allow_profile_backgrounds: true, + allow_uploaded_avatars: "0", + default_avatars: "", + external_system_avatars_enabled: true, + external_system_avatars_url: + "/letter_avatar_proxy/v4/letter/{first_letter}/{color}/{size}.png", + external_emoji_url: "", + selectable_avatars_mode: "disabled", + selectable_avatars: "", + allow_staff_to_upload_any_file_in_pm: true, + simultaneous_uploads: 5, + composer_media_optimization_image_enabled: true, + composer_media_optimization_image_bytes_optimization_threshold: 524288, + composer_media_optimization_image_resize_dimensions_threshold: 1920, + composer_media_optimization_image_resize_width_target: 1920, + composer_media_optimization_image_resize_pre_multiply: false, + composer_media_optimization_image_resize_linear_rgb: false, + composer_media_optimization_image_encode_quality: 75, + composer_media_optimization_debug_mode: false, + min_trust_level_to_allow_profile_background: 0, + min_trust_level_to_allow_user_card_background: 0, + min_trust_level_to_allow_ignore: 2, + tl1_requires_read_posts: 30, + tl3_links_no_follow: false, + enforce_second_factor: "no", + moderators_change_post_ownership: false, + moderators_view_emails: false, + use_admin_ip_allowlist: false, + allowed_iframes: + "https://www.google.com/maps/embed?|https://www.openstreetmap.org/export/embed.html?|https://calendar.google.com/calendar/embed?|https://codepen.io/|https://www.instagram.com|http://localhost:3000/discobot/certificate.svg", + can_permanently_delete: false, + max_oneboxes_per_post: 50, + reviewable_claiming: "disabled", + reviewable_default_topics: false, + reviewable_default_visibility: "low", + alert_admins_if_errors_per_minute: 0, + alert_admins_if_errors_per_hour: 0, + max_prints_per_hour_per_user: 5, + invite_link_max_redemptions_limit: 5000, + invite_link_max_redemptions_limit_users: 10, + enable_long_polling: true, + enable_chunked_encoding: true, + long_polling_base_url: "/", + background_polling_interval: 60000, + polling_interval: 3000, + anon_polling_interval: 25000, + flush_timings_secs: 60, + verbose_localization: false, + max_new_topics: 500, + enable_safe_mode: true, + tos_url: "", + privacy_policy_url: "", + faq_url: "", + enable_backups: true, + backup_location: "local", + maximum_backups: 5, + use_pg_headlines_for_excerpt: false, + min_search_term_length: 3, + log_search_queries: true, + version_checks: true, + suppress_uncategorized_badge: true, + header_dropdown_category_count: 8, + slug_generation_method: "ascii", + summary_timeline_button: false, + topic_views_heat_low: 1000, + topic_views_heat_medium: 2000, + topic_views_heat_high: 3500, + topic_post_like_heat_low: 0.5, + topic_post_like_heat_medium: 1, + topic_post_like_heat_high: 2, + history_hours_low: 12, + history_hours_medium: 24, + history_hours_high: 48, + cold_age_days_low: 14, + cold_age_days_medium: 90, + cold_age_days_high: 180, + global_notice: "", + show_create_topics_notice: true, + bootstrap_mode_min_users: 50, + bootstrap_mode_enabled: true, + automatically_unpin_topics: true, + read_time_word_count: 500, + topic_page_title_includes_category: true, + svg_icon_subset: "", + allow_bulk_invite: true, + disable_mailing_list_mode: true, + default_topics_automatic_unpin: true, + mute_all_categories_by_default: false, + tagging_enabled: true, + tag_style: "simple", + max_tags_per_topic: 5, + max_tag_length: 20, + min_trust_level_to_tag_topics: "0", + max_tag_search_results: 5, + max_tags_in_filter_list: 30, + tags_sort_alphabetically: false, + tags_listed_by_group: false, + suppress_overlapping_tags_in_list: false, + remove_muted_tags_from_latest: "always", + force_lowercase_tags: true, + dashboard_hidden_reports: "", + dashboard_visible_tabs: "moderation|security|reports", + dashboard_general_tab_activity_metrics: + "page_view_total_reqs|visits|time_to_first_response|likes|flags|user_to_user_private_messages_with_replies", + discourse_narrative_bot_enabled: true, + details_enabled: true, + custom_wizard_enabled: true, + wizard_redirect_exclude_paths: "admin", + wizard_recognised_image_upload_formats: "jpg|jpeg|png|gif", + wizard_important_notices_on_dashboard: true, + discourse_local_dates_email_format: "YYYY-MM-DDTHH:mm:ss[Z]", + discourse_local_dates_enabled: true, + discourse_local_dates_default_formats: "LLL|LTS|LL|LLLL", + discourse_local_dates_default_timezones: "Europe/Paris|America/Los_Angeles", + poll_enabled: true, + poll_maximum_options: 20, + poll_minimum_trust_level_to_create: 1, + poll_groupable_user_fields: "", + poll_export_data_explorer_query_id: -16, + presence_enabled: true, + presence_max_users_shown: 5, + available_locales: + '[{"name":"اللغة العربية","value":"ar"},{"name":"беларуская мова","value":"be"},{"name":"български език","value":"bg"},{"name":"bosanski jezik","value":"bs_BA"},{"name":"català","value":"ca"},{"name":"čeština","value":"cs"},{"name":"dansk","value":"da"},{"name":"Deutsch","value":"de"},{"name":"ελληνικά","value":"el"},{"name":"English (US)","value":"en"},{"name":"English (UK)","value":"en_GB"},{"name":"Español","value":"es"},{"name":"eesti","value":"et"},{"name":"فارسی","value":"fa_IR"},{"name":"suomi","value":"fi"},{"name":"Français","value":"fr"},{"name":"galego","value":"gl"},{"name":"עברית","value":"he"},{"name":"magyar","value":"hu"},{"name":"Հայերեն","value":"hy"},{"name":"Indonesian","value":"id"},{"name":"Italiano","value":"it"},{"name":"日本語","value":"ja"},{"name":"한국어","value":"ko"},{"name":"lietuvių kalba","value":"lt"},{"name":"latviešu valoda","value":"lv"},{"name":"Norsk bokmål","value":"nb_NO"},{"name":"Nederlands","value":"nl"},{"name":"polski","value":"pl_PL"},{"name":"Português","value":"pt"},{"name":"Português (BR)","value":"pt_BR"},{"name":"limba română","value":"ro"},{"name":"Русский","value":"ru"},{"name":"slovenčina","value":"sk"},{"name":"slovenščina","value":"sl"},{"name":"Shqip","value":"sq"},{"name":"српски језик","value":"sr"},{"name":"svenska","value":"sv"},{"name":"Kiswahili","value":"sw"},{"name":"తెలుగు","value":"te"},{"name":"ไทย","value":"th"},{"name":"Türkçe","value":"tr_TR"},{"name":"українська мова","value":"uk"},{"name":"اردو","value":"ur"},{"name":"Việt Nam","value":"vi"},{"name":"简体中文","value":"zh_CN"},{"name":"繁體中文","value":"zh_TW"}]', + require_invite_code: false, + site_logo_url: "http://localhost:3000/images/discourse-logo-sketch.png", + site_logo_small_url: + "http://localhost:3000/images/discourse-logo-sketch-small.png", + site_mobile_logo_url: + "http://localhost:3000/images/discourse-logo-sketch.png", + site_favicon_url: + "http://localhost:3000/uploads/default/optimized/1X/_129430568242d1b7f853bb13ebea28b3f6af4e7_2_32x32.png", + site_logo_dark_url: "", + site_logo_small_dark_url: "", + site_mobile_logo_dark_url: "", +}; diff --git a/assets/javascripts/wizard/tests/fixtures/tags.js.es6 b/assets/javascripts/wizard/tests/fixtures/tags.js.es6 index a072772e..61e51994 100644 --- a/assets/javascripts/wizard/tests/fixtures/tags.js.es6 +++ b/assets/javascripts/wizard/tests/fixtures/tags.js.es6 @@ -1,22 +1,22 @@ export default { - "tags": [ + tags: [ { - "id": "tag1", - "text": "tag1", - "name": "tag1", - "description": null, - "count": 1, - "pm_count": 0, - "target_tag": null + id: "tag1", + text: "tag1", + name: "tag1", + description: null, + count: 1, + pm_count: 0, + target_tag: null, }, { - "id": "tag2", - "text": "tag2", - "name": "tag2", - "description": null, - "count": 1, - "pm_count": 0, - "target_tag": null - } - ] -} + id: "tag2", + text: "tag2", + name: "tag2", + description: null, + count: 1, + pm_count: 0, + target_tag: null, + }, + ], +}; diff --git a/assets/javascripts/wizard/tests/fixtures/update.js.es6 b/assets/javascripts/wizard/tests/fixtures/update.js.es6 index 3908525c..5b20788c 100644 --- a/assets/javascripts/wizard/tests/fixtures/update.js.es6 +++ b/assets/javascripts/wizard/tests/fixtures/update.js.es6 @@ -1,5 +1,5 @@ export default { - "final": false, - "next_step_id": "step_2", - "wizard": {} -} + final: false, + next_step_id: "step_2", + wizard: {}, +}; diff --git a/assets/javascripts/wizard/tests/fixtures/user.js.es6 b/assets/javascripts/wizard/tests/fixtures/user.js.es6 index 8acd7392..d954905c 100644 --- a/assets/javascripts/wizard/tests/fixtures/user.js.es6 +++ b/assets/javascripts/wizard/tests/fixtures/user.js.es6 @@ -30,5 +30,5 @@ export default { title_count_mode: "notifications", timezone: "Australia/Perth", skip_new_user_tips: false, - can_review: true -} + can_review: true, +}; diff --git a/assets/javascripts/wizard/tests/fixtures/users.js.es6 b/assets/javascripts/wizard/tests/fixtures/users.js.es6 index 267d4909..437631a2 100644 --- a/assets/javascripts/wizard/tests/fixtures/users.js.es6 +++ b/assets/javascripts/wizard/tests/fixtures/users.js.es6 @@ -1,14 +1,14 @@ export default { - "users": [ + users: [ { - "username": "angus", - "name": "Angus", - "avatar_template": "/user_avatar/localhost/angus/{size}/12_2.png" + username: "angus", + name: "Angus", + avatar_template: "/user_avatar/localhost/angus/{size}/12_2.png", }, { - "username": "angus_2", - "name": "Angus 2", - "avatar_template": "/letter_avatar_proxy/v4/letter/a/e9a140/{size}.png" - } - ] -} + username: "angus_2", + name: "Angus 2", + avatar_template: "/letter_avatar_proxy/v4/letter/a/e9a140/{size}.png", + }, + ], +}; diff --git a/assets/javascripts/wizard/tests/fixtures/wizard.js.es6 b/assets/javascripts/wizard/tests/fixtures/wizard.js.es6 index be4fa8b2..73fe45c1 100644 --- a/assets/javascripts/wizard/tests/fixtures/wizard.js.es6 +++ b/assets/javascripts/wizard/tests/fixtures/wizard.js.es6 @@ -1,469 +1,471 @@ export default { - "id": "wizard", - "name": "Wizard", - "start": "step_1", - "background": "#333333", - "submission_last_updated_at": "2022-03-15T21:11:01+01:00", - "theme_id": 2, - "required": false, - "permitted": true, - "uncategorized_category_id": 1, - "categories": [], - "subscribed": false, - "resume_on_revisit": false, - "steps": [ + id: "wizard", + name: "Wizard", + start: "step_1", + background: "#333333", + submission_last_updated_at: "2022-03-15T21:11:01+01:00", + theme_id: 2, + required: false, + permitted: true, + uncategorized_category_id: 1, + categories: [], + subscribed: false, + resume_on_revisit: false, + steps: [ { - "id": "step_1", - "index": 0, - "next": "step_2", - "description": "

Text inputs!

", - "title": "Text", - "permitted": true, - "permitted_message": null, - "final": false, - "fields": [ + id: "step_1", + index: 0, + next: "step_2", + description: "

Text inputs!

", + title: "Text", + permitted: true, + permitted_message: null, + final: false, + fields: [ { - "id": "step_1_field_1", - "index": 0, - "type": "text", - "required": false, - "value": "I am prefilled", - "label": "

Text

", - "description": "Text field description.", - "file_types": null, - "format": null, - "limit": null, - "property": null, - "content": null, - "validations": {}, - "max_length": null, - "char_counter": null, - "preview_template": null, - "tabindex": 1, - "wizardId": "super_mega_fun_wizard", - "stepId": "step_1", - "_validState": 0 + id: "step_1_field_1", + index: 0, + type: "text", + required: false, + value: "I am prefilled", + label: "

Text

", + description: "Text field description.", + file_types: null, + format: null, + limit: null, + property: null, + content: null, + validations: {}, + max_length: null, + char_counter: null, + preview_template: null, + tabindex: 1, + wizardId: "super_mega_fun_wizard", + stepId: "step_1", + _validState: 0, }, { - "id": "step_1_field_2", - "index": 0, - "type": "textarea", - "required": false, - "value": "", - "label": "

Textarea

", - "file_types": null, - "format": null, - "limit": null, - "property": null, - "content": null, - "validations": {}, - "max_length": null, - "char_counter": null, - "preview_template": null, - "tabindex": 2, - "wizardId": "super_mega_fun_wizard", - "stepId": "step_1", - "_validState": 0 + id: "step_1_field_2", + index: 0, + type: "textarea", + required: false, + value: "", + label: "

Textarea

", + file_types: null, + format: null, + limit: null, + property: null, + content: null, + validations: {}, + max_length: null, + char_counter: null, + preview_template: null, + tabindex: 2, + wizardId: "super_mega_fun_wizard", + stepId: "step_1", + _validState: 0, }, { - "id": "step_1_field_3", - "index": 2, - "type": "composer", - "required": false, - "value": "", - "label": "

Composer

", - "file_types": null, - "format": null, - "limit": null, - "property": null, - "content": null, - "validations": {}, - "max_length": null, - "char_counter": null, - "preview_template": null, - "tabindex": 3, - "wizardId": "super_mega_fun_wizard", - "stepId": "step_1", - "_validState": 0 + id: "step_1_field_3", + index: 2, + type: "composer", + required: false, + value: "", + label: "

Composer

", + file_types: null, + format: null, + limit: null, + property: null, + content: null, + validations: {}, + max_length: null, + char_counter: null, + preview_template: null, + tabindex: 3, + wizardId: "super_mega_fun_wizard", + stepId: "step_1", + _validState: 0, }, { - "id": "step_1_field_4", - "index": 3, - "type": "text_only", - "required": false, - "value": null, - "label": "

I’m only text

", - "file_types": null, - "format": null, - "limit": null, - "property": null, - "content": null, - "validations": {}, - "max_length": null, - "char_counter": null, - "preview_template": null, - "tabindex": 4, - "wizardId": "super_mega_fun_wizard", - "stepId": "step_1", - "_validState": 0 + id: "step_1_field_4", + index: 3, + type: "text_only", + required: false, + value: null, + label: "

I’m only text

", + file_types: null, + format: null, + limit: null, + property: null, + content: null, + validations: {}, + max_length: null, + char_counter: null, + preview_template: null, + tabindex: 4, + wizardId: "super_mega_fun_wizard", + stepId: "step_1", + _validState: 0, }, { - "id": "step_1_field_5", - "index": 4, - "type": "composer_preview", - "required": false, - "value": "", - "label": "

I’m a preview

", - "file_types": null, - "format": null, - "limit": null, - "property": null, - "content": null, - "validations": {}, - "max_length": null, - "char_counter": null, - "preview_template": "

I am prefilled

", - "tabindex": 5, - "wizardId": "super_mega_fun_wizard", - "stepId": "step_1", - "_validState": 0 + id: "step_1_field_5", + index: 4, + type: "composer_preview", + required: false, + value: "", + label: "

I’m a preview

", + file_types: null, + format: null, + limit: null, + property: null, + content: null, + validations: {}, + max_length: null, + char_counter: null, + preview_template: "

I am prefilled

", + tabindex: 5, + wizardId: "super_mega_fun_wizard", + stepId: "step_1", + _validState: 0, }, { - "id": "step_1_field_6", - "index": 5, - "type": "composer_preview", - "required": false, - "value": "", - "file_types": null, - "format": null, - "limit": null, - "property": null, - "content": null, - "validations": {}, - "max_length": null, - "char_counter": null, - "preview_template": "

This is the preview of the composer

", - "tabindex": 6, - "wizardId": "super_mega_fun_wizard", - "stepId": "step_1", - "_validState": 0 - } + id: "step_1_field_6", + index: 5, + type: "composer_preview", + required: false, + value: "", + file_types: null, + format: null, + limit: null, + property: null, + content: null, + validations: {}, + max_length: null, + char_counter: null, + preview_template: "

This is the preview of the composer

", + tabindex: 6, + wizardId: "super_mega_fun_wizard", + stepId: "step_1", + _validState: 0, + }, ], - "_validState": 0, - "wizardId": "super_mega_fun_wizard" + _validState: 0, + wizardId: "super_mega_fun_wizard", }, { - "id": "step_2", - "index": 1, - "next": "step_3", - "previous": "step_1", - "description": "

Because I couldn’t think of another name for this step \":slight_smile:\"

", - "title": "Values", - "permitted": true, - "permitted_message": null, - "final": false, - "fields": [ + id: "step_2", + index: 1, + next: "step_3", + previous: "step_1", + description: + '

Because I couldn’t think of another name for this step :slight_smile:

', + title: "Values", + permitted: true, + permitted_message: null, + final: false, + fields: [ { - "id": "step_2_field_1", - "index": 0, - "type": "date", - "required": false, - "value": "", - "label": "

Date

", - "file_types": null, - "format": "YYYY-MM-DD", - "limit": null, - "property": null, - "content": null, - "validations": {}, - "max_length": null, - "char_counter": null, - "preview_template": null, - "tabindex": 1, - "wizardId": "super_mega_fun_wizard", - "stepId": "step_2", - "_validState": 0 + id: "step_2_field_1", + index: 0, + type: "date", + required: false, + value: "", + label: "

Date

", + file_types: null, + format: "YYYY-MM-DD", + limit: null, + property: null, + content: null, + validations: {}, + max_length: null, + char_counter: null, + preview_template: null, + tabindex: 1, + wizardId: "super_mega_fun_wizard", + stepId: "step_2", + _validState: 0, }, { - "id": "step_2_field_2", - "index": 0, - "type": "time", - "required": false, - "value": "", - "label": "

Time

", - "file_types": null, - "format": "HH:mm", - "limit": null, - "property": null, - "content": null, - "validations": {}, - "max_length": null, - "char_counter": null, - "preview_template": null, - "tabindex": 2, - "wizardId": "super_mega_fun_wizard", - "stepId": "step_2", - "_validState": 0 + id: "step_2_field_2", + index: 0, + type: "time", + required: false, + value: "", + label: "

Time

", + file_types: null, + format: "HH:mm", + limit: null, + property: null, + content: null, + validations: {}, + max_length: null, + char_counter: null, + preview_template: null, + tabindex: 2, + wizardId: "super_mega_fun_wizard", + stepId: "step_2", + _validState: 0, }, { - "id": "step_2_field_3", - "index": 2, - "type": "date_time", - "required": false, - "value": "", - "label": "

Date & Time

", - "file_types": null, - "format": "", - "limit": null, - "property": null, - "content": null, - "validations": {}, - "max_length": null, - "char_counter": null, - "preview_template": null, - "tabindex": 3, - "wizardId": "super_mega_fun_wizard", - "stepId": "step_2", - "_validState": 0 + id: "step_2_field_3", + index: 2, + type: "date_time", + required: false, + value: "", + label: "

Date & Time

", + file_types: null, + format: "", + limit: null, + property: null, + content: null, + validations: {}, + max_length: null, + char_counter: null, + preview_template: null, + tabindex: 3, + wizardId: "super_mega_fun_wizard", + stepId: "step_2", + _validState: 0, }, { - "id": "step_2_field_4", - "index": 3, - "type": "number", - "required": false, - "value": "", - "label": "

Number

", - "file_types": null, - "format": null, - "limit": null, - "property": null, - "content": null, - "validations": {}, - "max_length": null, - "char_counter": null, - "preview_template": null, - "tabindex": 5, - "wizardId": "super_mega_fun_wizard", - "stepId": "step_2", - "_validState": 0 + id: "step_2_field_4", + index: 3, + type: "number", + required: false, + value: "", + label: "

Number

", + file_types: null, + format: null, + limit: null, + property: null, + content: null, + validations: {}, + max_length: null, + char_counter: null, + preview_template: null, + tabindex: 5, + wizardId: "super_mega_fun_wizard", + stepId: "step_2", + _validState: 0, }, { - "id": "step_2_field_5", - "index": 4, - "type": "checkbox", - "required": false, - "value": false, - "label": "

Checkbox

", - "file_types": null, - "format": null, - "limit": null, - "property": null, - "content": null, - "validations": {}, - "max_length": null, - "char_counter": null, - "preview_template": null, - "tabindex": 6, - "wizardId": "super_mega_fun_wizard", - "stepId": "step_2", - "_validState": 0 + id: "step_2_field_5", + index: 4, + type: "checkbox", + required: false, + value: false, + label: "

Checkbox

", + file_types: null, + format: null, + limit: null, + property: null, + content: null, + validations: {}, + max_length: null, + char_counter: null, + preview_template: null, + tabindex: 6, + wizardId: "super_mega_fun_wizard", + stepId: "step_2", + _validState: 0, }, { - "id": "step_2_field_6", - "index": 5, - "type": "url", - "required": false, - "value": "", - "label": "

Url

", - "file_types": null, - "format": null, - "limit": null, - "property": null, - "content": null, - "validations": {}, - "max_length": null, - "char_counter": null, - "preview_template": null, - "tabindex": 7, - "wizardId": "super_mega_fun_wizard", - "stepId": "step_2", - "_validState": 0 + id: "step_2_field_6", + index: 5, + type: "url", + required: false, + value: "", + label: "

Url

", + file_types: null, + format: null, + limit: null, + property: null, + content: null, + validations: {}, + max_length: null, + char_counter: null, + preview_template: null, + tabindex: 7, + wizardId: "super_mega_fun_wizard", + stepId: "step_2", + _validState: 0, }, { - "id": "step_2_field_7", - "index": 6, - "type": "upload", - "required": false, - "value": "", - "label": "

Upload

", - "file_types": ".jpg,.jpeg,.png", - "format": null, - "limit": null, - "property": null, - "content": null, - "validations": {}, - "max_length": null, - "char_counter": null, - "preview_template": null, - "tabindex": 8, - "wizardId": "super_mega_fun_wizard", - "stepId": "step_2", - "_validState": 0 - } + id: "step_2_field_7", + index: 6, + type: "upload", + required: false, + value: "", + label: "

Upload

", + file_types: ".jpg,.jpeg,.png", + format: null, + limit: null, + property: null, + content: null, + validations: {}, + max_length: null, + char_counter: null, + preview_template: null, + tabindex: 8, + wizardId: "super_mega_fun_wizard", + stepId: "step_2", + _validState: 0, + }, ], - "_validState": 0, - "wizardId": "super_mega_fun_wizard" + _validState: 0, + wizardId: "super_mega_fun_wizard", }, { - "id": "step_3", - "index": 2, - "previous": "step_2", - "description": "

Unfortunately not the edible type \":sushi:\"

", - "title": "Combo-boxes", - "permitted": true, - "permitted_message": null, - "final": true, - "fields": [ + id: "step_3", + index: 2, + previous: "step_2", + description: + '

Unfortunately not the edible type :sushi:

', + title: "Combo-boxes", + permitted: true, + permitted_message: null, + final: true, + fields: [ { - "id": "step_3_field_1", - "index": 0, - "type": "dropdown", - "required": false, - "value": "choice1", - "label": "

Custom Dropdown

", - "file_types": null, - "format": null, - "limit": null, - "property": null, - "content": [ + id: "step_3_field_1", + index: 0, + type: "dropdown", + required: false, + value: "choice1", + label: "

Custom Dropdown

", + file_types: null, + format: null, + limit: null, + property: null, + content: [ { - "id": "one", - "name": "One" + id: "one", + name: "One", }, { - "id": "two", - "name": "Two" - } + id: "two", + name: "Two", + }, ], - "validations": {}, - "max_length": null, - "char_counter": null, - "preview_template": null, - "tabindex": 1, - "wizardId": "super_mega_fun_wizard", - "stepId": "step_3", - "_validState": 0 + validations: {}, + max_length: null, + char_counter: null, + preview_template: null, + tabindex: 1, + wizardId: "super_mega_fun_wizard", + stepId: "step_3", + _validState: 0, }, { - "id": "step_3_field_2", - "index": 0, - "type": "tag", - "required": false, - "value": null, - "label": "

Tag

", - "file_types": null, - "format": null, - "limit": null, - "property": null, - "content": null, - "validations": {}, - "max_length": null, - "char_counter": null, - "preview_template": null, - "tabindex": 2, - "wizardId": "super_mega_fun_wizard", - "stepId": "step_3", - "_validState": 0 + id: "step_3_field_2", + index: 0, + type: "tag", + required: false, + value: null, + label: "

Tag

", + file_types: null, + format: null, + limit: null, + property: null, + content: null, + validations: {}, + max_length: null, + char_counter: null, + preview_template: null, + tabindex: 2, + wizardId: "super_mega_fun_wizard", + stepId: "step_3", + _validState: 0, }, { - "id": "step_3_field_3", - "index": 2, - "type": "category", - "required": false, - "value": null, - "label": "

Category

", - "file_types": null, - "format": null, - "limit": 1, - "property": "id", - "content": null, - "validations": {}, - "max_length": null, - "char_counter": null, - "preview_template": null, - "tabindex": 3, - "wizardId": "super_mega_fun_wizard", - "stepId": "step_3", - "_validState": 0 + id: "step_3_field_3", + index: 2, + type: "category", + required: false, + value: null, + label: "

Category

", + file_types: null, + format: null, + limit: 1, + property: "id", + content: null, + validations: {}, + max_length: null, + char_counter: null, + preview_template: null, + tabindex: 3, + wizardId: "super_mega_fun_wizard", + stepId: "step_3", + _validState: 0, }, { - "id": "step_3_field_4", - "index": 3, - "type": "group", - "required": false, - "value": null, - "label": "

Group

", - "file_types": null, - "format": null, - "limit": null, - "property": null, - "content": null, - "validations": {}, - "max_length": null, - "char_counter": null, - "preview_template": null, - "tabindex": 4, - "wizardId": "super_mega_fun_wizard", - "stepId": "step_3", - "_validState": 0 + id: "step_3_field_4", + index: 3, + type: "group", + required: false, + value: null, + label: "

Group

", + file_types: null, + format: null, + limit: null, + property: null, + content: null, + validations: {}, + max_length: null, + char_counter: null, + preview_template: null, + tabindex: 4, + wizardId: "super_mega_fun_wizard", + stepId: "step_3", + _validState: 0, }, { - "id": "step_3_field_5", - "index": 4, - "type": "user_selector", - "required": false, - "value": null, - "label": "

User Selector

", - "file_types": null, - "format": null, - "limit": null, - "property": null, - "content": null, - "validations": {}, - "max_length": null, - "char_counter": null, - "preview_template": null, - "tabindex": 5, - "wizardId": "super_mega_fun_wizard", - "stepId": "step_3", - "_validState": 0 + id: "step_3_field_5", + index: 4, + type: "user_selector", + required: false, + value: null, + label: "

User Selector

", + file_types: null, + format: null, + limit: null, + property: null, + content: null, + validations: {}, + max_length: null, + char_counter: null, + preview_template: null, + tabindex: 5, + wizardId: "super_mega_fun_wizard", + stepId: "step_3", + _validState: 0, }, { - "id": "step_3_field_6", - "index": 5, - "type": "user_selector", - "required": false, - "value": null, - "label": "

Conditional User Selector

", - "description": "Shown when checkbox in step_2_field_5 is true", - "file_types": null, - "format": null, - "limit": null, - "property": null, - "content": null, - "validations": {}, - "max_length": null, - "char_counter": null, - "preview_template": null, - "tabindex": 6, - "wizardId": "super_mega_fun_wizard", - "stepId": "step_3", - "_validState": 0 - } + id: "step_3_field_6", + index: 5, + type: "user_selector", + required: false, + value: null, + label: "

Conditional User Selector

", + description: "Shown when checkbox in step_2_field_5 is true", + file_types: null, + format: null, + limit: null, + property: null, + content: null, + validations: {}, + max_length: null, + char_counter: null, + preview_template: null, + tabindex: 6, + wizardId: "super_mega_fun_wizard", + stepId: "step_3", + _validState: 0, + }, ], - "_validState": 0, - "wizardId": "super_mega_fun_wizard" - } + _validState: 0, + wizardId: "super_mega_fun_wizard", + }, ], - "groups": [] -} + groups: [], +}; diff --git a/assets/javascripts/wizard/tests/helpers/acceptance.js.es6 b/assets/javascripts/wizard/tests/helpers/acceptance.js.es6 index f5c1175f..913e8c7a 100644 --- a/assets/javascripts/wizard/tests/helpers/acceptance.js.es6 +++ b/assets/javascripts/wizard/tests/helpers/acceptance.js.es6 @@ -6,18 +6,20 @@ let server; let app; function acceptance(name, requests, cb) { - module(`Acceptance: ${name}`, function(hooks) { - hooks.beforeEach(function() { - server = setupPretender(function(pretender) { - requests.forEach(req => { - pretender[req.verb](req.path, () => (response(req.status, req.response))); + module(`Acceptance: ${name}`, function (hooks) { + hooks.beforeEach(function () { + server = setupPretender(function (pretender) { + requests.forEach((req) => { + pretender[req.verb](req.path, () => + response(req.status, req.response) + ); }); return pretender; }); app = startApp(); }); - hooks.afterEach(function() { + hooks.afterEach(function () { app.destroy(); server.shutdown(); }); @@ -28,9 +30,7 @@ function acceptance(name, requests, cb) { export default acceptance; -export { - server -}; +export { server }; // The discourse/test/helpers/qunit-helpers file has many functions and imports // we don't need, so there will be some duplciation here. diff --git a/assets/javascripts/wizard/tests/helpers/start-app.js.es6 b/assets/javascripts/wizard/tests/helpers/start-app.js.es6 index 6afe6eb9..821d8033 100644 --- a/assets/javascripts/wizard/tests/helpers/start-app.js.es6 +++ b/assets/javascripts/wizard/tests/helpers/start-app.js.es6 @@ -1,6 +1,12 @@ -const CustomWizard = requirejs("discourse/plugins/discourse-custom-wizard/wizard/application").default; -const initializer = requirejs("discourse/plugins/discourse-custom-wizard/wizard/lib/initialize/wizard").default; -const siteSettings = requirejs("discourse/plugins/discourse-custom-wizard/wizard/tests/fixtures/site-settings").default; +const CustomWizard = requirejs( + "discourse/plugins/discourse-custom-wizard/wizard/application" +).default; +const initializer = requirejs( + "discourse/plugins/discourse-custom-wizard/wizard/lib/initialize/wizard" +).default; +const siteSettings = requirejs( + "discourse/plugins/discourse-custom-wizard/wizard/tests/fixtures/site-settings" +).default; const { cloneJSON } = requirejs("discourse-common/lib/object").default; let app; diff --git a/assets/javascripts/wizard/tests/helpers/step.js.es6 b/assets/javascripts/wizard/tests/helpers/step.js.es6 index a24e04e1..41fe16cd 100644 --- a/assets/javascripts/wizard/tests/helpers/step.js.es6 +++ b/assets/javascripts/wizard/tests/helpers/step.js.es6 @@ -5,16 +5,13 @@ import wizardJson from "../fixtures/wizard"; const update = cloneJSON(updateJson); update.wizard = cloneJSON(wizardJson); -const saveStep = function(response) { +const saveStep = function (response) { return { verb: "put", - path: '/w/wizard/steps/:step_id', + path: "/w/wizard/steps/:step_id", status: 200, - response - } -} + response, + }; +}; -export { - saveStep, - update -} +export { saveStep, update }; diff --git a/assets/javascripts/wizard/tests/helpers/test.js.es6 b/assets/javascripts/wizard/tests/helpers/test.js.es6 index c7401fd5..360c883a 100644 --- a/assets/javascripts/wizard/tests/helpers/test.js.es6 +++ b/assets/javascripts/wizard/tests/helpers/test.js.es6 @@ -2,6 +2,4 @@ function exists(selector) { return document.querySelector(selector) !== null; } -export { - exists -} +export { exists }; diff --git a/assets/javascripts/wizard/tests/helpers/wizard.js.es6 b/assets/javascripts/wizard/tests/helpers/wizard.js.es6 index 997f6c36..4cd2e003 100644 --- a/assets/javascripts/wizard/tests/helpers/wizard.js.es6 +++ b/assets/javascripts/wizard/tests/helpers/wizard.js.es6 @@ -26,20 +26,20 @@ const allFieldsWizard = cloneJSON(wizard); allFieldsWizard.steps[0].fields = [ ...allFieldsWizard.steps[0].fields, ...allFieldsWizard.steps[1].fields, - ...allFieldsWizard.steps[2].fields + ...allFieldsWizard.steps[2].fields, ]; allFieldsWizard.steps = [cloneJSON(allFieldsWizard.steps[0])]; -allFieldsWizard.categories = cloneJSON(categoriesJson['categories']); -allFieldsWizard.groups = cloneJSON(groupsJson['groups']); +allFieldsWizard.categories = cloneJSON(categoriesJson["categories"]); +allFieldsWizard.groups = cloneJSON(groupsJson["groups"]); -const getWizard = function(response) { +const getWizard = function (response) { return { verb: "get", path: "/w/wizard", status: 200, - response - } -} + response, + }; +}; export { getWizard, @@ -48,5 +48,5 @@ export { wizardCompleted, stepNotPermitted, allFieldsWizard, - wizard -} + wizard, +}; diff --git a/assets/javascripts/wizard/tests/pretender.js.es6 b/assets/javascripts/wizard/tests/pretender.js.es6 index 88eae666..1f1d4a7d 100644 --- a/assets/javascripts/wizard/tests/pretender.js.es6 +++ b/assets/javascripts/wizard/tests/pretender.js.es6 @@ -1,23 +1,5 @@ import Pretender from "pretender"; -function parsePostData(query) { - const result = {}; - query.split("&").forEach(function (part) { - const item = part.split("="); - const firstSeg = decodeURIComponent(item[0]); - const m = /^([^\[]+)\[([^\]]+)\]/.exec(firstSeg); - - const val = decodeURIComponent(item[1]).replace(/\+/g, " "); - if (m) { - result[m[1]] = result[m[1]] || {}; - result[m[1]][m[2]] = val; - } else { - result[firstSeg] = val; - } - }); - return result; -} - function response(code, obj) { if (typeof code === "object") { obj = code; @@ -42,7 +24,7 @@ export default function (cb) { return body; }; - server.unhandledRequest = function (verb, path, request) { + server.unhandledRequest = function (verb, path) { const error = "Unhandled request in test environment: " + path + " (" + verb + ")"; window.console.error(error); From e7755b106f3edf805f817fec901296f320de034b Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Wed, 16 Mar 2022 14:09:23 +0100 Subject: [PATCH 108/160] Update workflow to add frontend tests && handle deprecations --- .github/workflows/plugin-tests.yml | 16 +------- .../components/custom-user-selector.js.es6 | 3 +- .../components/wizard-field-category.js.es6 | 3 +- .../components/wizard-field-composer.js.es6 | 3 +- .../wizard/components/wizard-step.js.es6 | 6 +-- .../components/wizard-text-field.js.es6 | 3 +- .../lib/initialize/inject-objects.js.es6 | 38 +++++++++---------- .../lib/initialize/patch-components.js.es6 | 16 ++++++++ .../wizard/lib/initialize/wizard.js.es6 | 1 + .../javascripts/wizard/lib/load-script.js.es6 | 16 ++++---- .../javascripts/wizard/lib/text-lite.js.es6 | 16 +++++--- .../javascripts/wizard/lib/user-search.js.es6 | 3 +- assets/javascripts/wizard/models/step.js.es6 | 3 +- .../javascripts/wizard/models/wizard.js.es6 | 3 +- .../components/wizard-composer-editor.hbs | 2 +- assets/javascripts/wizard/templates/step.hbs | 5 +-- 16 files changed, 76 insertions(+), 61 deletions(-) diff --git a/.github/workflows/plugin-tests.yml b/.github/workflows/plugin-tests.yml index 782ebc4f..baf5e381 100644 --- a/.github/workflows/plugin-tests.yml +++ b/.github/workflows/plugin-tests.yml @@ -73,18 +73,6 @@ jobs: ref: "${{ github.base_ref }}" fetch-depth: 1 - - name: Check spec existence - id: check_spec - uses: andstor/file-existence-action@v1 - with: - files: "plugins/${{ steps.repo-name.outputs.value }}/spec" - - - name: Check qunit existence - id: check_qunit - uses: andstor/file-existence-action@v1 - with: - files: "plugins/${{ steps.repo-name.outputs.value }}/test/javascripts" - - name: Setup Git run: | git config --global user.email "ci@ci.invalid" @@ -140,7 +128,7 @@ jobs: bin/rake db:migrate - name: Plugin RSpec with Coverage - if: matrix.build_type == 'backend' && steps.check_spec.outputs.files_exists == 'true' + if: matrix.build_type == 'backend' run: | if [ -e plugins/${{ steps.repo-name.outputs.value }}/.simplecov ] then @@ -150,6 +138,6 @@ jobs: bin/rake plugin:spec[${{ steps.repo-name.outputs.value }}] - name: Plugin QUnit - if: matrix.build_type == 'frontend' && steps.check_qunit.outputs.files_exists == 'true' + if: matrix.build_type == 'frontend' run: bundle exec rake plugin:qunit['${{ steps.repo-name.outputs.value }}','1200000'] timeout-minutes: 30 diff --git a/assets/javascripts/wizard/components/custom-user-selector.js.es6 b/assets/javascripts/wizard/components/custom-user-selector.js.es6 index c53c3bf4..56eb8f57 100644 --- a/assets/javascripts/wizard/components/custom-user-selector.js.es6 +++ b/assets/javascripts/wizard/components/custom-user-selector.js.es6 @@ -7,6 +7,7 @@ import userSearch from "../lib/user-search"; import WizardI18n from "../lib/wizard-i18n"; import Handlebars from "handlebars"; import { isEmpty } from "@ember/utils"; +import TextField from "@ember/component/text-field"; const template = function (params) { const options = params.options; @@ -31,7 +32,7 @@ const template = function (params) { return new Handlebars.SafeString(html).string; }; -export default Ember.TextField.extend({ +export default TextField.extend({ attributeBindings: ["autofocus", "maxLength"], autocorrect: false, autocapitalize: false, diff --git a/assets/javascripts/wizard/components/wizard-field-category.js.es6 b/assets/javascripts/wizard/components/wizard-field-category.js.es6 index 9f4b65ba..441f83d3 100644 --- a/assets/javascripts/wizard/components/wizard-field-category.js.es6 +++ b/assets/javascripts/wizard/components/wizard-field-category.js.es6 @@ -1,7 +1,8 @@ import { observes } from "discourse-common/utils/decorators"; import Category from "discourse/models/category"; +import Component from "@ember/component"; -export default Ember.Component.extend({ +export default Component.extend({ layoutName: "wizard/templates/components/wizard-field-category", didInsertElement() { diff --git a/assets/javascripts/wizard/components/wizard-field-composer.js.es6 b/assets/javascripts/wizard/components/wizard-field-composer.js.es6 index 07829e8a..255982ea 100644 --- a/assets/javascripts/wizard/components/wizard-field-composer.js.es6 +++ b/assets/javascripts/wizard/components/wizard-field-composer.js.es6 @@ -3,8 +3,9 @@ import { observes, } from "discourse-common/utils/decorators"; import EmberObject from "@ember/object"; +import Component from "@ember/component"; -export default Ember.Component.extend({ +export default Component.extend({ layoutName: "wizard/templates/components/wizard-field-composer", showPreview: false, diff --git a/assets/javascripts/wizard/components/wizard-step.js.es6 b/assets/javascripts/wizard/components/wizard-step.js.es6 index 18b1a255..ea1a63c6 100644 --- a/assets/javascripts/wizard/components/wizard-step.js.es6 +++ b/assets/javascripts/wizard/components/wizard-step.js.es6 @@ -87,7 +87,7 @@ export default Component.extend({ @observes("step.message") _handleMessage: function () { const message = this.get("step.message"); - this.sendAction("showMessage", message); + this.showMessage(message); }, keyPress(event) { @@ -162,7 +162,7 @@ export default Component.extend({ if (response["final"]) { CustomWizard.finished(response); } else { - this.sendAction("goNext", response); + this.goNext(response); } }) .catch(() => this.animateInvalidFields()) @@ -181,7 +181,7 @@ export default Component.extend({ }, showMessage(message) { - this.sendAction("showMessage", message); + this.sendAction(message); }, stylingDropdownChanged(id, value) { diff --git a/assets/javascripts/wizard/components/wizard-text-field.js.es6 b/assets/javascripts/wizard/components/wizard-text-field.js.es6 index 3d87be09..5991eefc 100644 --- a/assets/javascripts/wizard/components/wizard-text-field.js.es6 +++ b/assets/javascripts/wizard/components/wizard-text-field.js.es6 @@ -1,8 +1,9 @@ import computed from "discourse-common/utils/decorators"; import { isLTR, isRTL, siteDir } from "discourse/lib/text-direction"; import WizardI18n from "../lib/wizard-i18n"; +import TextField from "@ember/component/text-field"; -export default Ember.TextField.extend({ +export default TextField.extend({ attributeBindings: [ "autocorrect", "autocapitalize", diff --git a/assets/javascripts/wizard/lib/initialize/inject-objects.js.es6 b/assets/javascripts/wizard/lib/initialize/inject-objects.js.es6 index fb11c9e7..51dff7e1 100644 --- a/assets/javascripts/wizard/lib/initialize/inject-objects.js.es6 +++ b/assets/javascripts/wizard/lib/initialize/inject-objects.js.es6 @@ -1,5 +1,11 @@ export default { run(app) { + // siteSettings must always be registered first + if (!app.hasRegistration("site-settings:main")) { + const siteSettings = app.SiteSettings; + app.register("site-settings:main", siteSettings, { instantiate: false }); + } + const Store = requirejs("discourse/services/store").default; const Site = requirejs( "discourse/plugins/discourse-custom-wizard/wizard/models/site" @@ -10,11 +16,10 @@ export default { const sniffCapabilites = requirejs( "discourse/pre-initializers/sniff-capabilities" ).default; + const site = Site.current(); const session = Session.current(); - const registrations = [ - ["site-settings:main", app.SiteSettings, false], ["message-bus:main", messageBus, false], ["site:main", site, false], ["session:main", session, false], @@ -30,25 +35,18 @@ export default { } }); - const targets = [ - "controller", - "component", - "route", - "model", - "adapter", - "mixin", - ]; - const injections = [ - ["siteSettings", "site-settings:main"], - ["messageBus", "message-bus:main"], - ["site", "site:main"], - ["session", "session:main"], - ["store", "service:store"], - ["appEvents", "service:app-events"], - ]; + const targets = ["controller", "component", "route", "model", "adapter"]; - injections.forEach((injection) => { - targets.forEach((t) => app.inject(t, injection[0], injection[1])); + targets.forEach((t) => { + app.inject(t, "appEvents", "service:app-events"); + app.inject(t, "store", "service:store"); + app.inject(t, "site", "site:main"); + }); + + targets.concat("service").forEach((t) => { + app.inject(t, "session", "session:main"); + app.inject(t, "messageBus", "message-bus:main"); + app.inject(t, "siteSettings", "site-settings:main"); }); if (!app.hasRegistration("capabilities:main")) { diff --git a/assets/javascripts/wizard/lib/initialize/patch-components.js.es6 b/assets/javascripts/wizard/lib/initialize/patch-components.js.es6 index 75cc63f1..d5e7ea25 100644 --- a/assets/javascripts/wizard/lib/initialize/patch-components.js.es6 +++ b/assets/javascripts/wizard/lib/initialize/patch-components.js.es6 @@ -22,6 +22,11 @@ export default { const DEditor = requirejs("discourse/components/d-editor").default; const { clipboardHelpers } = requirejs("discourse/lib/utilities"); const toMarkdown = requirejs("discourse/lib/to-markdown").default; + const discourseComputed = requirejs("discourse-common/utils/decorators") + .default; + const WizardI18n = requirejs( + "discourse/plugins/discourse-custom-wizard/wizard/lib/wizard-i18n" + ).default; const isInside = (text, regex) => { const matches = text.match(regex); return matches && matches.length % 2; @@ -46,6 +51,17 @@ export default { } }, + @discourseComputed("placeholder", "placeholderOverride") + placeholderTranslated(placeholder, placeholderOverride) { + if (placeholderOverride) { + return placeholderOverride; + } + if (placeholder) { + return WizardI18n(placeholder); + } + return null; + }, + _wizardInsertText(args = {}) { if (args.fieldId === this.fieldId) { this._insertText(args.text, args.options); diff --git a/assets/javascripts/wizard/lib/initialize/wizard.js.es6 b/assets/javascripts/wizard/lib/initialize/wizard.js.es6 index 0020f7a8..915e5f26 100644 --- a/assets/javascripts/wizard/lib/initialize/wizard.js.es6 +++ b/assets/javascripts/wizard/lib/initialize/wizard.js.es6 @@ -40,6 +40,7 @@ export default { const Session = requirejs("discourse/models/session").default; const session = Session.current(); session.set("highlightJsPath", setupData.highlightJsPath); + session.set("markdownItUrl", setupData.markdownItUrl); [ "register-files", diff --git a/assets/javascripts/wizard/lib/load-script.js.es6 b/assets/javascripts/wizard/lib/load-script.js.es6 index 43d97d0a..d0b07c26 100644 --- a/assets/javascripts/wizard/lib/load-script.js.es6 +++ b/assets/javascripts/wizard/lib/load-script.js.es6 @@ -1,5 +1,7 @@ import { ajax } from "wizard/lib/ajax"; -import getURL from "discourse-common/lib/get-url"; +import getURL, { getURLWithCDN } from "discourse-common/lib/get-url"; +import { run } from "@ember/runloop"; +import { Promise } from "rsvp"; const _loaded = {}; const _loading = {}; @@ -25,7 +27,7 @@ function loadWithTag(path, cb) { ) { s = s.onload = s.onreadystatechange = null; if (!abort) { - Ember.run(null, cb); + run(null, cb); } } }; @@ -38,7 +40,7 @@ export function loadCSS(url) { export default function loadScript(url, opts) { // TODO: Remove this once plugins have been updated not to use it: if (url === "defer/html-sanitizer-bundle") { - return Ember.RSVP.Promise.resolve(); + return Promise.resolve(); } opts = opts || {}; @@ -51,7 +53,7 @@ export default function loadScript(url, opts) { } }); - return new Ember.RSVP.Promise(function (resolve) { + return new Promise(function (resolve) { url = getURL(url); // If we already loaded this url @@ -63,7 +65,7 @@ export default function loadScript(url, opts) { } let done; - _loading[url] = new Ember.RSVP.Promise(function (_done) { + _loading[url] = new Promise(function (_done) { done = _done; }); @@ -84,8 +86,8 @@ export default function loadScript(url, opts) { // Scripts should always load from CDN // CSS is type text, to accept it from a CDN we would need to handle CORS - if (!opts.css && Discourse.CDN && url[0] === "/" && url[1] !== "/") { - cdnUrl = Discourse.CDN.replace(/\/$/, "") + url; + if (!opts.css) { + cdnUrl = getURLWithCDN(url); } // Some javascript depends on the path of where it is loaded (ace editor) diff --git a/assets/javascripts/wizard/lib/text-lite.js.es6 b/assets/javascripts/wizard/lib/text-lite.js.es6 index cc161426..26cfc27a 100644 --- a/assets/javascripts/wizard/lib/text-lite.js.es6 +++ b/assets/javascripts/wizard/lib/text-lite.js.es6 @@ -3,6 +3,8 @@ import { default as PrettyText, buildOptions } from "pretty-text/pretty-text"; import Handlebars from "handlebars"; import getURL from "discourse-common/lib/get-url"; import { getOwner } from "discourse-common/lib/get-owner"; +import { Promise } from "rsvp"; +import Session from "discourse/models/session"; export function cook(text, options) { if (!options) { @@ -18,11 +20,15 @@ export function cook(text, options) { // everything should eventually move to async API and this should be renamed // cook export function cookAsync(text, options) { - if (Discourse.MarkdownItURL) { - return loadScript(Discourse.MarkdownItURL) - .then(() => cook(text, options)) - .catch((e) => Ember.Logger.error(e)); + let markdownItURL = Session.currentProp("markdownItURL"); + if (markdownItURL) { + return ( + loadScript(markdownItURL) + .then(() => cook(text, options)) + // eslint-disable-next-line no-console + .catch((e) => console.error(e)) + ); } else { - return Ember.RSVP.Promise.resolve(cook(text)); + return Promise.resolve(cook(text)); } } diff --git a/assets/javascripts/wizard/lib/user-search.js.es6 b/assets/javascripts/wizard/lib/user-search.js.es6 index e7171f18..04b6f97c 100644 --- a/assets/javascripts/wizard/lib/user-search.js.es6 +++ b/assets/javascripts/wizard/lib/user-search.js.es6 @@ -1,6 +1,7 @@ import { CANCELLED_STATUS } from "discourse/lib/autocomplete"; import { debounce } from "@ember/runloop"; import getUrl from "discourse-common/lib/get-url"; +import { Promise } from "rsvp"; let cache = {}, cacheTopicId, @@ -120,7 +121,7 @@ export default function userSearch(options) { currentTerm = term; - return new Ember.RSVP.Promise(function (resolve) { + return new Promise(function (resolve) { // TODO site setting for allowed regex in username if (term.match(/[^\w_\-\.@\+]/)) { resolve([]); diff --git a/assets/javascripts/wizard/models/step.js.es6 b/assets/javascripts/wizard/models/step.js.es6 index 2729e993..36503276 100644 --- a/assets/javascripts/wizard/models/step.js.es6 +++ b/assets/javascripts/wizard/models/step.js.es6 @@ -3,6 +3,7 @@ import ValidState from "wizard/mixins/valid-state"; import { ajax } from "wizard/lib/ajax"; import discourseComputed from "discourse-common/utils/decorators"; import { translatedText } from "discourse/plugins/discourse-custom-wizard/wizard/lib/wizard-i18n"; +import { later } from "@ember/runloop"; export default EmberObject.extend(ValidState, { id: null, @@ -105,6 +106,6 @@ export default EmberObject.extend(ValidState, { state: "error", text: message, }); - Ember.run.later(() => this.set("message", null), 6000); + later(() => this.set("message", null), 6000); }, }); diff --git a/assets/javascripts/wizard/models/wizard.js.es6 b/assets/javascripts/wizard/models/wizard.js.es6 index b2e26b25..15bcbb8c 100644 --- a/assets/javascripts/wizard/models/wizard.js.es6 +++ b/assets/javascripts/wizard/models/wizard.js.es6 @@ -113,8 +113,7 @@ CustomWizard.reopenClass({ } }); - Site.currentProp("categoriesList", categories); - Site.currentProp("sortedCategories", categories); + Site.currentProp("categories", categories); Site.currentProp("listByActivity", categories); Site.currentProp("categoriesById", categoriesById); Site.currentProp( diff --git a/assets/javascripts/wizard/templates/components/wizard-composer-editor.hbs b/assets/javascripts/wizard/templates/components/wizard-composer-editor.hbs index be98db8e..7d453a0b 100644 --- a/assets/javascripts/wizard/templates/components/wizard-composer-editor.hbs +++ b/assets/javascripts/wizard/templates/components/wizard-composer-editor.hbs @@ -1,7 +1,7 @@ {{d-editor tabindex=field.tabindex value=composer.reply - placeholderTranslated=replyPlaceholder + placeholderOverride=replyPlaceholder previewUpdated=(action "previewUpdated") markdownOptions=markdownOptions extraButtons=(action "extraButtons") diff --git a/assets/javascripts/wizard/templates/step.hbs b/assets/javascripts/wizard/templates/step.hbs index 6456a59c..5ed14cdf 100644 --- a/assets/javascripts/wizard/templates/step.hbs +++ b/assets/javascripts/wizard/templates/step.hbs @@ -13,8 +13,7 @@ {{#if step.permitted}} {{wizard-step step=step wizard=wizard - goNext="goNext" + goNext=(action "goNext") goBack=(action "goBack") - finished="finished" - showMessage="showMessage"}} + showMessage=(action "showMessage")}} {{/if}} From ea52b2779bbe9c382f719e5f69b0dfffff94b0f0 Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Wed, 16 Mar 2022 14:10:54 +0100 Subject: [PATCH 109/160] Remove duplicated functions --- assets/javascripts/wizard/components/validator.js.es6 | 1 - assets/javascripts/wizard/components/wizard-step.js.es6 | 2 -- 2 files changed, 3 deletions(-) diff --git a/assets/javascripts/wizard/components/validator.js.es6 b/assets/javascripts/wizard/components/validator.js.es6 index dafbf8c0..aa68660c 100644 --- a/assets/javascripts/wizard/components/validator.js.es6 +++ b/assets/javascripts/wizard/components/validator.js.es6 @@ -11,7 +11,6 @@ export default Component.extend({ invalidMessageKey: null, isValid: null, isInvalid: equal("isValid", false), - layoutName: "wizard/templates/components/validator", init() { this._super(...arguments); diff --git a/assets/javascripts/wizard/components/wizard-step.js.es6 b/assets/javascripts/wizard/components/wizard-step.js.es6 index ea1a63c6..cc23c5bf 100644 --- a/assets/javascripts/wizard/components/wizard-step.js.es6 +++ b/assets/javascripts/wizard/components/wizard-step.js.es6 @@ -169,8 +169,6 @@ export default Component.extend({ .finally(() => this.set("saving", false)); }, - keyPress() {}, - actions: { quit() { this.get("wizard").skip(); From 61928ce4360a3837002e07417935e06f376cf562 Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Wed, 16 Mar 2022 14:12:13 +0100 Subject: [PATCH 110/160] Apply rubocop --- app/serializers/custom_wizard/submission_serializer.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/serializers/custom_wizard/submission_serializer.rb b/app/serializers/custom_wizard/submission_serializer.rb index 9c2f8133..732d6743 100644 --- a/app/serializers/custom_wizard/submission_serializer.rb +++ b/app/serializers/custom_wizard/submission_serializer.rb @@ -7,12 +7,12 @@ class CustomWizard::SubmissionSerializer < ApplicationSerializer has_one :user, serializer: ::BasicUserSerializer, embed: :objects def include_user? - object.user.present? + object.user.present? end def fields @fields ||= begin - result = {} + result = {} object.wizard.template['steps'].each do |step| step['fields'].each do |field| From 6cd6b111bd3d5bec16dbccc5d745560e3636e356 Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Wed, 16 Mar 2022 14:13:45 +0100 Subject: [PATCH 111/160] Bump minor version --- plugin.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin.rb b/plugin.rb index e8dcf457..e745c3a8 100644 --- a/plugin.rb +++ b/plugin.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true # name: discourse-custom-wizard # about: Create custom wizards for topic creation, onboarding, user surveys and much more. -# version: 1.18.4 +# version: 1.19.0 # authors: Angus McLeod, Faizaan Gagan, Robert Barrow, Keegan George # contact_emails: support@thepavilion.io # url: https://github.com/paviliondev/discourse-custom-wizard From 7e295dfb1419d51333241b0e11cb073b016e3db2 Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Wed, 16 Mar 2022 14:22:18 +0100 Subject: [PATCH 112/160] Update plugin-tests.yml --- .github/workflows/plugin-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/plugin-tests.yml b/.github/workflows/plugin-tests.yml index baf5e381..13f76074 100644 --- a/.github/workflows/plugin-tests.yml +++ b/.github/workflows/plugin-tests.yml @@ -139,5 +139,5 @@ jobs: - name: Plugin QUnit if: matrix.build_type == 'frontend' - run: bundle exec rake plugin:qunit['${{ steps.repo-name.outputs.value }}','1200000'] + run: LOAD_PLUGINS=1 QUNIT_EMBER_CLI=0 bin/rake "qunit:test['600000','/w/qunit']" timeout-minutes: 30 From 01d972f6baa4b6b24a2558a0102025e114b9990a Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Wed, 16 Mar 2022 14:39:39 +0100 Subject: [PATCH 113/160] Try using bundle exec --- .github/workflows/plugin-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/plugin-tests.yml b/.github/workflows/plugin-tests.yml index 13f76074..34af2f04 100644 --- a/.github/workflows/plugin-tests.yml +++ b/.github/workflows/plugin-tests.yml @@ -139,5 +139,5 @@ jobs: - name: Plugin QUnit if: matrix.build_type == 'frontend' - run: LOAD_PLUGINS=1 QUNIT_EMBER_CLI=0 bin/rake "qunit:test['600000','/w/qunit']" + run: LOAD_PLUGINS=1 QUNIT_EMBER_CLI=0 bundle exec rake "qunit:test['600000','/w/qunit']" timeout-minutes: 30 From 591274d38fcf09e7c5816bfd184b0a095e59dc97 Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Wed, 16 Mar 2022 15:07:49 +0100 Subject: [PATCH 114/160] Skip core qunit --- .github/workflows/plugin-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/plugin-tests.yml b/.github/workflows/plugin-tests.yml index 34af2f04..f32cdf5c 100644 --- a/.github/workflows/plugin-tests.yml +++ b/.github/workflows/plugin-tests.yml @@ -139,5 +139,5 @@ jobs: - name: Plugin QUnit if: matrix.build_type == 'frontend' - run: LOAD_PLUGINS=1 QUNIT_EMBER_CLI=0 bundle exec rake "qunit:test['600000','/w/qunit']" + run: QUNIT_SKIP_CORE=1 LOAD_PLUGINS=1 QUNIT_EMBER_CLI=0 bin/rake "qunit:test['600000','/w/qunit']" timeout-minutes: 30 From 5edfb4c41ea5095bff6d965e8275f6b4984ec045 Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Wed, 16 Mar 2022 15:49:25 +0100 Subject: [PATCH 115/160] Remove subs and notices files --- app/controllers/custom_wizard/admin/admin.rb | 11 +- app/controllers/custom_wizard/admin/notice.rb | 68 ---- .../custom_wizard/admin/subscription.rb | 48 --- app/controllers/custom_wizard/wizard.rb | 5 - .../scheduled/custom_wizard/update_notices.rb | 9 - .../custom_wizard/update_subscription.rb | 9 - .../custom_wizard/notice_serializer.rb | 33 -- .../subscription/authentication_serializer.rb | 11 - .../subscription/subscription_serializer.rb | 10 - .../custom_wizard/subscription_serializer.rb | 6 - .../components/subscription-container.js.es6 | 25 -- .../components/wizard-notice-row.js.es6 | 19 - .../discourse/components/wizard-notice.js.es6 | 29 -- .../components/wizard-subscription.js.es6 | 55 --- .../custom-wizard-critical-notice.hbs | 5 - .../custom-wizard-critical-notice.js.es6 | 19 - .../controllers/admin-wizards-notices.js.es6 | 68 ---- .../admin-wizards-subscription.js.es6 | 62 --- .../discourse/helpers/notice-badge.js.es6 | 43 --- .../initializers/custom-wizard-edits.js.es6 | 45 --- .../discourse/mixins/notice-message.js.es6 | 68 ---- .../models/custom-wizard-notice.js.es6 | 74 ---- .../models/custom-wizard-subscription.js.es6 | 33 -- .../routes/admin-wizards-notices.js.es6 | 17 - .../routes/admin-wizards-subscription.js.es6 | 19 - .../templates/admin-wizards-notices.hbs | 49 --- .../templates/admin-wizards-subscription.hbs | 35 -- .../discourse/templates/admin-wizards.hbs | 7 - .../components/wizard-notice-row.hbs | 30 -- .../templates/components/wizard-notice.hbs | 39 -- .../components/wizard-subscription.hbs | 31 -- .../javascripts/wizard/tests/bootstrap.js.es6 | 30 +- config/locales/server.en.yml | 14 - config/routes.rb | 12 - lib/custom_wizard/notice.rb | 358 ------------------ lib/custom_wizard/notice/connection_error.rb | 77 ---- lib/custom_wizard/subscription.rb | 232 ------------ .../subscription/authentication.rb | 95 ----- .../subscription/subscription.rb | 22 -- plugin.rb | 21 - 40 files changed, 17 insertions(+), 1826 deletions(-) delete mode 100644 app/controllers/custom_wizard/admin/notice.rb delete mode 100644 app/controllers/custom_wizard/admin/subscription.rb delete mode 100644 app/jobs/scheduled/custom_wizard/update_notices.rb delete mode 100644 app/jobs/scheduled/custom_wizard/update_subscription.rb delete mode 100644 app/serializers/custom_wizard/notice_serializer.rb delete mode 100644 app/serializers/custom_wizard/subscription/authentication_serializer.rb delete mode 100644 app/serializers/custom_wizard/subscription/subscription_serializer.rb delete mode 100644 app/serializers/custom_wizard/subscription_serializer.rb delete mode 100644 assets/javascripts/discourse/components/subscription-container.js.es6 delete mode 100644 assets/javascripts/discourse/components/wizard-notice-row.js.es6 delete mode 100644 assets/javascripts/discourse/components/wizard-notice.js.es6 delete mode 100644 assets/javascripts/discourse/components/wizard-subscription.js.es6 delete mode 100644 assets/javascripts/discourse/connectors/admin-dashboard-top/custom-wizard-critical-notice.hbs delete mode 100644 assets/javascripts/discourse/connectors/admin-dashboard-top/custom-wizard-critical-notice.js.es6 delete mode 100644 assets/javascripts/discourse/controllers/admin-wizards-notices.js.es6 delete mode 100644 assets/javascripts/discourse/controllers/admin-wizards-subscription.js.es6 delete mode 100644 assets/javascripts/discourse/helpers/notice-badge.js.es6 delete mode 100644 assets/javascripts/discourse/mixins/notice-message.js.es6 delete mode 100644 assets/javascripts/discourse/models/custom-wizard-notice.js.es6 delete mode 100644 assets/javascripts/discourse/models/custom-wizard-subscription.js.es6 delete mode 100644 assets/javascripts/discourse/routes/admin-wizards-notices.js.es6 delete mode 100644 assets/javascripts/discourse/routes/admin-wizards-subscription.js.es6 delete mode 100644 assets/javascripts/discourse/templates/admin-wizards-notices.hbs delete mode 100644 assets/javascripts/discourse/templates/admin-wizards-subscription.hbs delete mode 100644 assets/javascripts/discourse/templates/components/wizard-notice-row.hbs delete mode 100644 assets/javascripts/discourse/templates/components/wizard-notice.hbs delete mode 100644 assets/javascripts/discourse/templates/components/wizard-subscription.hbs delete mode 100644 lib/custom_wizard/notice.rb delete mode 100644 lib/custom_wizard/notice/connection_error.rb delete mode 100644 lib/custom_wizard/subscription/authentication.rb delete mode 100644 lib/custom_wizard/subscription/subscription.rb diff --git a/app/controllers/custom_wizard/admin/admin.rb b/app/controllers/custom_wizard/admin/admin.rb index 63a89833..e011e094 100644 --- a/app/controllers/custom_wizard/admin/admin.rb +++ b/app/controllers/custom_wizard/admin/admin.rb @@ -4,16 +4,7 @@ class CustomWizard::AdminController < ::Admin::AdminController def index render_json_dump( - #TODO replace with appropriate static? - api_section: ["business"].include?(CustomWizard::Subscription.type), - active_notice_count: CustomWizard::Notice.active_count, - featured_notices: ActiveModel::ArraySerializer.new( - CustomWizard::Notice.list( - type: CustomWizard::Notice.types[:info], - archetype: CustomWizard::Notice.archetypes[:subscription_message] - ), - each_serializer: CustomWizard::NoticeSerializer - ) + api_section: ["business"].include?(CustomWizard::Subscription.type) ) end diff --git a/app/controllers/custom_wizard/admin/notice.rb b/app/controllers/custom_wizard/admin/notice.rb deleted file mode 100644 index 81ae00da..00000000 --- a/app/controllers/custom_wizard/admin/notice.rb +++ /dev/null @@ -1,68 +0,0 @@ -# frozen_string_literal: true - -class CustomWizard::AdminNoticeController < CustomWizard::AdminController - before_action :find_notice, only: [:dismiss, :hide] - - def index - type = params[:type] - archetype = params[:archtype] - page = params[:page].to_i - include_all = ActiveRecord::Type::Boolean.new.cast(params[:include_all]) - visible = ActiveRecord::Type::Boolean.new.cast(params[:visible]) - - if type - if type.is_a?(Array) - type = type.map { |t| CustomWizard::Notice.types[t.to_sym] } - else - type = CustomWizard::Notice.types[type.to_sym] - end - end - - if archetype - if archetype.is_a?(Array) - archetype = archetype.map { |t| CustomWizard::Notice.archetypes[archetype.to_sym] } - else - archetype = CustomWizard::Notice.archetypes[archetype.to_sym] - end - end - - notices = CustomWizard::Notice.list( - include_all: include_all, - page: page, - type: type, - archetype: archetype, - visible: visible - ) - - render_serialized(notices, CustomWizard::NoticeSerializer, root: :notices) - end - - def dismiss - if @notice.dismissable? && @notice.dismiss! - render json: success_json.merge(dismissed_at: @notice.dismissed_at) - else - render json: failed_json - end - end - - def hide - if @notice.can_hide? && @notice.hide! - render json: success_json.merge(hidden_at: @notice.hidden_at) - else - render json: failed_json - end - end - - def dismiss_all - if CustomWizard::Notice.dismiss_all - render json: success_json - else - render json: failed_json - end - end - - def find_notice - @notice = CustomWizard::Notice.find(params[:notice_id]) - raise Discourse::InvalidParameters.new(:notice_id) unless @notice - end -end diff --git a/app/controllers/custom_wizard/admin/subscription.rb b/app/controllers/custom_wizard/admin/subscription.rb deleted file mode 100644 index 15ff6396..00000000 --- a/app/controllers/custom_wizard/admin/subscription.rb +++ /dev/null @@ -1,48 +0,0 @@ -# frozen_string_literal: true - -class CustomWizard::AdminSubscriptionController < CustomWizard::AdminController - skip_before_action :check_xhr, :preload_json, :verify_authenticity_token, only: [:authorize, :authorize_callback] - - def index - render_serialized(subscription, CustomWizard::SubscriptionSerializer, root: false) - end - - def authorize - request_id = SecureRandom.hex(32) - cookies[:user_api_request_id] = request_id - redirect_to subscription.authentication_url(current_user.id, request_id).to_s - end - - def authorize_callback - payload = params[:payload] - request_id = cookies[:user_api_request_id] - - subscription.authentication_response(request_id, payload) - subscription.update - - redirect_to '/admin/wizards/subscription' - end - - def destroy_authentication - if subscription.destroy_authentication - render json: success_json - else - render json: failed_json - end - end - - def update_subscription - if subscription.update - serialized_subscription = CustomWizard::Subscription::SubscriptionSerializer.new(subscription.subscription, root: false) - render json: success_json.merge(subscription: serialized_subscription) - else - render json: failed_json - end - end - - protected - - def subscription - @subscription ||= CustomWizard::Subscription.new - end -end diff --git a/app/controllers/custom_wizard/wizard.rb b/app/controllers/custom_wizard/wizard.rb index a7988b0d..bc0a06af 100644 --- a/app/controllers/custom_wizard/wizard.rb +++ b/app/controllers/custom_wizard/wizard.rb @@ -11,7 +11,6 @@ class CustomWizard::WizardController < ::ActionController::Base before_action :preload_wizard_json before_action :ensure_plugin_enabled - before_action :update_subscription, only: [:index] before_action :ensure_logged_in, only: [:skip] helper_method :wizard_page_title @@ -123,8 +122,4 @@ class CustomWizard::WizardController < ::ActionController::Base redirect_to path("/") end end - - def update_subscription - CustomWizard::Subscription.update - end end diff --git a/app/jobs/scheduled/custom_wizard/update_notices.rb b/app/jobs/scheduled/custom_wizard/update_notices.rb deleted file mode 100644 index 25ebdf3b..00000000 --- a/app/jobs/scheduled/custom_wizard/update_notices.rb +++ /dev/null @@ -1,9 +0,0 @@ -# frozen_string_literal: true - -class Jobs::CustomWizardUpdateNotices < ::Jobs::Scheduled - every 5.minutes - - def execute(args = {}) - CustomWizard::Notice.update - end -end diff --git a/app/jobs/scheduled/custom_wizard/update_subscription.rb b/app/jobs/scheduled/custom_wizard/update_subscription.rb deleted file mode 100644 index 72e34435..00000000 --- a/app/jobs/scheduled/custom_wizard/update_subscription.rb +++ /dev/null @@ -1,9 +0,0 @@ -# frozen_string_literal: true - -class Jobs::CustomWizardUpdateSubscription < ::Jobs::Scheduled - every 1.hour - - def execute(args = {}) - CustomWizard::Subscription.update - end -end diff --git a/app/serializers/custom_wizard/notice_serializer.rb b/app/serializers/custom_wizard/notice_serializer.rb deleted file mode 100644 index 4354731d..00000000 --- a/app/serializers/custom_wizard/notice_serializer.rb +++ /dev/null @@ -1,33 +0,0 @@ -# frozen_string_literal: true - -class CustomWizard::NoticeSerializer < ApplicationSerializer - attributes :id, - :title, - :message, - :type, - :archetype, - :created_at, - :expired_at, - :updated_at, - :dismissed_at, - :retrieved_at, - :hidden_at, - :dismissable, - :can_hide - - def dismissable - object.dismissable? - end - - def can_hide - object.can_hide? - end - - def type - CustomWizard::Notice.types.key(object.type) - end - - def messsage - PrettyText.cook(object.message) - end -end diff --git a/app/serializers/custom_wizard/subscription/authentication_serializer.rb b/app/serializers/custom_wizard/subscription/authentication_serializer.rb deleted file mode 100644 index 3d54d0f2..00000000 --- a/app/serializers/custom_wizard/subscription/authentication_serializer.rb +++ /dev/null @@ -1,11 +0,0 @@ -# frozen_string_literal: true -class CustomWizard::Subscription::AuthenticationSerializer < ApplicationSerializer - attributes :active, - :client_id, - :auth_by, - :auth_at - - def active - object.active? - end -end diff --git a/app/serializers/custom_wizard/subscription/subscription_serializer.rb b/app/serializers/custom_wizard/subscription/subscription_serializer.rb deleted file mode 100644 index 95a2b323..00000000 --- a/app/serializers/custom_wizard/subscription/subscription_serializer.rb +++ /dev/null @@ -1,10 +0,0 @@ -# frozen_string_literal: true -class CustomWizard::Subscription::SubscriptionSerializer < ApplicationSerializer - attributes :type, - :active, - :updated_at - - def active - object.active? - end -end diff --git a/app/serializers/custom_wizard/subscription_serializer.rb b/app/serializers/custom_wizard/subscription_serializer.rb deleted file mode 100644 index 067cfbcd..00000000 --- a/app/serializers/custom_wizard/subscription_serializer.rb +++ /dev/null @@ -1,6 +0,0 @@ -# frozen_string_literal: true -class CustomWizard::SubscriptionSerializer < ApplicationSerializer - attributes :server - has_one :authentication, serializer: CustomWizard::Subscription::AuthenticationSerializer, embed: :objects - has_one :subscription, serializer: CustomWizard::Subscription::SubscriptionSerializer, embed: :objects -end diff --git a/assets/javascripts/discourse/components/subscription-container.js.es6 b/assets/javascripts/discourse/components/subscription-container.js.es6 deleted file mode 100644 index a12b8949..00000000 --- a/assets/javascripts/discourse/components/subscription-container.js.es6 +++ /dev/null @@ -1,25 +0,0 @@ -import Component from "@ember/component"; -import discourseComputed from "discourse-common/utils/decorators"; - -export default Component.extend({ - classNameBindings: [":subscription-container", "subscribed"], - - @discourseComputed("subscribed") - subscribedIcon(subscribed) { - return subscribed ? "check" : "dash"; - }, - - @discourseComputed("subscribed") - subscribedLabel(subscribed) { - return `admin.wizard.subscription_container.${ - subscribed ? "subscribed" : "not_subscribed" - }.label`; - }, - - @discourseComputed("subscribed") - subscribedTitle(subscribed) { - return `admin.wizard.subscription_container.${ - subscribed ? "subscribed" : "not_subscribed" - }.title`; - }, -}); diff --git a/assets/javascripts/discourse/components/wizard-notice-row.js.es6 b/assets/javascripts/discourse/components/wizard-notice-row.js.es6 deleted file mode 100644 index ada4384d..00000000 --- a/assets/javascripts/discourse/components/wizard-notice-row.js.es6 +++ /dev/null @@ -1,19 +0,0 @@ -import Component from "@ember/component"; -import NoticeMessage from "../mixins/notice-message"; - -export default Component.extend(NoticeMessage, { - tagName: "tr", - attributeBindings: ["notice.id:data-notice-id"], - classNameBindings: [ - ":wizard-notice-row", - "notice.typeClass", - "notice.expired:expired", - "notice.dismissed:dismissed", - ], - - actions: { - dismiss() { - this.notice.dismiss(); - }, - }, -}); diff --git a/assets/javascripts/discourse/components/wizard-notice.js.es6 b/assets/javascripts/discourse/components/wizard-notice.js.es6 deleted file mode 100644 index ca6b7658..00000000 --- a/assets/javascripts/discourse/components/wizard-notice.js.es6 +++ /dev/null @@ -1,29 +0,0 @@ -import Component from "@ember/component"; -import NoticeMessage from "../mixins/notice-message"; - -export default Component.extend(NoticeMessage, { - attributeBindings: ["notice.id:data-notice-id"], - classNameBindings: [ - ":wizard-notice", - "notice.typeClass", - "notice.dismissed:dismissed", - "notice.expired:expired", - "notice.hidden:hidden", - ], - - actions: { - dismiss() { - this.set("dismissing", true); - this.notice.dismiss().then(() => { - this.set("dismissing", false); - }); - }, - - hide() { - this.set("hiding", true); - this.notice.hide().then(() => { - this.set("hiding", false); - }); - }, - }, -}); diff --git a/assets/javascripts/discourse/components/wizard-subscription.js.es6 b/assets/javascripts/discourse/components/wizard-subscription.js.es6 deleted file mode 100644 index 29e47bc2..00000000 --- a/assets/javascripts/discourse/components/wizard-subscription.js.es6 +++ /dev/null @@ -1,55 +0,0 @@ -import Component from "@ember/component"; -import CustomWizardSubscription from "../models/custom-wizard-subscription"; -import { notEmpty } from "@ember/object/computed"; -import discourseComputed from "discourse-common/utils/decorators"; -import I18n from "I18n"; - -export default Component.extend({ - classNameBindings: [ - ":custom-wizard-subscription", - "subscription.active:active:inactive", - ], - subscribed: notEmpty("subscription"), - - @discourseComputed("subscription.type") - title(type) { - return type - ? I18n.t(`admin.wizard.subscription.subscription.title.${type}`) - : I18n.t("admin.wizard.subscription.not_subscribed"); - }, - - @discourseComputed("subscription.active") - stateClass(active) { - return active ? "active" : "inactive"; - }, - - @discourseComputed("stateClass") - stateLabel(stateClass) { - return I18n.t( - `admin.wizard.subscription.subscription.status.${stateClass}` - ); - }, - - actions: { - update() { - this.set("updating", true); - CustomWizardSubscription.update() - .then((result) => { - if (result.success) { - this.setProperties({ - updateIcon: "check", - subscription: result.subscription, - }); - } else { - this.set("updateIcon", "times"); - } - }) - .finally(() => { - this.set("updating", false); - setTimeout(() => { - this.set("updateIcon", null); - }, 7000); - }); - }, - }, -}); diff --git a/assets/javascripts/discourse/connectors/admin-dashboard-top/custom-wizard-critical-notice.hbs b/assets/javascripts/discourse/connectors/admin-dashboard-top/custom-wizard-critical-notice.hbs deleted file mode 100644 index 9d96bed9..00000000 --- a/assets/javascripts/discourse/connectors/admin-dashboard-top/custom-wizard-critical-notice.hbs +++ /dev/null @@ -1,5 +0,0 @@ -{{#if notices}} - {{#each notices as |notice|}} - {{wizard-notice notice=notice showPlugin=true}} - {{/each}} -{{/if}} diff --git a/assets/javascripts/discourse/connectors/admin-dashboard-top/custom-wizard-critical-notice.js.es6 b/assets/javascripts/discourse/connectors/admin-dashboard-top/custom-wizard-critical-notice.js.es6 deleted file mode 100644 index 803e58a4..00000000 --- a/assets/javascripts/discourse/connectors/admin-dashboard-top/custom-wizard-critical-notice.js.es6 +++ /dev/null @@ -1,19 +0,0 @@ -import { getOwner } from "discourse-common/lib/get-owner"; - -export default { - shouldRender(attrs, ctx) { - return ctx.siteSettings.wizard_critical_notices_on_dashboard; - }, - - setupComponent(attrs, component) { - const controller = getOwner(this).lookup("controller:admin-dashboard"); - - component.set("notices", controller.get("customWizardCriticalNotices")); - controller.addObserver("customWizardCriticalNotices.[]", () => { - if (this._state === "destroying") { - return; - } - component.set("notices", controller.get("customWizardCriticalNotices")); - }); - }, -}; diff --git a/assets/javascripts/discourse/controllers/admin-wizards-notices.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards-notices.js.es6 deleted file mode 100644 index 1721e699..00000000 --- a/assets/javascripts/discourse/controllers/admin-wizards-notices.js.es6 +++ /dev/null @@ -1,68 +0,0 @@ -import Controller from "@ember/controller"; -import CustomWizardNotice from "../models/custom-wizard-notice"; -import discourseComputed from "discourse-common/utils/decorators"; -import { notEmpty } from "@ember/object/computed"; -import { A } from "@ember/array"; -import I18n from "I18n"; - -export default Controller.extend({ - messageUrl: "https://thepavilion.io/t/3652", - messageKey: "info", - messageIcon: "info-circle", - messageClass: "info", - hasNotices: notEmpty("notices"), - page: 0, - loadingMore: false, - canLoadMore: true, - - @discourseComputed("notices.[]", "notices.@each.dismissed") - allDismisssed(notices) { - return notices.every((n) => !n.canDismiss || n.dismissed); - }, - - loadMoreNotices() { - if (!this.canLoadMore) { - return; - } - const page = this.get("page"); - this.set("loadingMore", true); - - CustomWizardNotice.list({ page, include_all: true }) - .then((result) => { - if (result.notices.length === 0) { - this.set("canLoadMore", false); - return; - } - - this.get("notices").pushObjects( - A(result.notices.map((notice) => CustomWizardNotice.create(notice))) - ); - }) - .finally(() => this.set("loadingMore", false)); - }, - - actions: { - loadMore() { - if (this.canLoadMore) { - this.set("page", this.page + 1); - this.loadMoreNotices(); - } - }, - - dismissAll() { - bootbox.confirm( - I18n.t("admin.wizard.notice.dismiss_all.confirm"), - I18n.t("no_value"), - I18n.t("yes_value"), - (result) => { - if (result) { - this.set("loadingMore", true); - CustomWizardNotice.dismissAll().finally(() => - this.set("loadingMore", false) - ); - } - } - ); - }, - }, -}); diff --git a/assets/javascripts/discourse/controllers/admin-wizards-subscription.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards-subscription.js.es6 deleted file mode 100644 index 76f16119..00000000 --- a/assets/javascripts/discourse/controllers/admin-wizards-subscription.js.es6 +++ /dev/null @@ -1,62 +0,0 @@ -import Controller from "@ember/controller"; -import discourseComputed from "discourse-common/utils/decorators"; -import CustomWizardSubscription from "../models/custom-wizard-subscription"; -import { alias } from "@ember/object/computed"; - -export default Controller.extend({ - messageUrl: "https://thepavilion.io/t/3652", - messageType: "info", - messageKey: null, - showSubscription: alias("model.authentication.active"), - - setup() { - const authentication = this.get("model.authentication"); - const subscription = this.get("model.subscription"); - const subscribed = subscription && subscription.active; - const authenticated = authentication && authentication.active; - - if (!subscribed) { - this.set("messageKey", authenticated ? "not_subscribed" : "authorize"); - } else { - this.set( - "messageKey", - !authenticated - ? "subscription_expiring" - : subscribed - ? "subscription_active" - : "subscription_inactive" - ); - } - }, - - @discourseComputed("model.server") - messageOpts(server) { - return { server }; - }, - - actions: { - unauthorize() { - this.set("unauthorizing", true); - - CustomWizardSubscription.unauthorize() - .then((result) => { - if (result.success) { - this.setProperties({ - messageKey: "unauthorized", - messageType: "warn", - "model.authentication": null, - "model.subscription": null, - }); - } else { - this.setProperties({ - messageKey: "unauthorize_failed", - messageType: "error", - }); - } - }) - .finally(() => { - this.set("unauthorizing", false); - }); - }, - }, -}); diff --git a/assets/javascripts/discourse/helpers/notice-badge.js.es6 b/assets/javascripts/discourse/helpers/notice-badge.js.es6 deleted file mode 100644 index ea32b462..00000000 --- a/assets/javascripts/discourse/helpers/notice-badge.js.es6 +++ /dev/null @@ -1,43 +0,0 @@ -import { autoUpdatingRelativeAge } from "discourse/lib/formatter"; -import { iconHTML } from "discourse-common/lib/icon-library"; -import I18n from "I18n"; -import { registerUnbound } from "discourse-common/lib/helpers"; -import { htmlSafe } from "@ember/template"; - -registerUnbound("notice-badge", function (attrs) { - let tag = attrs.url ? "a" : "div"; - let attrStr = ""; - if (attrs.title) { - attrStr += `title='${I18n.t(attrs.title)}'`; - } - if (attrs.url) { - attrStr += `href='${attrs.url}'`; - } - let html = `<${tag} class="${ - attrs.class ? `${attrs.class} ` : "" - }notice-badge" ${attrStr}>`; - if (attrs.icon) { - html += iconHTML(attrs.icon); - } - if (attrs.label) { - if (attrs.icon) { - html += " "; - } - html += `${I18n.t(attrs.label)}`; - } - if (attrs.date) { - if (attrs.icon || attrs.label) { - html += " "; - } - let dateAttrs = {}; - if (attrs.leaveAgo) { - dateAttrs = { - format: "medium", - leaveAgo: true, - }; - } - html += autoUpdatingRelativeAge(new Date(attrs.date), dateAttrs); - } - html += ``; - return htmlSafe(html); -}); diff --git a/assets/javascripts/discourse/initializers/custom-wizard-edits.js.es6 b/assets/javascripts/discourse/initializers/custom-wizard-edits.js.es6 index 6a027dee..40d21b35 100644 --- a/assets/javascripts/discourse/initializers/custom-wizard-edits.js.es6 +++ b/assets/javascripts/discourse/initializers/custom-wizard-edits.js.es6 @@ -1,6 +1,5 @@ import DiscourseURL from "discourse/lib/url"; import { withPluginApi } from "discourse/lib/plugin-api"; -import CustomWizardNotice from "../models/custom-wizard-notice"; import { isPresent } from "@ember/utils"; import { A } from "@ember/array"; import getUrl from "discourse-common/lib/get-url"; @@ -23,50 +22,6 @@ export default { }; withPluginApi("0.8.36", (api) => { - api.modifyClass("route:admin-dashboard", { - pluginId: "custom-wizard", - - setupController(controller) { - this._super(...arguments); - - controller.loadCriticalNotices(); - controller.subscribe(); - }, - }); - - api.modifyClass("controller:admin-dashboard", { - pluginId: "custom-wizard", - criticalNotices: A(), - - unsubscribe() { - this.messageBus.unsubscribe("/custom-wizard/notices"); - }, - - subscribe() { - this.unsubscribe(); - this.messageBus.subscribe("/custom-wizard/notices", (data) => { - if (isPresent(data.active_notice_count)) { - this.loadCriticalNotices(); - } - }); - }, - - loadCriticalNotices() { - CustomWizardNotice.list({ - type: ["connection_error", "warning"], - archetype: "plugin_status", - visible: true, - }).then((result) => { - if (result.notices && result.notices.length) { - const criticalNotices = A( - result.notices.map((n) => CustomWizardNotice.create(n)) - ); - this.set("customWizardCriticalNotices", criticalNotices); - } - }); - }, - }); - api.modifyClass("component:d-navigation", { pluginId: "custom-wizard", actions: { diff --git a/assets/javascripts/discourse/mixins/notice-message.js.es6 b/assets/javascripts/discourse/mixins/notice-message.js.es6 deleted file mode 100644 index 76e311bb..00000000 --- a/assets/javascripts/discourse/mixins/notice-message.js.es6 +++ /dev/null @@ -1,68 +0,0 @@ -import Mixin from "@ember/object/mixin"; -import { bind, scheduleOnce } from "@ember/runloop"; -import { cookAsync } from "discourse/lib/text"; -import { createPopper } from "@popperjs/core"; - -export default Mixin.create({ - showCookedMessage: false, - - didReceiveAttrs() { - const message = this.notice.message; - cookAsync(message).then((cooked) => { - this.set("cookedMessage", cooked); - }); - }, - - createMessageModal() { - let container = this.element.querySelector(".notice-message"); - let modal = this.element.querySelector(".cooked-notice-message"); - - this._popper = createPopper(container, modal, { - strategy: "absolute", - placement: "bottom-start", - modifiers: [ - { - name: "preventOverflow", - }, - { - name: "offset", - options: { - offset: [0, 5], - }, - }, - ], - }); - }, - - didInsertElement() { - $(document).on("click", bind(this, this.documentClick)); - }, - - willDestroyElement() { - $(document).off("click", bind(this, this.documentClick)); - }, - - documentClick(event) { - if (this._state === "destroying") { - return; - } - - if ( - !event.target.closest( - `[data-notice-id="${this.notice.id}"] .notice-message` - ) - ) { - this.set("showCookedMessage", false); - } - }, - - actions: { - toggleCookedMessage() { - this.toggleProperty("showCookedMessage"); - - if (this.showCookedMessage) { - scheduleOnce("afterRender", this, this.createMessageModal); - } - }, - }, -}); diff --git a/assets/javascripts/discourse/models/custom-wizard-notice.js.es6 b/assets/javascripts/discourse/models/custom-wizard-notice.js.es6 deleted file mode 100644 index 035e2ad5..00000000 --- a/assets/javascripts/discourse/models/custom-wizard-notice.js.es6 +++ /dev/null @@ -1,74 +0,0 @@ -import EmberObject from "@ember/object"; -import discourseComputed from "discourse-common/utils/decorators"; -import { ajax } from "discourse/lib/ajax"; -import { popupAjaxError } from "discourse/lib/ajax-error"; -import { and, not, notEmpty } from "@ember/object/computed"; -import { dasherize } from "@ember/string"; -import I18n from "I18n"; - -const CustomWizardNotice = EmberObject.extend({ - expired: notEmpty("expired_at"), - dismissed: notEmpty("dismissed_at"), - hidden: notEmpty("hidden_at"), - notHidden: not("hidden"), - notDismissed: not("dismissed"), - canDismiss: and("dismissable", "notDismissed"), - canHide: and("can_hide", "notHidden"), - - @discourseComputed("type") - typeClass(type) { - return dasherize(type); - }, - - @discourseComputed("type") - typeLabel(type) { - return I18n.t(`admin.wizard.notice.type.${type}`); - }, - - dismiss() { - if (!this.get("canDismiss")) { - return; - } - - return ajax(`/admin/wizards/notice/${this.get("id")}/dismiss`, { - type: "PUT", - }) - .then((result) => { - if (result.success) { - this.set("dismissed_at", result.dismissed_at); - } - }) - .catch(popupAjaxError); - }, - - hide() { - if (!this.get("canHide")) { - return; - } - - return ajax(`/admin/wizards/notice/${this.get("id")}/hide`, { type: "PUT" }) - .then((result) => { - if (result.success) { - this.set("hidden_at", result.hidden_at); - } - }) - .catch(popupAjaxError); - }, -}); - -CustomWizardNotice.reopenClass({ - list(data = {}) { - return ajax("/admin/wizards/notice", { - type: "GET", - data, - }).catch(popupAjaxError); - }, - - dismissAll() { - return ajax("/admin/wizards/notice/dismiss", { - type: "PUT", - }).catch(popupAjaxError); - }, -}); - -export default CustomWizardNotice; diff --git a/assets/javascripts/discourse/models/custom-wizard-subscription.js.es6 b/assets/javascripts/discourse/models/custom-wizard-subscription.js.es6 deleted file mode 100644 index 469460d5..00000000 --- a/assets/javascripts/discourse/models/custom-wizard-subscription.js.es6 +++ /dev/null @@ -1,33 +0,0 @@ -import { ajax } from "discourse/lib/ajax"; -import { popupAjaxError } from "discourse/lib/ajax-error"; -import EmberObject from "@ember/object"; - -const CustomWizardSubscription = EmberObject.extend(); - -const basePath = "/admin/wizards/subscription"; - -CustomWizardSubscription.reopenClass({ - status() { - return ajax(basePath, { - type: "GET", - }).catch(popupAjaxError); - }, - - authorize() { - window.location.href = `${basePath}/authorize`; - }, - - unauthorize() { - return ajax(`${basePath}/authorize`, { - type: "DELETE", - }).catch(popupAjaxError); - }, - - update() { - return ajax(basePath, { - type: "POST", - }).catch(popupAjaxError); - }, -}); - -export default CustomWizardSubscription; diff --git a/assets/javascripts/discourse/routes/admin-wizards-notices.js.es6 b/assets/javascripts/discourse/routes/admin-wizards-notices.js.es6 deleted file mode 100644 index 1d8b7cc8..00000000 --- a/assets/javascripts/discourse/routes/admin-wizards-notices.js.es6 +++ /dev/null @@ -1,17 +0,0 @@ -import CustomWizardNotice from "../models/custom-wizard-notice"; -import DiscourseRoute from "discourse/routes/discourse"; -import { A } from "@ember/array"; - -export default DiscourseRoute.extend({ - model() { - return CustomWizardNotice.list({ include_all: true }); - }, - - setupController(controller, model) { - controller.setProperties({ - notices: A( - model.notices.map((notice) => CustomWizardNotice.create(notice)) - ), - }); - }, -}); diff --git a/assets/javascripts/discourse/routes/admin-wizards-subscription.js.es6 b/assets/javascripts/discourse/routes/admin-wizards-subscription.js.es6 deleted file mode 100644 index 4824425a..00000000 --- a/assets/javascripts/discourse/routes/admin-wizards-subscription.js.es6 +++ /dev/null @@ -1,19 +0,0 @@ -import CustomWizardSubscription from "../models/custom-wizard-subscription"; -import DiscourseRoute from "discourse/routes/discourse"; - -export default DiscourseRoute.extend({ - model() { - return CustomWizardSubscription.status(); - }, - - setupController(controller, model) { - controller.set("model", model); - controller.setup(); - }, - - actions: { - authorize() { - CustomWizardSubscription.authorize(); - }, - }, -}); diff --git a/assets/javascripts/discourse/templates/admin-wizards-notices.hbs b/assets/javascripts/discourse/templates/admin-wizards-notices.hbs deleted file mode 100644 index 039afe49..00000000 --- a/assets/javascripts/discourse/templates/admin-wizards-notices.hbs +++ /dev/null @@ -1,49 +0,0 @@ -
-

{{i18n "admin.wizard.notices.title"}}

- -
- {{d-button - label="admin.wizard.notice.dismiss_all.label" - title="admin.wizard.notice.dismiss_all.title" - action=(action "dismissAll") - disabled=allDismisssed - icon="check"}} -
-
- -{{wizard-message - key=messageKey - url=messageUrl - type=messageType - opts=messageOpts - items=messageItems - loading=loading - component="notices"}} - -
- {{#load-more selector=".wizard-table tr" action=(action "loadMore")}} - {{#if hasNotices}} - - - - - - - - - - - {{#each notices as |notice|}} - {{wizard-notice-row notice=notice}} - {{/each}} - -
{{I18n "admin.wizard.notice.time"}}{{I18n "admin.wizard.notice.type.label"}}{{I18n "admin.wizard.notice.title"}}{{I18n "admin.wizard.notice.status"}}
- {{else}} - {{#unless loadingMore}} -

{{i18n "search.no_results"}}

- {{/unless}} - {{/if}} - - {{conditional-loading-spinner condition=loadingMore}} - {{/load-more}} -
diff --git a/assets/javascripts/discourse/templates/admin-wizards-subscription.hbs b/assets/javascripts/discourse/templates/admin-wizards-subscription.hbs deleted file mode 100644 index c75f3e1b..00000000 --- a/assets/javascripts/discourse/templates/admin-wizards-subscription.hbs +++ /dev/null @@ -1,35 +0,0 @@ -
-

{{i18n "admin.wizard.subscription.title"}}

- -
- {{#if model.authentication.active}} - {{conditional-loading-spinner size="small" condition=unauthorizing}} - - {{i18n "admin.wizard.subscription.unauthorize"}} - - - {{else}} - {{d-button - icon="id-card" - class="btn-primary" - label="admin.wizard.subscription.authorize" - title="admin.wizard.subscription.authorize" - action=(route-action "authorize")}} - {{/if}} -
-
- -{{wizard-message - key=messageKey - url=messageUrl - type=messageType - opts=messageOpts - component="subscription"}} - -
- {{#if showSubscription}} - {{wizard-subscription subscription=model.subscription}} - {{/if}} -
diff --git a/assets/javascripts/discourse/templates/admin-wizards.hbs b/assets/javascripts/discourse/templates/admin-wizards.hbs index 4447380f..a50748cc 100644 --- a/assets/javascripts/discourse/templates/admin-wizards.hbs +++ b/assets/javascripts/discourse/templates/admin-wizards.hbs @@ -7,15 +7,8 @@ {{/if}} {{nav-item route="adminWizardsLogs" label="admin.wizard.log.nav_label"}} {{nav-item route="adminWizardsManager" label="admin.wizard.manager.nav_label"}} - {{nav-item route="adminWizardsSubscription" label="admin.wizard.subscription.nav_label"}}
- {{d-icon "far-life-ring"}}{{i18n "admin.wizard.support_button.label"}} diff --git a/assets/javascripts/discourse/templates/components/wizard-notice-row.hbs b/assets/javascripts/discourse/templates/components/wizard-notice-row.hbs deleted file mode 100644 index cc22a42e..00000000 --- a/assets/javascripts/discourse/templates/components/wizard-notice-row.hbs +++ /dev/null @@ -1,30 +0,0 @@ - - {{#if notice.updated_at}} - {{notice-badge class="notice-updated-at" date=notice.updated_at label="admin.wizard.notice.updated_at" leaveAgo=true}} - {{else}} - {{notice-badge class="notice-created-at" date=notice.created_at label="admin.wizard.notice.created_at" leaveAgo=true}} - {{/if}} - -{{notice.typeLabel}} - - {{notice.title}} - {{#if showCookedMessage}} - {{cookedMessage}} - {{/if}} - - - {{#if notice.canDismiss}} - {{d-button - action="dismiss" - label="admin.wizard.notice.dismiss.label" - title="admin.wizard.notice.dismiss.title" - class="btn-dismiss" - icon="check"}} - {{else if notice.dismissed}} - {{i18n "admin.wizard.notice.dismissed_at"}} {{format-date notice.dismissed_at leaveAgo="true"}} - {{else if notice.expired}} - {{i18n "admin.wizard.notice.expired_at"}} {{format-date notice.expired_at leaveAgo="true"}} - {{else}} - {{i18n "admin.wizard.notice.active"}} - {{/if}} - diff --git a/assets/javascripts/discourse/templates/components/wizard-notice.hbs b/assets/javascripts/discourse/templates/components/wizard-notice.hbs deleted file mode 100644 index 24a853d3..00000000 --- a/assets/javascripts/discourse/templates/components/wizard-notice.hbs +++ /dev/null @@ -1,39 +0,0 @@ -
-
- {{notice.title}} - {{#if showCookedMessage}} - {{cookedMessage}} - {{/if}} -
-
- {{#if notice.expired}} - {{notice-badge class="notice-expired-at" icon="check" label="admin.wizard.notice.expired_at" date=notice.expired_at}} - {{/if}} - {{#if showPlugin}} - {{notice-badge class="notice-plugin" icon="plug" title="admin.wizard.notice.plugin" label="admin.wizard.notice.plugin" url="/admin/wizards/notices"}} - {{/if}} - {{notice-badge class="notice-created-at" icon="far-clock" label="admin.wizard.notice.created_at" date=notice.created_at leaveAgo=true}} - {{#if notice.updated_at}} - {{notice-badge class="notice-updated-at" icon="far-clock" label="admin.wizard.notice.updated_at" date=notice.updated_at}} - {{/if}} - - {{#if notice.canDismiss}} -
- {{#if dismissing}} - {{loading-spinner size="small"}} - {{else}} - {{d-icon "times"}} - {{/if}} -
- {{/if}} - {{#if notice.canHide}} -
- {{#if hiding}} - {{loading-spinner size="small"}} - {{else}} - {{d-icon "far-eye-slash"}} - {{/if}} -
- {{/if}} -
-
diff --git a/assets/javascripts/discourse/templates/components/wizard-subscription.hbs b/assets/javascripts/discourse/templates/components/wizard-subscription.hbs deleted file mode 100644 index 418225a3..00000000 --- a/assets/javascripts/discourse/templates/components/wizard-subscription.hbs +++ /dev/null @@ -1,31 +0,0 @@ -
-

{{title}}

- -
- - {{#if updating}} - {{loading-spinner size="small"}} - {{else if updateIcon}} - {{d-icon updateIcon}} - {{/if}} - - {{d-button - icon="sync" - action=(action "update") - disabled=updating - title="admin.wizard.subscription.subscription.update" - label="admin.wizard.subscription.subscription.update"}} -
-
- -{{#if subscribed}} -
-
{{stateLabel}}
- - {{#if subscription.updated_at}} -
- {{i18n "admin.wizard.subscription.subscription.last_updated"}} {{format-date subscription.updated_at leaveAgo="true"}} -
- {{/if}} -
-{{/if}} diff --git a/assets/javascripts/wizard/tests/bootstrap.js.es6 b/assets/javascripts/wizard/tests/bootstrap.js.es6 index d2c503ad..dec0c4b4 100644 --- a/assets/javascripts/wizard/tests/bootstrap.js.es6 +++ b/assets/javascripts/wizard/tests/bootstrap.js.es6 @@ -1,17 +1,19 @@ // discourse-skip-module -document.addEventListener("DOMContentLoaded", function () { - document.body.insertAdjacentHTML( - "afterbegin", - ` -
- - ` - ); -}); +if (window.location.pathname.indexOf("/w/") > -1 && Ember.testing) { + document.addEventListener("DOMContentLoaded", function () { + document.body.insertAdjacentHTML( + "afterbegin", + ` +
+ + ` + ); + }); -Object.keys(requirejs.entries).forEach(function (entry) { - if (/\-test/.test(entry)) { - requirejs(entry); - } -}); + Object.keys(requirejs.entries).forEach(function (entry) { + if (/\-test/.test(entry)) { + requirejs(entry); + } + }); +} diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index d0ca29a6..2483503b 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -54,20 +54,6 @@ en: after_time: "After time setting is invalid." liquid_syntax_error: "Liquid syntax error in %{attribute}: %{message}" - notice: - connection_error: "Failed to connect to http://%{domain}" - compatibility_issue: - title: The Custom Wizard Plugin is incompatibile with the latest version of Discourse. - message: Please check the Custom Wizard Plugin status on [%{domain}](http://%{domain}) before updating Discourse. - plugin_status: - connection_error: - title: Unable to connect to the Custom Wizard Plugin status server - message: Please check the Custom Wizard Plugin status on [%{domain}](http://%{domain}) before updating Discourse. If this issue persists contact support@thepavilion.io for further assistance. - subscription_message: - connection_error: - title: Unable to connect to the Custom Wizard Plugin subscription server - message: If this issue persists contact support@thepavilion.io for further assistance. - site_settings: custom_wizard_enabled: "Enable custom wizards." wizard_redirect_exclude_paths: "Routes excluded from wizard redirects." diff --git a/config/routes.rb b/config/routes.rb index 514605de..2d9f52c6 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -45,17 +45,5 @@ Discourse::Application.routes.append do get 'admin/wizards/manager/export' => 'admin_manager#export' post 'admin/wizards/manager/import' => 'admin_manager#import' delete 'admin/wizards/manager/destroy' => 'admin_manager#destroy' - - get 'admin/wizards/subscription' => 'admin_subscription#index' - post 'admin/wizards/subscription' => 'admin_subscription#update_subscription' - get 'admin/wizards/subscription/authorize' => 'admin_subscription#authorize' - get 'admin/wizards/subscription/authorize/callback' => 'admin_subscription#authorize_callback' - delete 'admin/wizards/subscription/authorize' => 'admin_subscription#destroy_authentication' - - get 'admin/wizards/notice' => 'admin_notice#index' - put 'admin/wizards/notice/:notice_id/dismiss' => 'admin_notice#dismiss' - put 'admin/wizards/notice/:notice_id/hide' => 'admin_notice#hide' - put 'admin/wizards/notice/dismiss' => 'admin_notice#dismiss_all' - get 'admin/wizards/notices' => 'admin_notice#index' end end diff --git a/lib/custom_wizard/notice.rb b/lib/custom_wizard/notice.rb deleted file mode 100644 index 9d5c1edf..00000000 --- a/lib/custom_wizard/notice.rb +++ /dev/null @@ -1,358 +0,0 @@ -# frozen_string_literal: true - -class CustomWizard::Notice - include ActiveModel::Serialization - - PLUGIN_STATUS_DOMAINS = { - "main" => "plugins.discourse.pavilion.tech", - "master" => "plugins.discourse.pavilion.tech", - "tests-passed" => "plugins.discourse.pavilion.tech", - "stable" => "stable.plugins.discourse.pavilion.tech" - } - SUBSCRIPTION_MESSAGE_DOMAIN = "test.thepavilion.io" - LOCALHOST_DOMAIN = "localhost:3000" - PLUGIN_STATUSES_TO_WARN = %w(incompatible tests_failing) - CHECK_PLUGIN_STATUS_ON_BRANCH = %w(tests-passed main stable) - PAGE_LIMIT = 30 - - attr_reader :id, - :title, - :message, - :type, - :archetype, - :created_at - - attr_accessor :retrieved_at, - :updated_at, - :dismissed_at, - :expired_at, - :hidden_at - - def initialize(attrs) - @id = self.class.generate_notice_id(attrs[:title], attrs[:created_at]) - @title = attrs[:title] - @message = attrs[:message] - @type = attrs[:type].to_i - @archetype = attrs[:archetype].to_i - @created_at = attrs[:created_at] - @updated_at = attrs[:updated_at] - @retrieved_at = attrs[:retrieved_at] - @dismissed_at = attrs[:dismissed_at] - @expired_at = attrs[:expired_at] - @hidden_at = attrs[:hidden_at] - end - - def dismiss! - if dismissable? - self.dismissed_at = DateTime.now.iso8601(3) - self.save_and_publish - end - end - - def hide! - if can_hide? - self.hidden_at = DateTime.now.iso8601(3) - self.save_and_publish - end - end - - def expire! - if !expired? - self.expired_at = DateTime.now.iso8601(3) - self.save_and_publish - end - end - - def save_and_publish - if self.save - self.class.publish_notice_count - true - else - false - end - end - - def expired? - expired_at.present? - end - - def dismissed? - dismissed_at.present? - end - - def dismissable? - !expired? && !dismissed? && type === self.class.types[:info] - end - - def hidden? - hidden_at.present? - end - - def can_hide? - !hidden? && ( - type === self.class.types[:connection_error] || - type === self.class.types[:warning] - ) && ( - archetype === self.class.archetypes[:plugin_status] - ) - end - - def save - attrs = { - expired_at: expired_at, - updated_at: updated_at, - retrieved_at: retrieved_at, - created_at: created_at, - title: title, - message: message, - type: type, - archetype: archetype - } - - if current = self.class.find(self.id) - attrs[:dismissed_at] = current.dismissed_at || self.dismissed_at - attrs[:hidden_at] = current.hidden_at || self.hidden_at - end - - self.class.store(id, attrs) - end - - def self.types - @types ||= Enum.new( - info: 0, - warning: 1, - connection_error: 2 - ) - end - - def self.archetypes - @archetypes ||= Enum.new( - subscription_message: 0, - plugin_status: 1 - ) - end - - def self.update(skip_subscription: false, skip_plugin: false) - notices = [] - - if !skip_subscription - subscription_messages = request(:subscription_message) - - if subscription_messages.present? - subscription_notices = convert_subscription_messages_to_notices(subscription_messages[:messages]) - notices.push(*subscription_notices) - end - end - - if !skip_plugin && request_plugin_status? - plugin_status = request(:plugin_status) - - if plugin_status.present? && plugin_status[:status].present? - plugin_notice = convert_plugin_status_to_notice(plugin_status) - notices.push(plugin_notice) if plugin_notice - end - end - - if notices.any? - notices.each do |notice_data| - notice = new(notice_data) - notice.retrieved_at = DateTime.now.iso8601(3) - notice.save - end - end - - publish_notice_count - end - - def self.publish_notice_count - payload = { - active_notice_count: CustomWizard::Notice.active_count - } - MessageBus.publish("/custom-wizard/notices", payload, group_ids: [Group::AUTO_GROUPS[:admins]]) - end - - def self.convert_subscription_messages_to_notices(messages) - messages.reduce([]) do |result, message| - id = generate_notice_id(message[:title], message[:created_at]) - result.push( - id: id, - title: message[:title], - message: message[:message], - type: types[message[:type].to_sym], - archetype: archetypes[:subscription_message], - created_at: message[:created_at], - expired_at: message[:expired_at] - ) - result - end - end - - def self.convert_plugin_status_to_notice(plugin_status) - notice = nil - - if PLUGIN_STATUSES_TO_WARN.include?(plugin_status[:status]) - title = I18n.t('wizard.notice.compatibility_issue.title') - created_at = plugin_status[:status_changed_at] - id = generate_notice_id(title, created_at) - - unless exists?(id) - message = I18n.t('wizard.notice.compatibility_issue.message', domain: plugin_status_domain) - notice = { - id: id, - title: title, - message: message, - type: types[:warning], - archetype: archetypes[:plugin_status], - created_at: created_at - } - end - else - expire_all(types[:warning], archetypes[:plugin_status]) - end - - notice - end - - def self.notify_connection_errors(archetype) - domain = self.send("#{archetype.to_s}_domain") - title = I18n.t("wizard.notice.#{archetype.to_s}.connection_error.title") - notices = list(type: types[:connection_error], archetype: archetypes[archetype.to_sym], title: title) - - if notices.any? - notice = notices.first - notice.updated_at = DateTime.now.iso8601(3) - notice.save - else - notice = new( - title: title, - message: I18n.t("wizard.notice.#{archetype.to_s}.connection_error.message", domain: domain), - archetype: archetypes[archetype.to_sym], - type: types[:connection_error], - created_at: DateTime.now.iso8601(3), - updated_at: DateTime.now.iso8601(3) - ) - notice.save - end - end - - def self.request_plugin_status? - CHECK_PLUGIN_STATUS_ON_BRANCH.include?(Discourse.git_branch) || Rails.env.test? || Rails.env.development? - end - - def self.subscription_message_domain - return LOCALHOST_DOMAIN if (Rails.env.test? || Rails.env.development?) - SUBSCRIPTION_MESSAGE_DOMAIN - end - - def self.subscription_message_url - "https://#{subscription_message_domain}/subscription-server/messages.json" - end - - def self.plugin_status_domain - return LOCALHOST_DOMAIN if (Rails.env.test? || Rails.env.development?) - PLUGIN_STATUS_DOMAINS[Discourse.git_branch] - end - - def self.plugin_status_url - "https://#{plugin_status_domain}/plugin-manager/status/discourse-custom-wizard" - end - - def self.request(archetype) - url = self.send("#{archetype.to_s}_url") - - begin - response = Excon.get(url) - rescue Excon::Error::Socket, Excon::Error::Timeout => e - response = nil - end - connection_error = CustomWizard::Notice::ConnectionError.new(archetype) - - if response && response.status == 200 - connection_error.expire! - expire_all(types[:connection_error], archetypes[archetype.to_sym]) - - begin - data = JSON.parse(response.body).deep_symbolize_keys - rescue JSON::ParserError - return nil - end - - data - else - connection_error.create! - notify_connection_errors(archetype) if connection_error.reached_limit? - nil - end - end - - def self.namespace - "#{CustomWizard::PLUGIN_NAME}_notice" - end - - def self.find(id) - raw = PluginStore.get(namespace, id) - new(raw.symbolize_keys) if raw.present? - end - - def self.exists?(id) - PluginStoreRow.where(plugin_name: namespace, key: id).exists? - end - - def self.store(id, raw_notice) - PluginStore.set(namespace, id, raw_notice) - end - - def self.list_query(type: nil, archetype: nil, title: nil, include_all: false, page: nil, visible: false) - query = PluginStoreRow.where(plugin_name: namespace) - query = query.where("(value::json->>'hidden_at') IS NULL") if visible - query = query.where("(value::json->>'dismissed_at') IS NULL") unless include_all - query = query.where("(value::json->>'expired_at') IS NULL") unless include_all - query = query.where("(value::json->>'archetype')::integer = ?", archetype) if archetype - if type - type_query_str = type.is_a?(Array) ? "(value::json->>'type')::integer IN (?)" : "(value::json->>'type')::integer = ?" - query = query.where(type_query_str, type) - end - query = query.where("(value::json->>'title')::text = ?", title) if title - query = query.limit(PAGE_LIMIT).offset(page.to_i * PAGE_LIMIT) if !page.nil? - query.order("value::json->>'expired_at' DESC, value::json->>'updated_at' DESC,value::json->>'dismissed_at' DESC, value::json->>'created_at' DESC") - end - - def self.list(type: nil, archetype: nil, title: nil, include_all: false, page: 0, visible: false) - list_query(type: type, archetype: archetype, title: title, include_all: include_all, page: page, visible: visible) - .map { |r| self.new(JSON.parse(r.value).symbolize_keys) } - end - - def self.active_count - list_query.count - end - - def self.dismiss_all - dismissed_count = PluginStoreRow.where(" - plugin_name = '#{namespace}' AND - (value::json->>'type')::integer = #{types[:info]} AND - (value::json->>'expired_at') IS NULL AND - (value::json->>'dismissed_at') IS NULL - ").update_all(" - value = jsonb_set(value::jsonb, '{dismissed_at}', (to_json(now())::text)::jsonb, true) - ") - publish_notice_count if dismissed_count.to_i > 0 - dismissed_count - end - - def self.expire_all(type, archetype) - expired_count = PluginStoreRow.where(" - plugin_name = '#{namespace}' AND - (value::json->>'type')::integer = #{type} AND - (value::json->>'archetype')::integer = #{archetype} AND - (value::json->>'expired_at') IS NULL - ").update_all(" - value = jsonb_set(value::jsonb, '{expired_at}', (to_json(now())::text)::jsonb, true) - ") - publish_notice_count if expired_count.to_i > 0 - expired_count - end - - def self.generate_notice_id(title, created_at) - Digest::SHA1.hexdigest("#{title}-#{created_at}") - end -end diff --git a/lib/custom_wizard/notice/connection_error.rb b/lib/custom_wizard/notice/connection_error.rb deleted file mode 100644 index a1d834c6..00000000 --- a/lib/custom_wizard/notice/connection_error.rb +++ /dev/null @@ -1,77 +0,0 @@ -# frozen_string_literal: true - -class CustomWizard::Notice::ConnectionError - - attr_reader :archetype - - def initialize(archetype) - @archetype = archetype - end - - def create! - if attrs = current_error - key = "#{archetype.to_s}_error_#{attrs[:id]}" - attrs[:updated_at] = Time.now - attrs[:count] = attrs[:count].to_i + 1 - else - domain = CustomWizard::Notice.send("#{archetype.to_s}_domain") - id = SecureRandom.hex(8) - attrs = { - id: id, - message: I18n.t("wizard.notice.connection_error", domain: domain), - archetype: CustomWizard::Notice.archetypes[archetype.to_sym], - created_at: Time.now, - count: 1 - } - key = "#{archetype.to_s}_error_#{id}" - end - - PluginStore.set(namespace, key, attrs) - - @current_error = nil - end - - def expire! - if query = current_error(query_only: true) - record = query.first - error = JSON.parse(record.value) - error['expired_at'] = Time.now - record.value = error.to_json - record.save - end - end - - def plugin_status_limit - 5 - end - - def subscription_message_limit - 10 - end - - def limit - self.send("#{archetype.to_s}_limit") - end - - def reached_limit? - return false unless current_error.present? - current_error[:count].to_i >= limit - end - - def namespace - "#{CustomWizard::PLUGIN_NAME}_notice_connection" - end - - def current_error(query_only: false) - @current_error ||= begin - query = PluginStoreRow.where(plugin_name: namespace) - query = query.where("(value::json->>'archetype')::integer = ?", CustomWizard::Notice.archetypes[archetype.to_sym]) - query = query.where("(value::json->>'expired_at') IS NULL") - - return nil if !query.exists? - return query if query_only - - JSON.parse(query.first.value).deep_symbolize_keys - end - end -end diff --git a/lib/custom_wizard/subscription.rb b/lib/custom_wizard/subscription.rb index 7bac7f59..39febc14 100644 --- a/lib/custom_wizard/subscription.rb +++ b/lib/custom_wizard/subscription.rb @@ -1,240 +1,8 @@ -# frozen_string_literal: true - class CustomWizard::Subscription - include ActiveModel::Serialization - - attr_accessor :authentication, - :subscription - - SUBSCRIPTION_LEVELS = { - standard: { - actions: ["send_message", "add_to_group", "watch_categories"], - custom_fields: { - klass: [], - type: ["json"], - }, - }, - business: { - actions: ["create_category", "create_group", "send_to_api"], - custom_fields: { - klass: ["group", "category"], - type: [], - }, - }, - } - - def initialize - @authentication = CustomWizard::Subscription::Authentication.new(get_authentication) - @subscription = CustomWizard::Subscription::Subscription.new(get_subscription) - end - - def authorized? - @authentication.active? - end - - def subscribed? - @subscription.active? - end - - def server - "test.thepavilion.io" - end - - def subscription_type - "stripe" - end - - def type - @subscription.type - end - - def client_name - "custom-wizard" - end - - def scope - "discourse-subscription-server:user_subscription" - end - - def requires_additional_subscription(kategory, sub_kategory) - case kategory - when "actions" - case self.type - when "business" - [] - when "standard" - SUBSCRIPTION_LEVELS[:business][kategory.to_sym] - else - SUBSCRIPTION_LEVELS[:standard][kategory.to_sym] + SUBSCRIPTION_LEVELS[:business][kategory.to_sym] - end - when "custom_fields" - case self.type - when "business" - [] - when "standard" - SUBSCRIPTION_LEVELS[:business][kategory.to_sym][sub_kategory.to_sym] - else - SUBSCRIPTION_LEVELS[:standard][kategory.to_sym][sub_kategory.to_sym] + SUBSCRIPTION_LEVELS[:business][kategory.to_sym][sub_kategory.to_sym] - end - else - [] - end - end - - def update - if @authentication.active? - response = Excon.get( - "https://#{server}/subscription-server/user-subscriptions/#{subscription_type}/#{client_name}", - headers: { - "User-Api-Key" => @authentication.api_key - } - ) - - if response.status == 200 - begin - data = JSON.parse(response.body).deep_symbolize_keys - rescue JSON::ParserError - return false - end - - return false unless data && data.is_a?(Hash) - subscriptions = data[:subscriptions] - - if subscriptions.present? && type = subscriptions.first[:price_nickname] - @subscription = set_subscription(type) - return true - end - end - end - - destroy_subscription - false - end - - def destroy_subscription - if remove_subscription - @subscription = CustomWizard::Subscription::Subscription.new(get_subscription) - !@subscription.active? - else - false - end - end - - def authentication_url(user_id, request_id) - keys = @authentication.generate_keys(user_id, request_id) - params = { - public_key: keys.public_key, - nonce: keys.nonce, - client_id: @authentication.client_id, - auth_redirect: "#{Discourse.base_url}/admin/wizards/subscription/authorize/callback", - application_name: SiteSetting.title, - scopes: scope - } - - uri = URI.parse("https://#{server}/user-api-key/new") - uri.query = URI.encode_www_form(params) - uri.to_s - end - - def authentication_response(request_id, payload) - data = @authentication.decrypt_payload(request_id, payload) - return false unless data.is_a?(Hash) && data[:key] && data[:user_id] - - api_key = data[:key] - user_id = data[:user_id] - user = User.find(user_id) - - if user&.admin - @authentication = set_authentication(api_key, user.id) - true - else - false - end - end - - def destroy_authentication - if remove_authentication - @authentication = CustomWizard::Subscription::Authentication.new(get_authentication) - !@authentication.active? - else - false - end - end - def self.subscribed? - self.new.subscribed? end def self.type - self.new.type - end - def self.requires_additional_subscription(kategory, sub_kategory) - self.new.requires_additional_subscription(kategory, sub_kategory) - end - - def self.authorized? - self.new.authorized? - end - - def self.update - self.new.update - end - - def self.namespace - "custom_wizard_subscription" - end - - private - - def subscription_db_key - "subscription" - end - - def authentication_db_key - "authentication" - end - - def get_subscription - raw = PluginStore.get(self.class.namespace, subscription_db_key) - - if raw.present? - OpenStruct.new( - type: raw['type'], - updated_at: raw['updated_at'] - ) - end - end - - def remove_subscription - PluginStore.remove(self.class.namespace, subscription_db_key) - end - - def set_subscription(type) - PluginStore.set(CustomWizard::Subscription.namespace, subscription_db_key, type: type, updated_at: Time.now) - CustomWizard::Subscription::Subscription.new(get_subscription) - end - - def get_authentication - raw = PluginStore.get(self.class.namespace, authentication_db_key) - OpenStruct.new( - key: raw && raw['key'], - auth_by: raw && raw['auth_by'], - auth_at: raw && raw['auth_at'] - ) - end - - def set_authentication(key, user_id) - PluginStore.set(self.class.namespace, authentication_db_key, - key: key, - auth_by: user_id, - auth_at: Time.now - ) - CustomWizard::Subscription::Authentication.new(get_authentication) - end - - def remove_authentication - PluginStore.remove(self.class.namespace, authentication_db_key) - get_authentication end end diff --git a/lib/custom_wizard/subscription/authentication.rb b/lib/custom_wizard/subscription/authentication.rb deleted file mode 100644 index 7ad02d2c..00000000 --- a/lib/custom_wizard/subscription/authentication.rb +++ /dev/null @@ -1,95 +0,0 @@ -# frozen_string_literal: true -class CustomWizard::Subscription::Authentication - include ActiveModel::Serialization - - attr_reader :client_id, - :auth_by, - :auth_at, - :api_key - - def initialize(auth) - if auth - @api_key = auth.key - @auth_at = auth.auth_at - @auth_by = auth.auth_by - end - - @client_id = get_client_id || set_client_id - end - - def active? - @api_key.present? - end - - def generate_keys(user_id, request_id) - rsa = OpenSSL::PKey::RSA.generate(2048) - nonce = SecureRandom.hex(32) - set_keys(request_id, user_id, rsa, nonce) - - OpenStruct.new(nonce: nonce, public_key: rsa.public_key) - end - - def decrypt_payload(request_id, payload) - keys = get_keys(request_id) - - return false unless keys.present? && keys.pem - delete_keys(request_id) - - rsa = OpenSSL::PKey::RSA.new(keys.pem) - decrypted_payload = rsa.private_decrypt(Base64.decode64(payload)) - - return false unless decrypted_payload.present? - - begin - data = JSON.parse(decrypted_payload).symbolize_keys - rescue JSON::ParserError - return false - end - - return false unless data[:nonce] == keys.nonce - data[:user_id] = keys.user_id - - data - end - - def get_keys(request_id) - raw = PluginStore.get(CustomWizard::Subscription.namespace, "#{keys_db_key}_#{request_id}") - OpenStruct.new( - user_id: raw && raw['user_id'], - pem: raw && raw['pem'], - nonce: raw && raw['nonce'] - ) - end - - private - - def keys_db_key - "keys" - end - - def client_id_db_key - "client_id" - end - - def set_keys(request_id, user_id, rsa, nonce) - PluginStore.set(CustomWizard::Subscription.namespace, "#{keys_db_key}_#{request_id}", - user_id: user_id, - pem: rsa.export, - nonce: nonce - ) - end - - def delete_keys(request_id) - PluginStore.remove(CustomWizard::Subscription.namespace, "#{keys_db_key}_#{request_id}") - end - - def get_client_id - PluginStore.get(CustomWizard::Subscription.namespace, client_id_db_key) - end - - def set_client_id - client_id = SecureRandom.hex(32) - PluginStore.set(CustomWizard::Subscription.namespace, client_id_db_key, client_id) - client_id - end -end diff --git a/lib/custom_wizard/subscription/subscription.rb b/lib/custom_wizard/subscription/subscription.rb deleted file mode 100644 index 0b3bb84d..00000000 --- a/lib/custom_wizard/subscription/subscription.rb +++ /dev/null @@ -1,22 +0,0 @@ -# frozen_string_literal: true -class CustomWizard::Subscription::Subscription - include ActiveModel::Serialization - - attr_reader :type, - :updated_at - - def initialize(subscription) - if subscription - @type = subscription.type - @updated_at = subscription.updated_at - end - end - - def types - %w(none standard business) - end - - def active? - types.include?(type) && updated_at.to_datetime > (Time.zone.now - 2.hours).to_datetime - end -end diff --git a/plugin.rb b/plugin.rb index e745c3a8..03862103 100644 --- a/plugin.rb +++ b/plugin.rb @@ -70,15 +70,11 @@ after_initialize do ../app/controllers/custom_wizard/admin/logs.rb ../app/controllers/custom_wizard/admin/manager.rb ../app/controllers/custom_wizard/admin/custom_fields.rb - ../app/controllers/custom_wizard/admin/subscription.rb - ../app/controllers/custom_wizard/admin/notice.rb ../app/controllers/custom_wizard/wizard.rb ../app/controllers/custom_wizard/steps.rb ../app/controllers/custom_wizard/realtime_validations.rb ../app/jobs/regular/refresh_api_access_token.rb ../app/jobs/regular/set_after_time_wizard.rb - ../app/jobs/scheduled/custom_wizard/update_subscription.rb - ../app/jobs/scheduled/custom_wizard/update_notices.rb ../lib/custom_wizard/validators/template.rb ../lib/custom_wizard/validators/update.rb ../lib/custom_wizard/action_result.rb @@ -97,11 +93,6 @@ after_initialize do ../lib/custom_wizard/submission.rb ../lib/custom_wizard/template.rb ../lib/custom_wizard/wizard.rb - ../lib/custom_wizard/notice.rb - ../lib/custom_wizard/notice/connection_error.rb - ../lib/custom_wizard/subscription.rb - ../lib/custom_wizard/subscription/subscription.rb - ../lib/custom_wizard/subscription/authentication.rb ../lib/custom_wizard/api/api.rb ../lib/custom_wizard/api/authorization.rb ../lib/custom_wizard/api/endpoint.rb @@ -122,10 +113,6 @@ after_initialize do ../app/serializers/custom_wizard/log_serializer.rb ../app/serializers/custom_wizard/submission_serializer.rb ../app/serializers/custom_wizard/realtime_validation/similar_topics_serializer.rb - ../app/serializers/custom_wizard/subscription/authentication_serializer.rb - ../app/serializers/custom_wizard/subscription/subscription_serializer.rb - ../app/serializers/custom_wizard/subscription_serializer.rb - ../app/serializers/custom_wizard/notice_serializer.rb ../lib/custom_wizard/extensions/extra_locales_controller.rb ../lib/custom_wizard/extensions/invites_controller.rb ../lib/custom_wizard/extensions/users_controller.rb @@ -271,14 +258,6 @@ after_initialize do "#{serializer_klass}_serializer".classify.constantize.prepend CustomWizardCustomFieldSerializer end - AdminDashboardData.add_problem_check do - warning_notices = CustomWizard::Notice.list( - type: CustomWizard::Notice.types[:warning], - archetype: CustomWizard::Notice.archetypes[:plugin_status] - ) - warning_notices.any? ? ActionView::Base.full_sanitizer.sanitize(warning_notices.first.message, tags: %w(a)) : nil - end - reloadable_patch do |plugin| ::TagsController.prepend CustomWizardTagsController ::DiscourseTagging.singleton_class.prepend CustomWizardDiscourseTagging From f607863510f0cb58d961192409244151df119120 Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Fri, 25 Mar 2022 12:18:54 +0100 Subject: [PATCH 116/160] Remove more subscription related things and integrate with subscription client --- app/controllers/custom_wizard/admin/admin.rb | 6 +- .../custom_wizard/admin/custom_fields.rb | 4 +- app/controllers/custom_wizard/admin/wizard.rb | 4 +- .../components/custom-field-input.js.es6 | 33 --- .../components/wizard-custom-action.js.es6 | 26 +- .../wizard-subscription-badge.js.es6 | 30 ++ .../wizard-subscription-container.js.es6 | 26 ++ .../wizard-subscription-selector.js.es6 | 49 ++- .../custom-wizard-category-settings.hbs | 12 +- .../admin-wizards-wizard-show.js.es6 | 1 + .../controllers/admin-wizards.js.es6 | 27 +- .../custom-wizard-admin-route-map.js.es6 | 10 - .../discourse/lib/wizard-schema.js.es6 | 90 +----- .../discourse/lib/wizard-subscription.js.es6 | 17 ++ .../discourse/mixins/subscription.js.es6 | 26 ++ .../routes/admin-wizards-custom-fields.js.es6 | 6 +- .../routes/admin-wizards-wizard-show.js.es6 | 4 +- .../discourse/routes/admin-wizards.js.es6 | 18 +- .../templates/admin-wizards-custom-fields.hbs | 4 +- .../templates/admin-wizards-wizard-show.hbs | 8 +- .../discourse/templates/admin-wizards.hbs | 3 +- .../components/custom-field-input.hbs | 24 +- .../components/wizard-custom-action.hbs | 3 +- .../components/wizard-custom-field.hbs | 9 +- .../components/wizard-custom-step.hbs | 8 +- .../components/wizard-subscription-badge.hbs | 6 + ....hbs => wizard-subscription-container.hbs} | 4 +- .../wizard-subscription-selector-header.hbs | 6 +- .../wizard-subscription-selector-row.hbs | 4 +- assets/stylesheets/admin/admin.scss | 279 +++++------------- .../stylesheets/admin/wizard/variables.scss | 5 + config/locales/client.en.yml | 89 ++---- config/settings.yml | 2 +- lib/custom_wizard/custom_field.rb | 4 +- lib/custom_wizard/mapper.rb | 3 +- lib/custom_wizard/subscription.rb | 164 +++++++++- lib/custom_wizard/validators/template.rb | 43 +-- plugin.rb | 25 +- spec/components/custom_wizard/notice_spec.rb | 159 ---------- .../custom_wizard/subscription_spec.rb | 125 -------- spec/jobs/update_notices_spec.rb | 29 -- spec/jobs/update_subscription_spec.rb | 11 - spec/plugin_helper.rb | 35 --- .../admin/notice_controller_spec.rb | 72 ----- .../admin/subscription_controller_spec.rb | 71 ----- .../custom_wizard/notice_serializer_spec.rb | 22 -- .../authentication_serializer_spec.rb | 17 -- .../subscription_serializer_spec.rb | 14 - .../subscription_serializer_spec.rb | 14 - 49 files changed, 522 insertions(+), 1129 deletions(-) create mode 100644 assets/javascripts/discourse/components/wizard-subscription-badge.js.es6 create mode 100644 assets/javascripts/discourse/components/wizard-subscription-container.js.es6 create mode 100644 assets/javascripts/discourse/lib/wizard-subscription.js.es6 create mode 100644 assets/javascripts/discourse/mixins/subscription.js.es6 create mode 100644 assets/javascripts/discourse/templates/components/wizard-subscription-badge.hbs rename assets/javascripts/discourse/templates/components/{subscription-container.hbs => wizard-subscription-container.hbs} (54%) delete mode 100644 spec/components/custom_wizard/notice_spec.rb delete mode 100644 spec/components/custom_wizard/subscription_spec.rb delete mode 100644 spec/jobs/update_notices_spec.rb delete mode 100644 spec/jobs/update_subscription_spec.rb delete mode 100644 spec/requests/custom_wizard/admin/notice_controller_spec.rb delete mode 100644 spec/requests/custom_wizard/admin/subscription_controller_spec.rb delete mode 100644 spec/serializers/custom_wizard/notice_serializer_spec.rb delete mode 100644 spec/serializers/custom_wizard/subscription/authentication_serializer_spec.rb delete mode 100644 spec/serializers/custom_wizard/subscription/subscription_serializer_spec.rb delete mode 100644 spec/serializers/custom_wizard/subscription_serializer_spec.rb diff --git a/app/controllers/custom_wizard/admin/admin.rb b/app/controllers/custom_wizard/admin/admin.rb index e011e094..867be56c 100644 --- a/app/controllers/custom_wizard/admin/admin.rb +++ b/app/controllers/custom_wizard/admin/admin.rb @@ -3,8 +3,12 @@ class CustomWizard::AdminController < ::Admin::AdminController before_action :ensure_admin def index + subcription = CustomWizard::Subscription.new render_json_dump( - api_section: ["business"].include?(CustomWizard::Subscription.type) + subscribed: subcription.subscribed?, + subscription_type: subcription.type, + subscription_attributes: CustomWizard::Subscription.attributes, + subscription_client_installed: subcription.client_installed? ) end diff --git a/app/controllers/custom_wizard/admin/custom_fields.rb b/app/controllers/custom_wizard/admin/custom_fields.rb index 1cd20f5d..111e9faf 100644 --- a/app/controllers/custom_wizard/admin/custom_fields.rb +++ b/app/controllers/custom_wizard/admin/custom_fields.rb @@ -2,9 +2,7 @@ class CustomWizard::AdminCustomFieldsController < CustomWizard::AdminController def index render_json_dump( - custom_fields: custom_field_list, - subscribed: CustomWizard::Subscription.subscribed?, - subscription: CustomWizard::Subscription.type + custom_fields: custom_field_list ) end diff --git a/app/controllers/custom_wizard/admin/wizard.rb b/app/controllers/custom_wizard/admin/wizard.rb index ad63209a..0a59e02b 100644 --- a/app/controllers/custom_wizard/admin/wizard.rb +++ b/app/controllers/custom_wizard/admin/wizard.rb @@ -10,9 +10,7 @@ class CustomWizard::AdminWizardController < CustomWizard::AdminController ), field_types: CustomWizard::Field.types, realtime_validations: CustomWizard::RealtimeValidation.types, - custom_fields: custom_field_list, - subscribed: CustomWizard::Subscription.subscribed?, - subscription: CustomWizard::Subscription.type + custom_fields: custom_field_list ) end diff --git a/assets/javascripts/discourse/components/custom-field-input.js.es6 b/assets/javascripts/discourse/components/custom-field-input.js.es6 index 7ee70716..5d2d6c3b 100644 --- a/assets/javascripts/discourse/components/custom-field-input.js.es6 +++ b/assets/javascripts/discourse/components/custom-field-input.js.es6 @@ -3,29 +3,6 @@ import discourseComputed, { observes } from "discourse-common/utils/decorators"; import { alias, equal, or } from "@ember/object/computed"; import I18n from "I18n"; -import wizardSchema, { - requiringAdditionalSubscription, - subscriptionLevel, -} from "discourse/plugins/discourse-custom-wizard/discourse/lib/wizard-schema"; - -const generateContent = function (kategory, subscription) { - let unsubscribedCustomFields = requiringAdditionalSubscription( - subscription, - "custom_fields", - kategory - ); - return wizardSchema.custom_field[kategory].reduce((result, item) => { - let disabled = unsubscribedCustomFields.includes(item); - result.push({ - id: item, - name: I18n.t(`admin.wizard.custom_field.${kategory}.${item}`), - subscription: subscriptionLevel(item, "custom_fields", kategory), - disabled, - }); - return result; - }, []); -}; - export default Component.extend({ tagName: "tr", topicSerializers: ["topic_view", "topic_list_item"], @@ -58,16 +35,6 @@ export default Component.extend({ } }, - @discourseComputed("subscription") - customFieldTypes(subscription) { - return generateContent("type", subscription); - }, - - @discourseComputed("subscription") - customFieldKlasses(subscription) { - return generateContent("klass", subscription); - }, - @observes("field.klass") clearSerializersWhenClassChanges() { this.set("field.serializers", null); diff --git a/assets/javascripts/discourse/components/wizard-custom-action.js.es6 b/assets/javascripts/discourse/components/wizard-custom-action.js.es6 index 6cf22942..a0a9cc72 100644 --- a/assets/javascripts/discourse/components/wizard-custom-action.js.es6 +++ b/assets/javascripts/discourse/components/wizard-custom-action.js.es6 @@ -1,8 +1,6 @@ import { default as discourseComputed } from "discourse-common/utils/decorators"; -import wizardSchema, { - requiringAdditionalSubscription, - subscriptionLevel, -} from "discourse/plugins/discourse-custom-wizard/discourse/lib/wizard-schema"; +import wizardSchema from "discourse/plugins/discourse-custom-wizard/discourse/lib/wizard-schema"; +import { subscriptionSelectKitContent } from "discourse/plugins/discourse-custom-wizard/discourse/lib/wizard-subscription"; import { empty, equal, or } from "@ember/object/computed"; import { notificationLevels, selectKitContent } from "../lib/wizard"; import { computed } from "@ember/object"; @@ -97,22 +95,8 @@ export default Component.extend(UndoChanges, { return apis.find((a) => a.name === api).endpoints; }, - @discourseComputed("subscription") - actionTypes(subscription) { - let unsubscribedActions = requiringAdditionalSubscription( - subscription, - "actions", - "" - ); - return Object.keys(wizardSchema.action.types).reduce((result, type) => { - let disabled = unsubscribedActions.includes(type); - result.push({ - id: type, - name: I18n.t(`admin.wizard.action.${type}.label`), - subscription: subscriptionLevel(type, "actions", ""), - disabled, - }); - return result; - }, []); + @discourseComputed + actionTypes() { + return subscriptionSelectKitContent("action", "types"); }, }); diff --git a/assets/javascripts/discourse/components/wizard-subscription-badge.js.es6 b/assets/javascripts/discourse/components/wizard-subscription-badge.js.es6 new file mode 100644 index 00000000..7a63f4d3 --- /dev/null +++ b/assets/javascripts/discourse/components/wizard-subscription-badge.js.es6 @@ -0,0 +1,30 @@ +import Component from "@ember/component"; +import discourseComputed from "discourse-common/utils/decorators"; +import Subscription from "../mixins/subscription"; +import DiscourseURL from "discourse/lib/url"; +import I18n from "I18n"; + +export default Component.extend(Subscription, { + tagName: 'a', + classNameBindings: [":wizard-subscription-badge", "subscriptionType"], + attributeBindings: ["title"], + + @discourseComputed("subscriptionType") + i18nKey(type) { + return `admin.wizard.subscription_container.type.${type ? type : "none"}`; + }, + + @discourseComputed("i18nKey") + title(i18nKey) { + return I18n.t(`${i18nKey}.title`); + }, + + @discourseComputed("i18nKey") + label(i18nKey) { + return I18n.t(`${i18nKey}.label`); + }, + + click() { + DiscourseURL.routeTo(this.subscriptionLink); + } +}); diff --git a/assets/javascripts/discourse/components/wizard-subscription-container.js.es6 b/assets/javascripts/discourse/components/wizard-subscription-container.js.es6 new file mode 100644 index 00000000..3569276c --- /dev/null +++ b/assets/javascripts/discourse/components/wizard-subscription-container.js.es6 @@ -0,0 +1,26 @@ +import Component from "@ember/component"; +import discourseComputed from "discourse-common/utils/decorators"; +import Subscription from "../mixins/subscription"; + +export default Component.extend(Subscription, { + classNameBindings: [":wizard-subscription-container", "subscribed"], + + @discourseComputed("subscribed") + subscribedIcon(subscribed) { + return subscribed ? "check" : "dash"; + }, + + @discourseComputed("subscribed") + subscribedLabel(subscribed) { + return `admin.wizard.subscription_container.${ + subscribed ? "subscribed" : "not_subscribed" + }.label`; + }, + + @discourseComputed("subscribed") + subscribedTitle(subscribed) { + return `admin.wizard.subscription_container.${ + subscribed ? "subscribed" : "not_subscribed" + }.title`; + } +}); diff --git a/assets/javascripts/discourse/components/wizard-subscription-selector.js.es6 b/assets/javascripts/discourse/components/wizard-subscription-selector.js.es6 index ea1aa5d9..59714578 100644 --- a/assets/javascripts/discourse/components/wizard-subscription-selector.js.es6 +++ b/assets/javascripts/discourse/components/wizard-subscription-selector.js.es6 @@ -1,6 +1,21 @@ import SingleSelectComponent from "select-kit/components/single-select"; +import Subscription from "../mixins/subscription"; +import wizardSchema from "discourse/plugins/discourse-custom-wizard/discourse/lib/wizard-schema"; +import { + subscriptionTypeSufficient, + subscriptionTypes +} from "discourse/plugins/discourse-custom-wizard/discourse/lib/wizard-subscription"; +import discourseComputed from "discourse-common/utils/decorators"; -export default SingleSelectComponent.extend({ +const nameKey = function(feature, attribute, value) { + if (feature === 'action') { + return `admin.wizard.action.${value}.label`; + } else { + return `admin.wizard.${feature}.${attribute}.${value}`; + } +} + +export default SingleSelectComponent.extend(Subscription, { classNames: ["combo-box", "wizard-subscription-selector"], selectKitOptions: { @@ -13,6 +28,38 @@ export default SingleSelectComponent.extend({ caretDownIcon: "caret-down", }, + requiredSubscriptionType(feature, attribute, value) { + let attributes = this.subscriptionAttributes[feature]; + if (!attributes || !attributes[attribute]) return null; + + let requiredType = null; + Object.keys(attributes[attribute]).some(subscriptionType => { + let values = attributes[attribute][subscriptionType]; + if (values[0] === "*" || values.includes(value)) { + if (subscriptionTypes.includes(subscriptionType)) { + requiredType = subscriptionType; + } + return true; + } + return false; + }); + + return requiredType; + }, + + @discourseComputed('feature', 'attribute') + content(feature, attribute) { + return wizardSchema[feature][attribute].map((value) => { + let requiredSubscriptionType = this.requiredSubscriptionType(feature, attribute, value); + return { + id: value, + name: I18n.t(nameKey(feature, attribute, value)), + subscriptionType: requiredSubscriptionType, + disabled: !subscriptionTypeSufficient(this.subscriptionType, requiredSubscriptionType) + }; + }); + }, + modifyComponentForRow() { return "wizard-subscription-selector/wizard-subscription-selector-row"; }, diff --git a/assets/javascripts/discourse/connectors/category-custom-settings/custom-wizard-category-settings.hbs b/assets/javascripts/discourse/connectors/category-custom-settings/custom-wizard-category-settings.hbs index 4b5d673d..5ce96f2f 100644 --- a/assets/javascripts/discourse/connectors/category-custom-settings/custom-wizard-category-settings.hbs +++ b/assets/javascripts/discourse/connectors/category-custom-settings/custom-wizard-category-settings.hbs @@ -6,11 +6,11 @@
{{combo-box - value=wizardListVal - content=wizardList - onChange=(action "changeWizard") - options=(hash - none="admin.wizard.select" - )}} + value=wizardListVal + content=wizardList + onChange=(action "changeWizard") + options=(hash + none="admin.wizard.select" + )}}
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..2ecc42da 100644 --- a/assets/javascripts/discourse/controllers/admin-wizards-wizard-show.js.es6 +++ b/assets/javascripts/discourse/controllers/admin-wizards-wizard-show.js.es6 @@ -14,6 +14,7 @@ import I18n from "I18n"; export default Controller.extend({ hasName: notEmpty("wizard.name"), + @observes("currentStep") resetCurrentObjects() { const currentStep = this.currentStep; diff --git a/assets/javascripts/discourse/controllers/admin-wizards.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards.js.es6 index 33841460..b69b878a 100644 --- a/assets/javascripts/discourse/controllers/admin-wizards.js.es6 +++ b/assets/javascripts/discourse/controllers/admin-wizards.js.es6 @@ -1,26 +1,7 @@ -import Controller, { inject as controller } from "@ember/controller"; -import { isPresent } from "@ember/utils"; -import { A } from "@ember/array"; +import Controller from "@ember/controller"; +import { equal } from "@ember/object/computed"; export default Controller.extend({ - adminWizardsNotices: controller(), - - unsubscribe() { - this.messageBus.unsubscribe("/custom-wizard/notices"); - }, - - subscribe() { - this.unsubscribe(); - this.messageBus.subscribe("/custom-wizard/notices", (data) => { - if (isPresent(data.active_notice_count)) { - this.set("activeNoticeCount", data.active_notice_count); - this.adminWizardsNotices.setProperties({ - notices: A(), - page: 0, - canLoadMore: true, - }); - this.adminWizardsNotices.loadMoreNotices(); - } - }); - }, + businessSubscription: equal('subscriptionType', 'business'), + standardSubscription: equal('subscriptionType', 'standard') }); diff --git a/assets/javascripts/discourse/custom-wizard-admin-route-map.js.es6 b/assets/javascripts/discourse/custom-wizard-admin-route-map.js.es6 index c3c95e48..272e276e 100644 --- a/assets/javascripts/discourse/custom-wizard-admin-route-map.js.es6 +++ b/assets/javascripts/discourse/custom-wizard-admin-route-map.js.es6 @@ -58,16 +58,6 @@ export default { path: "/manager", resetNamespace: true, }); - - this.route("adminWizardsSubscription", { - path: "/subscription", - resetNamespace: true, - }); - - this.route("adminWizardsNotices", { - path: "/notices", - resetNamespace: true, - }); } ); }, diff --git a/assets/javascripts/discourse/lib/wizard-schema.js.es6 b/assets/javascripts/discourse/lib/wizard-schema.js.es6 index 26c21bd2..ca5c30ee 100644 --- a/assets/javascripts/discourse/lib/wizard-schema.js.es6 +++ b/assets/javascripts/discourse/lib/wizard-schema.js.es6 @@ -203,101 +203,17 @@ const custom_field = { type: ["string", "boolean", "integer", "json"], }; -const subscription_levels = { - standard: { - actions: ["send_message", "add_to_group", "watch_categories"], - custom_fields: { - klass: [], - type: ["json"], - }, - }, - - business: { - actions: ["create_category", "create_group", "send_to_api"], - custom_fields: { - klass: ["group", "category"], - type: [], - }, - }, -}; +field.type = Object.keys(field.types); +action.type = Object.keys(action.types); const wizardSchema = { wizard, step, field, custom_field, - action, - subscription_levels, + action }; -export function requiringAdditionalSubscription( - currentSubscription, - category, - subCategory -) { - switch (category) { - case "actions": - switch (currentSubscription) { - case "business": - return []; - case "standard": - return subscription_levels["business"][category]; - default: - return subscription_levels["standard"][category].concat( - subscription_levels["business"][category] - ); - } - case "custom_fields": - switch (currentSubscription) { - case "business": - return []; - case "standard": - return subscription_levels["business"][category][subCategory]; - default: - return subscription_levels["standard"][category][subCategory].concat( - subscription_levels["business"][category][subCategory] - ); - } - default: - return []; - } -} - -export function subscriptionLevel(type, category, subCategory) { - switch (category) { - case "actions": - if (subscription_levels["business"].actions.includes(type)) { - return "business"; - } else { - if (subscription_levels["standard"].actions.includes(type)) { - return "standard"; - } else { - return ""; - } - } - case "custom_fields": - if ( - subscription_levels["business"].custom_fields[subCategory].includes( - type - ) - ) { - return "business"; - } else { - if ( - subscription_levels["standard"].custom_fields[subCategory].includes( - type - ) - ) { - return "standard"; - } else { - return ""; - } - } - default: - return ""; - } -} - export function buildFieldTypes(types) { wizardSchema.field.types = types; } diff --git a/assets/javascripts/discourse/lib/wizard-subscription.js.es6 b/assets/javascripts/discourse/lib/wizard-subscription.js.es6 new file mode 100644 index 00000000..deff32ca --- /dev/null +++ b/assets/javascripts/discourse/lib/wizard-subscription.js.es6 @@ -0,0 +1,17 @@ +const subscriptionTypes = [ + 'standard', + 'business' +] + +function subscriptionTypeSufficient(subscriptionType, requiredType) { + if (requiredType && !subscriptionType) return false; + if (requiredType === 'none' || requiredType === null) return true; + if (requiredType === 'standard' && subscriptionTypes.includes(subscriptionType)) return true; + if (requiredType === 'business' && subscriptionType === 'business') return true; + return false; +} + +export { + subscriptionTypeSufficient, + subscriptionTypes +} diff --git a/assets/javascripts/discourse/mixins/subscription.js.es6 b/assets/javascripts/discourse/mixins/subscription.js.es6 new file mode 100644 index 00000000..00f77bb2 --- /dev/null +++ b/assets/javascripts/discourse/mixins/subscription.js.es6 @@ -0,0 +1,26 @@ +import Mixin from "@ember/object/mixin"; +import { getOwner } from "discourse-common/lib/get-owner"; +import { readOnly } from "@ember/object/computed"; +import discourseComputed from "discourse-common/utils/decorators"; + +export default Mixin.create({ + subscriptionLandingUrl: "https://custom-wizard.pavilion.tech", + subscriptionClientUrl: "/admin/plugins/subscription-client", + + @discourseComputed + adminWizards() { + return getOwner(this).lookup('controller:admin-wizards'); + }, + + subscribed: readOnly('adminWizards.subscribed'), + subscriptionType: readOnly('adminWizards.subscriptionType'), + businessSubscription: readOnly('adminWizards.businessSubscription'), + standardSubscription: readOnly('adminWizards.standardSubscription'), + subscriptionAttributes: readOnly('adminWizards.subscriptionAttributes'), + subscriptionClientInstalled: readOnly('adminWizards.subscriptionClientInstalled'), + + @discourseComputed("subscriptionClientInstalled") + subscriptionLink(subscriptionClientInstalled) { + return subscriptionClientInstalled ? this.subscriptionClientUrl : this.subscriptionLandingUrl; + } +}); diff --git a/assets/javascripts/discourse/routes/admin-wizards-custom-fields.js.es6 b/assets/javascripts/discourse/routes/admin-wizards-custom-fields.js.es6 index 4992f92d..4d9de2c4 100644 --- a/assets/javascripts/discourse/routes/admin-wizards-custom-fields.js.es6 +++ b/assets/javascripts/discourse/routes/admin-wizards-custom-fields.js.es6 @@ -9,13 +9,9 @@ export default DiscourseRoute.extend({ setupController(controller, model) { const customFields = A(model.custom_fields || []); - const subscribed = model.subscribed; - const subscription = model.subscription; controller.setProperties({ - customFields, - subscribed, - subscription, + customFields }); }, }); diff --git a/assets/javascripts/discourse/routes/admin-wizards-wizard-show.js.es6 b/assets/javascripts/discourse/routes/admin-wizards-wizard-show.js.es6 index d6263471..63ff564e 100644 --- a/assets/javascripts/discourse/routes/admin-wizards-wizard-show.js.es6 +++ b/assets/javascripts/discourse/routes/admin-wizards-wizard-show.js.es6 @@ -38,9 +38,7 @@ export default DiscourseRoute.extend({ wizard, currentStep: wizard.steps[0], currentAction: wizard.actions[0], - creating: model.create, - subscribed: parentModel.subscribed, - subscription: parentModel.subscription, + creating: model.create }; controller.setProperties(props); diff --git a/assets/javascripts/discourse/routes/admin-wizards.js.es6 b/assets/javascripts/discourse/routes/admin-wizards.js.es6 index 5c39c0d6..0286926d 100644 --- a/assets/javascripts/discourse/routes/admin-wizards.js.es6 +++ b/assets/javascripts/discourse/routes/admin-wizards.js.es6 @@ -7,21 +7,17 @@ export default DiscourseRoute.extend({ }, setupController(controller, model) { - controller.set("api_section", model.api_section); - - if (model.active_notice_count) { - controller.set("activeNoticeCount", model.active_notice_count); - } - if (model.featured_notices) { - controller.set("featuredNotices", model.featured_notices); - } - - controller.subscribe(); + controller.setProperties({ + subscribed: model.subscribed, + subscriptionType: model.subscription_type, + subscriptionAttributes: model.subscription_attributes, + subscriptionClientInstalled: model.subscription_client_installed + }); }, afterModel(model, transition) { if (transition.targetName === "adminWizards.index") { this.transitionTo("adminWizardsWizard"); } - }, + } }); diff --git a/assets/javascripts/discourse/templates/admin-wizards-custom-fields.hbs b/assets/javascripts/discourse/templates/admin-wizards-custom-fields.hbs index dceed458..10501498 100644 --- a/assets/javascripts/discourse/templates/admin-wizards-custom-fields.hbs +++ b/assets/javascripts/discourse/templates/admin-wizards-custom-fields.hbs @@ -32,9 +32,7 @@ {{custom-field-input field=field removeField=(action "removeField") - saveField=(action "saveField") - subscribed=subscribed - subscription=subscription}} + saveField=(action "saveField")}} {{/each}} diff --git a/assets/javascripts/discourse/templates/admin-wizards-wizard-show.hbs b/assets/javascripts/discourse/templates/admin-wizards-wizard-show.hbs index abf06ba9..5d1c2166 100644 --- a/assets/javascripts/discourse/templates/admin-wizards-wizard-show.hbs +++ b/assets/javascripts/discourse/templates/admin-wizards-wizard-show.hbs @@ -126,7 +126,7 @@
- {{#subscription-container subscribed=subscribed}} + {{#wizard-subscription-container}}
@@ -146,7 +146,7 @@ {{i18n "admin.wizard.restart_on_revisit_label"}}
- {{/subscription-container}} + {{/wizard-subscription-container}}
{{wizard-links @@ -177,9 +177,7 @@ wizard=wizard apis=apis removeAction="removeAction" - wizardFields=wizardFields - subscribed=subscribed - subscription=subscription}} + wizardFields=wizardFields}} {{/each}}
diff --git a/assets/javascripts/discourse/templates/admin-wizards.hbs b/assets/javascripts/discourse/templates/admin-wizards.hbs index a50748cc..26ba3e06 100644 --- a/assets/javascripts/discourse/templates/admin-wizards.hbs +++ b/assets/javascripts/discourse/templates/admin-wizards.hbs @@ -2,13 +2,14 @@ {{nav-item route="adminWizardsWizard" label="admin.wizard.nav_label"}} {{nav-item route="adminWizardsCustomFields" label="admin.wizard.custom_field.nav_label"}} {{nav-item route="adminWizardsSubmissions" label="admin.wizard.submissions.nav_label"}} - {{#if api_section}} + {{#if businessSubscription}} {{nav-item route="adminWizardsApi" label="admin.wizard.api.nav_label"}} {{/if}} {{nav-item route="adminWizardsLogs" label="admin.wizard.log.nav_label"}} {{nav-item route="adminWizardsManager" label="admin.wizard.manager.nav_label"}}
+ {{wizard-subscription-badge}} {{d-icon "far-life-ring"}}{{i18n "admin.wizard.support_button.label"}} diff --git a/assets/javascripts/discourse/templates/components/custom-field-input.hbs b/assets/javascripts/discourse/templates/components/custom-field-input.hbs index b4ddcc81..335a9b9d 100644 --- a/assets/javascripts/discourse/templates/components/custom-field-input.hbs +++ b/assets/javascripts/discourse/templates/components/custom-field-input.hbs @@ -2,16 +2,22 @@ {{wizard-subscription-selector value=field.klass - content=customFieldKlasses - none="admin.wizard.custom_field.klass.select" - onChange=(action (mut field.klass))}} + feature="custom_field" + attribute="klass" + onChange=(action (mut field.klass)) + options=(hash + none="admin.wizard.custom_field.klass.select" + )}} {{wizard-subscription-selector value=field.type - content=customFieldTypes - none="admin.wizard.custom_field.type.select" - onChange=(action (mut field.type))}} + feature="custom_field" + attribute="type" + onChange=(action (mut field.type)) + options=(hash + none="admin.wizard.custom_field.type.select" + )}} {{input @@ -22,8 +28,10 @@ {{multi-select value=field.serializers content=serializerContent - none="admin.wizard.custom_field.serializers.select" - onChange=(action (mut field.serializers))}} + onChange=(action (mut field.serializers)) + options=(hash + none="admin.wizard.custom_field.serializers.select" + )}} {{#if loading}} diff --git a/assets/javascripts/discourse/templates/components/wizard-custom-action.hbs b/assets/javascripts/discourse/templates/components/wizard-custom-action.hbs index cb4cf28d..818c525d 100644 --- a/assets/javascripts/discourse/templates/components/wizard-custom-action.hbs +++ b/assets/javascripts/discourse/templates/components/wizard-custom-action.hbs @@ -14,7 +14,8 @@
{{wizard-subscription-selector value=action.type - content=actionTypes + feature="action" + attribute="type" onChange=(action "changeType") options=(hash none="admin.wizard.select_type" diff --git a/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs b/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs index b4f1ac3a..00c20153 100644 --- a/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs +++ b/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs @@ -222,7 +222,7 @@
{{/if}} -{{#subscription-container subscribed=subscribed}} +{{#wizard-subscription-container}}
@@ -237,7 +237,7 @@
- > +
@@ -248,10 +248,9 @@
{{#if isCategory}} -
+
- {{i18n "admin.wizard.pro.label"}}
@@ -269,4 +268,4 @@ {{#if validations}} {{wizard-realtime-validations field=field validations=validations}} {{/if}} -{{/subscription-container}} +{{/wizard-subscription-container}} diff --git a/assets/javascripts/discourse/templates/components/wizard-custom-step.hbs b/assets/javascripts/discourse/templates/components/wizard-custom-step.hbs index 0d1294f3..61812d04 100644 --- a/assets/javascripts/discourse/templates/components/wizard-custom-step.hbs +++ b/assets/javascripts/discourse/templates/components/wizard-custom-step.hbs @@ -34,7 +34,7 @@
-{{#subscription-container subscribed=subscribed}} +{{#wizard-subscription-container}}
@@ -56,11 +56,11 @@
-
+
- {{i18n "admin.wizard.pro.label"}}
+
{{wizard-mapper inputs=step.required_data @@ -99,7 +99,7 @@ )}}
-{{/subscription-container}} +{{/wizard-subscription-container}} {{wizard-links itemType="field" diff --git a/assets/javascripts/discourse/templates/components/wizard-subscription-badge.hbs b/assets/javascripts/discourse/templates/components/wizard-subscription-badge.hbs new file mode 100644 index 00000000..b2ce05bc5 --- /dev/null +++ b/assets/javascripts/discourse/templates/components/wizard-subscription-badge.hbs @@ -0,0 +1,6 @@ + + + +{{label}} diff --git a/assets/javascripts/discourse/templates/components/subscription-container.hbs b/assets/javascripts/discourse/templates/components/wizard-subscription-container.hbs similarity index 54% rename from assets/javascripts/discourse/templates/components/subscription-container.hbs rename to assets/javascripts/discourse/templates/components/wizard-subscription-container.hbs index 8b012671..78be031f 100644 --- a/assets/javascripts/discourse/templates/components/subscription-container.hbs +++ b/assets/javascripts/discourse/templates/components/wizard-subscription-container.hbs @@ -1,7 +1,7 @@
-

{{i18n "admin.wizard.subscription_container.title"}}

+

{{i18n "admin.wizard.subscription_container.title"}}

- + {{d-icon subscribedIcon}} {{i18n subscribedLabel}} diff --git a/assets/javascripts/discourse/templates/components/wizard-subscription-selector/wizard-subscription-selector-header.hbs b/assets/javascripts/discourse/templates/components/wizard-subscription-selector/wizard-subscription-selector-header.hbs index a7e3d2e6..708b0887 100644 --- a/assets/javascripts/discourse/templates/components/wizard-subscription-selector/wizard-subscription-selector-header.hbs +++ b/assets/javascripts/discourse/templates/components/wizard-subscription-selector/wizard-subscription-selector-header.hbs @@ -7,8 +7,10 @@ shouldDisplayClearableButton=shouldDisplayClearableButton }} - {{#if selectedContent.subscription}} - {{i18n "admin.wizard.subscription.label"}} + {{#if selectedContent.subscriptionType}} + + {{selectedContent.subscriptionType}} + {{/if}} {{d-icon caretIcon class="caret-icon"}} diff --git a/assets/javascripts/discourse/templates/components/wizard-subscription-selector/wizard-subscription-selector-row.hbs b/assets/javascripts/discourse/templates/components/wizard-subscription-selector/wizard-subscription-selector-row.hbs index ecd77cb1..1db906e6 100644 --- a/assets/javascripts/discourse/templates/components/wizard-subscription-selector/wizard-subscription-selector-row.hbs +++ b/assets/javascripts/discourse/templates/components/wizard-subscription-selector/wizard-subscription-selector-row.hbs @@ -9,9 +9,9 @@
{{html-safe label}} - {{#if item.subscription}} + {{#if item.subscriptionType}} - {{item.subscription}} + {{item.subscriptionType}} {{/if}}
diff --git a/assets/stylesheets/admin/admin.scss b/assets/stylesheets/admin/admin.scss index c026263a..b8458163 100644 --- a/assets/stylesheets/admin/admin.scss +++ b/assets/stylesheets/admin/admin.scss @@ -25,6 +25,14 @@ $error: #ef1700; } } +.admin-wizards .admin-actions { + display: flex; + + .btn-pavilion-support { + margin-left: 10px; + } +} + .wizard-message { background-color: var(--primary-low); width: 100%; @@ -156,6 +164,16 @@ $error: #ef1700; @extend .wizard-settings-group; } +.admin-wizard-container.settings { + .wizard-settings { + .wizard-subscription-container { + [class~="setting"] { + margin-bottom: 0; + } + } + } +} + .wizard-custom-field { background: transparent; background-color: var(--primary-very-low); @@ -802,84 +820,6 @@ $error: #ef1700; vertical-align: middle; } -.admin-wizards-subscription { - .admin-wizard-controls { - h3, - label { - margin: 0; - } - - label { - padding: 0.4em 0.5em; - margin-left: 0.75em; - background-color: var(--success); - color: var(--secondary); - } - - .buttons { - display: flex; - align-items: center; - - .loading-container { - margin-right: 1em; - } - } - } - - .custom-wizard-subscription { - .title-container { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 0.5em; - - h3 { - margin: 0; - } - - .buttons > span { - margin-right: 0.5em; - } - } - - .detail-container { - display: flex; - align-items: center; - padding: 1em; - background-color: var(--primary-very-low); - - .subscription-state { - padding: 0.25em 0.5em; - margin-right: 0.75em; - - &.active { - background-color: var(--success); - color: var(--secondary); - } - } - } - } -} - -.wizard-subscription-selector.select-kit.single-select { - .select-kit-row { - .texts { - display: flex; - align-items: center; - } - &.disabled { - background: var(--primary-low); - } - } - - .subscription-label { - margin-left: 0.75em; - padding-top: 0.25em; - color: var(--tertiary); - font-size: 0.75em; - } -} - .btn.btn-pavilion-support { background: var(--pavilion-primary); color: var(--pavilion-secondary); @@ -899,7 +839,7 @@ $error: #ef1700; } } -.subscription-container { +.wizard-subscription-container { width: 100%; padding: 1em; background-color: rgba($pavilion_primary, 0.1); @@ -907,158 +847,79 @@ $error: #ef1700; .subscription-header { display: flex; justify-content: space-between; - margin-bottom: 1em; + margin-bottom: .25em; h3 { margin: 0; } a { - color: var(--pavilion-primary); + color: var(--primary); } } &:not(.subscribed) .subscription-settings { filter: blur(1px); + pointer-events: none; } } -.admin-wizards-notices { - .wizard-table { - overflow: unset; - } -} - -.wizard-notice { - padding: 0.75em; - margin-bottom: 1em; - border: 1px solid var(--primary-low); - - &.dismissed { - display: none; - } - - &.expired .notice-badge:not(.notice-expired-at), - &.expired a, - &.expired p { - color: var(--primary-medium) !important; - } - - .notice-badge { - padding: 0 0.5em; - } - - .notice-header { - display: flex; - - .notice-title { - padding: 0; - } - - .notice-header-right { - margin-left: auto; - display: flex; - align-items: center; - - .notice-badge { - margin-left: 0.5em; - } - } - } - - .dismiss-notice-container, - .hide-notice-container { - width: 40px; - display: flex; - justify-content: center; - align-items: center; - } - - .dismiss-notice, - .hide-notice { - display: flex; - align-items: center; - - .d-icon { - margin-right: 0; - color: var(--primary); - } - } -} - -.notice-badge { +.wizard-subscription-badge { display: inline-flex; align-items: center; - min-height: 25px; + max-height: 34px; box-sizing: border-box; - color: var(--primary) !important; -} - -.admin-actions { - display: flex; - align-items: center; -} - -.wizard-notices-link { position: relative; - margin-right: 10px; + cursor: pointer; + padding: .5em .65em; + background-color: rgba($primary-medium, 0.05); + border: 1.5px solid rgba($primary-medium, 0.5); + color: $primary-medium; - div > a { - @include btn; - color: var(--secondary) !important; - background-color: var(--primary-medium); + &:hover { + color: $primary-medium; + } - &.active { - background-color: var(--tertiary) !important; - color: var(--secondary) !important; + svg { + width: 15px; + height: 15px; + margin-right: 0.45em; + margin-bottom: 0.15em; + } + + &.standard { + background-color: rgba($subscription_standard, 0.05); + border: 1.5px solid rgba($subscription_standard, 0.5); + color: $subscription_standard; + } + + &.business { + background-color: $subscription_business; + border: 1.5px solid $subscription_business; + color: $secondary; + } + + .d-icon { + margin-right: 0.75em; + } +} + +.wizard-subscription-selector.select-kit.single-select { + .select-kit-row { + .texts { + display: flex; + align-items: center; + } + &.disabled { + background: var(--primary-low); + cursor: unset; } } -} -.active-notice-count { - background-color: $danger; - color: $secondary; - border-radius: 50%; - width: 18px; - height: 18px; - line-height: 18px; - text-align: center; - position: absolute; - top: -8px; - right: -8px; - font-size: 0.7em; -} - -a.show-notice-message { - padding: 0.25em 0.5em; - color: var(--primary); -} - -.wizard-notice, -.wizard-notice-row:not(.expired):not(.dismissed) { - &.info { - background-color: rgba($info, 0.1); - border: 1px solid rgba($info, 0.5); - } - &.warning, - &.connection-error { - background-color: rgba($warning, 0.1); - border: 1px solid rgba($warning, 0.5); - } -} - -.notice-message { - position: relative; - - .cooked-notice-message { - background-color: var(--secondary); - padding: 1em; - z-index: 1; - box-shadow: shadow("dropdown"); - border-top: 1px solid var(--primary-low); - - p { - margin: 0; - } + .subscription-label { + margin-left: 0.75em; + padding-top: 0.25em; + color: var(--pavilion-primary); + font-size: 0.75em; } } diff --git a/assets/stylesheets/admin/wizard/variables.scss b/assets/stylesheets/admin/wizard/variables.scss index 5912f961..9bb93798 100644 --- a/assets/stylesheets/admin/wizard/variables.scss +++ b/assets/stylesheets/admin/wizard/variables.scss @@ -2,8 +2,13 @@ $pavilion_primary: #3c1c8c; $pavilion_secondary: #ffffff; $pavilion_warning: rgb(243, 163, 61); +$subscription_standard: $pavilion_primary; +$subscription_business: #333; + :root { --pavilion-primary: #{$pavilion_primary}; --pavilion-secondary: #{$pavilion_secondary}; --pavilion-warning: #{$pavilion_warning}; + --subscription-standard: #{$subscription_standard}; + --subscription-business: #{$subscription_business}; } diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 34c7003b..45efabb8 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -47,7 +47,7 @@ en: value: "Value" profile: "profile" type: "Type" - none: "Make a selection" + none: "Make a selection" submission_key: 'submission key' param_key: 'param' group: "Group" @@ -126,9 +126,9 @@ en: hide: "Hide" preview: "{{action}} Preview" popover: "{{action}} Fields" - + input: - conditional: + conditional: name: 'if' output: 'then' assignment: @@ -137,7 +137,7 @@ en: name: 'map' validation: name: 'ensure' - + selector: label: text: "text" @@ -175,7 +175,7 @@ en: dependent: "{{property}} is dependent on {{dependent}}" conflict: "{{type}} with {{property}} '{{value}}' already exists" after_time: "After time invalid" - + step: header: "Steps" title: "Title" @@ -189,7 +189,7 @@ en: force_final: label: "Conditional Final Step" description: "Display this step as the final step if conditions on later steps have not passed when the user reaches this step." - + field: header: "Fields" label: "Label" @@ -212,7 +212,7 @@ en: prefill: "Prefill" content: "Content" tag_groups: "Tag Groups" - date_time_format: + date_time_format: label: "Format" instructions: "Moment.js format" validations: @@ -229,7 +229,7 @@ en: weeks: "Weeks" months: "Months" years: "Years" - + type: text: "Text" textarea: Textarea @@ -248,7 +248,7 @@ en: date: Date time: Time date_time: Date & Time - + connector: and: "and" or: "or" @@ -262,7 +262,7 @@ en: regex: '=~' association: '→' is: 'is' - + action: header: "Actions" include: "Include Fields" @@ -270,8 +270,8 @@ en: post: "Post" topic_attr: "Topic Attribute" interpolate_fields: "Insert wizard fields using the field_id in w{}. Insert user fields using field key in u{}." - - run_after: + + run_after: label: "Run After" wizard_completion: "Wizard Completion" custom_fields: @@ -353,11 +353,11 @@ en: messageable_level: Messageable Level visibility_level: Visibility Level members_visibility_level: Members Visibility Level - + custom_field: nav_label: "Custom Fields" add: "Add" - external: + external: label: "from another plugin" title: "This custom field has been added by another plugin. You can use it in your wizards but you can't edit the field here." name: @@ -386,7 +386,7 @@ en: basic_category: "Category" basic_group: "Group" post: "Post" - + submissions: nav_label: "Submissions" title: "{{name}} Submissions" @@ -468,59 +468,22 @@ en: subscription_container: title: Subscriber Features - subscribed: + subscribed: label: Subscribed title: You're subscribed and can use these features not_subscribed: label: Not Subscribed title: Subscribe to use these features - - subscription: - nav_label: Subscription - label: In subscription - additional_label: "Add subscription" - title: Custom Wizard Subscription - authorize: Authorize - authorized: Authorized - unauthorize: cancel - not_subscribed: You're not currently subscribed - subscription: - title: - standard: Standard Subscription - business: Business Subscription - status: - active: Active - inactive: Inactive - update: Update - last_updated: Last updated - - notice: - plugin: Custom Wizard Plugin - message: Message - time: Time - status: Status - title: Title - dismiss: - label: Dismiss - title: Dismiss notice - dismiss_all: - label: Dismiss All - title: Dismiss all informational Custom Wizard notices - confirm: Are you sure you want to dismiss all informational Custom Wizard notices? - active: active - created_at: issued - updated_at: updated - expired_at: expired - dismissed_at: dismissed type: - label: Type - info: Information - warning: Warning - connection_error: Connection Error - - notices: - nav_label: Notices - title: Plugin Notices + none: + label: Not Subscribed + title: There is no Custom Wizard subscription active on this forum. + business: + label: Business + title: There is a Custom Wizard Business subscription active on this forum. + standard: + label: Standard + title: There is a Custom Wizard Standard subscription active on this forum. wizard_js: group: @@ -636,7 +599,7 @@ en: yourself_confirm: title: "Did you forget to add recipients?" body: "Right now this message is only being sent to yourself!" - + realtime_validations: similar_topics: insufficient_characters: "Type a minimum 5 characters to start looking for similar topics" diff --git a/config/settings.yml b/config/settings.yml index c4337d92..514462d5 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -1,5 +1,5 @@ plugins: - custom_wizard_enabled: + custom_wizard_enabled: default: true client: true wizard_redirect_exclude_paths: diff --git a/lib/custom_wizard/custom_field.rb b/lib/custom_wizard/custom_field.rb index ed0c9594..f26c11fa 100644 --- a/lib/custom_wizard/custom_field.rb +++ b/lib/custom_wizard/custom_field.rb @@ -84,7 +84,7 @@ class ::CustomWizard::CustomField next end - if attr == 'klass' && @subscription.requires_additional_subscription("custom_fields", "klass").include?(value) + if attr == 'klass' && @subscription.includes?(:custom_field, :klass, value) add_error(I18n.t("wizard.custom_field.error.subscription_type", type: value)) end @@ -99,7 +99,7 @@ class ::CustomWizard::CustomField add_error(I18n.t("#{i18n_key}.unsupported_type", type: value)) end - if attr == 'type' && @subscription.requires_additional_subscription("custom_fields", "type").include?(value) + if attr == 'type' && @subscription.includes?(:custom_field, :type, value) add_error(I18n.t("wizard.custom_field.error.subscription_type", type: value)) end diff --git a/lib/custom_wizard/mapper.rb b/lib/custom_wizard/mapper.rb index c513307b..aa444de1 100644 --- a/lib/custom_wizard/mapper.rb +++ b/lib/custom_wizard/mapper.rb @@ -47,7 +47,6 @@ class CustomWizard::Mapper @data = params[:data] || {} @user = params[:user] @opts = params[:opts] || {} - @subscription = CustomWizard::Subscription.new end def perform @@ -252,7 +251,7 @@ class CustomWizard::Mapper end end - if opts[:template] && @subscription.subscribed? + if opts[:template] && CustomWizard::Subscription.subscribed? template = Liquid::Template.parse(string) string = template.render(data) end diff --git a/lib/custom_wizard/subscription.rb b/lib/custom_wizard/subscription.rb index 39febc14..b59791b3 100644 --- a/lib/custom_wizard/subscription.rb +++ b/lib/custom_wizard/subscription.rb @@ -1,8 +1,170 @@ class CustomWizard::Subscription + STANDARD_PRODUCT_ID = 'prod_LNAGVAaIqDsHmB' + BUSINESS_PRODUCT_ID = 'prod_LNABQ50maBQ1pY' + + def self.attributes + { + wizard: { + required: { + none: [], + standard: ['*'], + business: ['*'] + }, + permitted: { + none: [], + standard: ['*'], + business: ['*'] + } + }, + step: { + condition: { + none: [], + standard: ['*'], + business: ['*'] + }, + index: { + none: [], + standard: ['*'], + business: ['*'] + }, + required_data: { + none: [], + standard: ['*'], + business: ['*'] + }, + permitted_params: { + none: [], + standard: ['*'], + business: ['*'] + } + }, + field: { + condition: { + none: [], + standard: ['*'], + business: ['*'] + }, + index: { + none: [], + standard: ['*'], + business: ['*'] + }, + type: { + none: ['label', 'description', 'image', 'required', 'placeholder', 'file'], + standard: ['*'], + business: ['*'] + }, + prefill: { + standard: ['*'], + business: ['*'] + }, + content: { + none: [], + standard: ['*'], + business: ['*'] + }, + realtime_validations: { + none: [], + standard: ['*'], + business: ['*'] + } + }, + action: { + type: { + none: [], + standard: ['send_message', 'watch_categories', 'add_to_group'], + business: ['*'] + } + }, + custom_field: { + klass: { + none: ['topic', 'post'], + standard: ['topic', 'post'], + business: ['*'] + }, + type: { + none: ['string', 'boolean', 'integer'], + standard: ['string', 'boolean', 'integer'], + business: ['*'] + } + } + } + end + + def initialize + @subscription = find_subscription + end + + def includes?(feature, attribute, value) + attributes = self.class.attributes[feature] + + ## Attribute is not part of a subscription + return true unless attributes.present? && attributes.key?(attribute) + + values = attributes[attribute][type] + + ## Subscription type does not support the attribute. + return false if values.blank? + + ## Subscription type supports all values of the attribute. + return true if values === "*" + + ## Subscription type supports some values of the attributes. + values.include?(value) + end + + def type + return :none unless subscribed? + return :standard if standard? + return :business if business? + end + + def subscribed? + standard? || business? + end + + def standard? + @subscription.product_id === STANDARD_PRODUCT_ID + end + + def business? + @subscription.product_id === BUSINESS_PRODUCT_ID + end + + def client_installed? + defined?(SubscriptionClient) == 'constant' && SubscriptionClient.class == Module + end + + def find_subscription + subscription = nil + + if client_installed? + subscription = SubscriptionClientSubscription.active + .where(product_id: [STANDARD_PRODUCT_ID, BUSINESS_PRODUCT_ID]) + .order("product_id = '#{BUSINESS_PRODUCT_ID}' DESC") + .first + end + + unless subscription + subscription = OpenStruct.new(product_id: nil) + end + + subscription + end + def self.subscribed? + new.subscribed? + end + + def self.business? + new.business? + end + + def self.standard? + new.standard? end def self.type - + new.type end end diff --git a/lib/custom_wizard/validators/template.rb b/lib/custom_wizard/validators/template.rb index 9ae37170..0ffc2608 100644 --- a/lib/custom_wizard/validators/template.rb +++ b/lib/custom_wizard/validators/template.rb @@ -56,29 +56,10 @@ class CustomWizard::TemplateValidator def self.subscription { - wizard: { - save_submissions: 'false', - restart_on_revisit: 'true', - }, - step: { - condition: 'present', - index: 'conditional', - required_data: 'present', - permitted_params: 'present' - }, - field: { - condition: 'present', - index: 'conditional' - }, - action: { - type: %w[ - send_message - add_to_group - create_category - create_group - send_to_api - ] - } + wizard: ['save_submissions', 'restart_on_revisit'], + step: ['condition', 'index', 'required_data', 'permitted_params'], + field: ['condition', 'index'], + action: ['type'] } end @@ -93,16 +74,8 @@ class CustomWizard::TemplateValidator end def validate_subscription(object, type) - self.class.subscription[type].each do |property, subscription_type| - val = object[property.to_s] - is_subscription = (val != nil) && ( - subscription_type === 'present' && val.present? || - (['true', 'false'].include?(subscription_type) && cast_bool(val) == cast_bool(subscription_type)) || - (subscription_type === 'conditional' && val.is_a?(Hash)) || - (subscription_type.is_a?(Array) && subscription_type.include?(val)) - ) - - if is_subscription && !@subscription.subscribed? + self.class.subscription[type].each do |property| + if !@subscription.includes?(type, property, object[property]) errors.add :base, I18n.t("wizard.validation.subscription", type: type.to_s, property: property) end end @@ -148,10 +121,6 @@ class CustomWizard::TemplateValidator end end - def cast_bool(val) - ActiveRecord::Type::Boolean.new.cast(val) - end - def validate_liquid_template(object, type) %w[ description diff --git a/plugin.rb b/plugin.rb index 03862103..ebd5c6d5 100644 --- a/plugin.rb +++ b/plugin.rb @@ -5,17 +5,13 @@ # authors: Angus McLeod, Faizaan Gagan, Robert Barrow, Keegan George # contact_emails: support@thepavilion.io # url: https://github.com/paviliondev/discourse-custom-wizard +# subscription_url: https://coop.pavilion.tech gem 'liquid', '5.0.1', require: true register_asset 'stylesheets/admin/admin.scss', :desktop enabled_site_setting :custom_wizard_enabled -config = Rails.application.config -plugin_asset_path = "#{Rails.root}/plugins/discourse-custom-wizard/assets" -config.assets.paths << "#{plugin_asset_path}/javascripts" -config.assets.paths << "#{plugin_asset_path}/stylesheets/wizard" - if Rails.env.production? config.assets.precompile += %w{ wizard-custom-guest.js @@ -59,6 +55,24 @@ class ::Sprockets::DirectiveProcessor end end +## Override necessary due to 'assets/javascripts/wizard', particularly its tests. +def each_globbed_asset + if @path + root_path = "#{File.dirname(@path)}/assets/javascripts/discourse" + + Dir.glob(["#{root_path}/**/*"]).sort.each do |f| + f_str = f.to_s + if File.directory?(f) + yield [f, true] + elsif f_str.end_with?(".js.es6") || f_str.end_with?(".hbs") || f_str.end_with?(".hbr") + yield [f, false] + elsif transpile_js && f_str.end_with?(".js") + yield [f, false] + end + end + end +end + after_initialize do %w[ ../lib/custom_wizard/engine.rb @@ -91,6 +105,7 @@ after_initialize do ../lib/custom_wizard/step_updater.rb ../lib/custom_wizard/step.rb ../lib/custom_wizard/submission.rb + ../lib/custom_wizard/subscription.rb ../lib/custom_wizard/template.rb ../lib/custom_wizard/wizard.rb ../lib/custom_wizard/api/api.rb diff --git a/spec/components/custom_wizard/notice_spec.rb b/spec/components/custom_wizard/notice_spec.rb deleted file mode 100644 index 0b34664d..00000000 --- a/spec/components/custom_wizard/notice_spec.rb +++ /dev/null @@ -1,159 +0,0 @@ -# frozen_string_literal: true - -require_relative '../../plugin_helper' - -describe CustomWizard::Notice do - fab!(:user) { Fabricate(:user) } - let(:subscription_message) { - { - title: "Title of message about subscription", - message: "Message about subscription", - type: "info", - created_at: Time.now - 3.day, - expired_at: nil - } - } - let(:plugin_status) { - { - name: 'discourse-custom-wizard', - status: 'incompatible', - status_changed_at: Time.now - 3.day - } - } - - context "subscription message" do - before do - freeze_time - stub_request(:get, described_class.subscription_message_url).to_return(status: 200, body: { messages: [subscription_message] }.to_json) - described_class.update(skip_plugin: true) - end - - it "converts subscription messages into notices" do - notice = described_class.list.first - expect(notice.type).to eq(described_class.types[:info]) - expect(notice.message).to eq(subscription_message[:message]) - expect(notice.created_at.to_datetime).to be_within(1.second).of (subscription_message[:created_at].to_datetime) - end - - it "expires notice if subscription message is expired" do - subscription_message[:expired_at] = Time.now - stub_request(:get, described_class.subscription_message_url).to_return(status: 200, body: { messages: [subscription_message] }.to_json) - described_class.update(skip_plugin: true) - - notice = described_class.list(include_all: true).first - expect(notice.expired?).to eq(true) - end - - it "dismisses informational subscription notices" do - notice = described_class.list(include_all: true).first - expect(notice.dismissed?).to eq(false) - - notice.dismiss! - expect(notice.dismissed?).to eq(true) - end - - it "dismisses all informational subscription notices" do - 4.times do |index| - subscription_message[:title] += " #{index}" - stub_request(:get, described_class.subscription_message_url).to_return(status: 200, body: { messages: [subscription_message] }.to_json) - described_class.update(skip_plugin: true) - end - expect(described_class.list.count).to eq(5) - described_class.dismiss_all - expect(described_class.list.count).to eq(0) - end - end - - context "plugin status" do - before do - freeze_time - stub_request(:get, described_class.plugin_status_url).to_return(status: 200, body: plugin_status.to_json) - described_class.update(skip_subscription: true) - end - - it "converts warning into notice" do - notice = described_class.list.first - expect(notice.type).to eq(described_class.types[:warning]) - expect(notice.message).to eq(I18n.t("wizard.notice.compatibility_issue.message", domain: described_class.plugin_status_domain)) - expect(notice.created_at.to_datetime).to be_within(1.second).of (plugin_status[:status_changed_at].to_datetime) - end - - it "expires warning notices if status is recommended or compatible" do - plugin_status[:status] = 'compatible' - plugin_status[:status_changed_at] = Time.now - stub_request(:get, described_class.plugin_status_url).to_return(status: 200, body: plugin_status.to_json) - described_class.update(skip_subscription: true) - - notice = described_class.list(type: described_class.types[:warning], include_all: true).first - expect(notice.expired?).to eq(true) - end - - it "hides plugin status warnings" do - notice = described_class.list.first - expect(notice.hidden?).to eq(false) - - notice.hide! - expect(notice.hidden?).to eq(true) - end - end - - it "lists notices not expired more than a day ago" do - subscription_message[:expired_at] = Time.now - 8.hours - stub_request(:get, described_class.subscription_message_url).to_return(status: 200, body: { messages: [subscription_message] }.to_json) - stub_request(:get, described_class.plugin_status_url).to_return(status: 200, body: plugin_status.to_json) - - described_class.update - expect(described_class.list(include_all: true).length).to eq(2) - end - - context "connection errors" do - before do - freeze_time - end - - it "creates an error if connection to notice server fails" do - stub_request(:get, described_class.plugin_status_url).to_return(status: 400, body: plugin_status.to_json) - described_class.update(skip_subscription: true) - - error = CustomWizard::Notice::ConnectionError.new(:plugin_status) - expect(error.current_error.present?).to eq(true) - end - - it "only creates one connection error per type at a time" do - stub_request(:get, described_class.subscription_message_url).to_return(status: 400, body: { messages: [subscription_message] }.to_json) - stub_request(:get, described_class.plugin_status_url).to_return(status: 400, body: plugin_status.to_json) - - 5.times { described_class.update } - - plugin_status_errors = CustomWizard::Notice::ConnectionError.new(:plugin_status) - subscription_message_errors = CustomWizard::Notice::ConnectionError.new(:subscription_message) - - expect(plugin_status_errors.current_error[:count]).to eq(5) - expect(subscription_message_errors.current_error[:count]).to eq(5) - end - - it "creates a connection error notice if connection errors reach limit" do - stub_request(:get, described_class.plugin_status_url).to_return(status: 400, body: plugin_status.to_json) - - error = CustomWizard::Notice::ConnectionError.new(:plugin_status) - error.limit.times { described_class.update(skip_subscription: true) } - notice = described_class.list(type: described_class.types[:connection_error]).first - - expect(error.current_error[:count]).to eq(error.limit) - expect(notice.type).to eq(described_class.types[:connection_error]) - end - - it "expires a connection error notice if connection succeeds" do - stub_request(:get, described_class.plugin_status_url).to_return(status: 400, body: plugin_status.to_json) - error = CustomWizard::Notice::ConnectionError.new(:plugin_status) - error.limit.times { described_class.update(skip_subscription: true) } - - stub_request(:get, described_class.plugin_status_url).to_return(status: 200, body: plugin_status.to_json) - described_class.update(skip_subscription: true) - notice = described_class.list(type: described_class.types[:connection_error], include_all: true).first - - expect(notice.type).to eq(described_class.types[:connection_error]) - expect(notice.expired_at.present?).to eq(true) - end - end -end diff --git a/spec/components/custom_wizard/subscription_spec.rb b/spec/components/custom_wizard/subscription_spec.rb deleted file mode 100644 index 47fb2d3f..00000000 --- a/spec/components/custom_wizard/subscription_spec.rb +++ /dev/null @@ -1,125 +0,0 @@ -# frozen_string_literal: true - -require_relative '../../plugin_helper' - -describe CustomWizard::Subscription do - fab!(:user) { Fabricate(:user) } - - it "initializes subscription authentication and subscription" do - subscription = described_class.new - expect(subscription.authentication.class).to eq(CustomWizard::Subscription::Authentication) - expect(subscription.subscription.class).to eq(CustomWizard::Subscription::Subscription) - end - - it "returns authorized and subscribed states" do - subscription = described_class.new - expect(subscription.authorized?).to eq(false) - expect(subscription.subscribed?).to eq(false) - end - - context "subscription" do - before do - @subscription = described_class.new - end - - it "updates valid subscriptions" do - stub_subscription_request(200, valid_subscription) - expect(@subscription.update).to eq(true) - expect(@subscription.subscribed?).to eq(true) - end - - it "handles invalid subscriptions" do - stub_subscription_request(200, invalid_subscription) - expect(@subscription.update).to eq(false) - expect(@subscription.subscribed?).to eq(false) - end - - it "handles subscription http errors" do - stub_subscription_request(404, {}) - expect(@subscription.update).to eq(false) - expect(@subscription.subscribed?).to eq(false) - end - - it "destroys subscriptions" do - stub_subscription_request(200, valid_subscription) - expect(@subscription.update).to eq(true) - expect(@subscription.destroy_subscription).to eq(true) - expect(@subscription.subscribed?).to eq(false) - end - - it "has class aliases" do - authenticate_subscription - stub_subscription_request(200, valid_subscription) - expect(described_class.update).to eq(true) - expect(described_class.subscribed?).to eq(true) - end - end - - context "authentication" do - before do - @subscription = described_class.new - user.update!(admin: true) - end - - it "generates a valid authentication request url" do - request_id = SecureRandom.hex(32) - uri = URI(@subscription.authentication_url(user.id, request_id)) - expect(uri.host).to eq(@subscription.server) - - parsed_query = Rack::Utils.parse_query uri.query - expect(parsed_query['public_key'].present?).to eq(true) - expect(parsed_query['nonce'].present?).to eq(true) - expect(parsed_query['client_id'].present?).to eq(true) - expect(parsed_query['auth_redirect'].present?).to eq(true) - expect(parsed_query['application_name']).to eq(SiteSetting.title) - expect(parsed_query['scopes']).to eq(@subscription.scope) - end - - def generate_payload(request_id, user_id) - uri = URI(@subscription.authentication_url(user_id, request_id)) - keys = @subscription.authentication.get_keys(request_id) - raw_payload = { - key: "12345", - nonce: keys.nonce, - push: false, - api: UserApiKeysController::AUTH_API_VERSION - }.to_json - public_key = OpenSSL::PKey::RSA.new(keys.pem) - Base64.encode64(public_key.public_encrypt(raw_payload)) - end - - it "handles authentication response if request and response is valid" do - request_id = SecureRandom.hex(32) - payload = generate_payload(request_id, user.id) - - expect(@subscription.authentication_response(request_id, payload)).to eq(true) - expect(@subscription.authorized?).to eq(true) - end - - it "discards authentication response if user who made request as not an admin" do - user.update!(admin: false) - - request_id = SecureRandom.hex(32) - payload = generate_payload(request_id, user.id) - - expect(@subscription.authentication_response(request_id, payload)).to eq(false) - expect(@subscription.authorized?).to eq(false) - end - - it "discards authentication response if request_id is invalid" do - payload = generate_payload(SecureRandom.hex(32), user.id) - - expect(@subscription.authentication_response(SecureRandom.hex(32), payload)).to eq(false) - expect(@subscription.authorized?).to eq(false) - end - - it "destroys authentication" do - request_id = SecureRandom.hex(32) - payload = generate_payload(request_id, user.id) - @subscription.authentication_response(request_id, payload) - - expect(@subscription.destroy_authentication).to eq(true) - expect(@subscription.authorized?).to eq(false) - end - end -end diff --git a/spec/jobs/update_notices_spec.rb b/spec/jobs/update_notices_spec.rb deleted file mode 100644 index df0697b8..00000000 --- a/spec/jobs/update_notices_spec.rb +++ /dev/null @@ -1,29 +0,0 @@ -# frozen_string_literal: true - -require_relative '../plugin_helper' - -describe Jobs::CustomWizardUpdateNotices do - let(:subscription_message) { - { - message: "Message about subscription", - type: "info", - created_at: Time.now - 3.day, - expired_at: nil - } - } - let(:plugin_status) { - { - name: 'discourse-custom-wizard', - status: 'incompatible', - status_changed_at: Time.now - 3.day - } - } - - it "updates the notices" do - stub_request(:get, CustomWizard::Notice.subscription_message_url).to_return(status: 200, body: { messages: [subscription_message] }.to_json) - stub_request(:get, CustomWizard::Notice.plugin_status_url).to_return(status: 200, body: plugin_status.to_json) - - described_class.new.execute - expect(CustomWizard::Notice.list.length).to eq(2) - end -end diff --git a/spec/jobs/update_subscription_spec.rb b/spec/jobs/update_subscription_spec.rb deleted file mode 100644 index c5adaf38..00000000 --- a/spec/jobs/update_subscription_spec.rb +++ /dev/null @@ -1,11 +0,0 @@ -# frozen_string_literal: true - -require_relative '../plugin_helper' - -describe Jobs::CustomWizardUpdateSubscription do - it "updates the subscription" do - stub_subscription_request(200, valid_subscription) - described_class.new.execute - expect(CustomWizard::Subscription.subscribed?).to eq(true) - end -end diff --git a/spec/plugin_helper.rb b/spec/plugin_helper.rb index 6e340ccf..f7818296 100644 --- a/spec/plugin_helper.rb +++ b/spec/plugin_helper.rb @@ -7,38 +7,3 @@ def get_wizard_fixture(path) ).read ).with_indifferent_access end - -def authenticate_subscription - CustomWizard::Subscription::Authentication.any_instance.stubs(:active?).returns(true) -end - -def enable_subscription(type) - # CustomWizard::Subscription.new - CustomWizard::Subscription.any_instance.stubs(:subscribed?).returns(true) - CustomWizard::Subscription.any_instance.stubs(:type).returns(type) -end - -def disable_subscription - CustomWizard::Subscription.any_instance.stubs(:subscribed?).returns(false) -end - -def valid_subscription - { - product_id: "prod_CBTNpi3fqWWkq0", - price_id: "price_id", - price_nickname: "business" - } -end - -def invalid_subscription - { - product_id: "prod_CBTNpi3fqWWkq0", - price_id: "price_id" - } -end - -def stub_subscription_request(status, subscription) - authenticate_subscription - sub = CustomWizard::Subscription.new - stub_request(:get, "https://#{sub.server}/subscription-server/user-subscriptions/#{sub.subscription_type}/#{sub.client_name}").to_return(status: status, body: { subscriptions: [subscription] }.to_json) -end diff --git a/spec/requests/custom_wizard/admin/notice_controller_spec.rb b/spec/requests/custom_wizard/admin/notice_controller_spec.rb deleted file mode 100644 index 33d03432..00000000 --- a/spec/requests/custom_wizard/admin/notice_controller_spec.rb +++ /dev/null @@ -1,72 +0,0 @@ -# frozen_string_literal: true -require_relative '../../../plugin_helper' - -describe CustomWizard::AdminNoticeController do - fab!(:admin_user) { Fabricate(:user, admin: true) } - let(:subscription_message_notice) { - { - title: "Title of message about subscription", - message: "Message about subscription", - type: 0, - created_at: Time.now.iso8601(3), - expired_at: nil - } - } - let(:plugin_status_notice) { - { - title: "The Custom Wizard Plugin is incompatibile with the latest version of Discourse.", - message: "Please check the Custom Wizard Plugin status on [localhost:3000](http://localhost:3000) before updating Discourse.", - type: 1, - archetype: 1, - created_at: Time.now.iso8601(3), - expired_at: nil - } - } - - before do - sign_in(admin_user) - end - - it "lists notices" do - @notice = CustomWizard::Notice.new(subscription_message_notice) - @notice.save - - get "/admin/wizards/notice.json" - expect(response.status).to eq(200) - expect(response.parsed_body.length).to eq(1) - end - - it "dismisses notices" do - @notice = CustomWizard::Notice.new(subscription_message_notice) - @notice.save - - put "/admin/wizards/notice/#{@notice.id}/dismiss.json" - expect(response.status).to eq(200) - - updated = CustomWizard::Notice.find(@notice.id) - expect(updated.dismissed?).to eq(true) - end - - it "dismisses all notices" do - 5.times do |index| - subscription_message_notice[:title] += " #{index}" - @notice = CustomWizard::Notice.new(subscription_message_notice) - @notice.save - end - - put "/admin/wizards/notice/dismiss.json" - expect(response.status).to eq(200) - expect(CustomWizard::Notice.list.size).to eq(0) - end - - it "hides notices" do - @notice = CustomWizard::Notice.new(plugin_status_notice) - @notice.save - - put "/admin/wizards/notice/#{@notice.id}/hide.json" - expect(response.status).to eq(200) - - updated = CustomWizard::Notice.find(@notice.id) - expect(updated.hidden?).to eq(true) - end -end diff --git a/spec/requests/custom_wizard/admin/subscription_controller_spec.rb b/spec/requests/custom_wizard/admin/subscription_controller_spec.rb deleted file mode 100644 index 2f8aad20..00000000 --- a/spec/requests/custom_wizard/admin/subscription_controller_spec.rb +++ /dev/null @@ -1,71 +0,0 @@ -# frozen_string_literal: true -require_relative '../../../plugin_helper' - -describe CustomWizard::AdminSubscriptionController do - fab!(:admin_user) { Fabricate(:user, admin: true) } - - def generate_payload(request_id, user_id) - uri = URI(@subscription.authentication_url(user_id, request_id)) - keys = @subscription.authentication.get_keys(request_id) - raw_payload = { - key: "12345", - nonce: keys.nonce, - push: false, - api: UserApiKeysController::AUTH_API_VERSION - }.to_json - public_key = OpenSSL::PKey::RSA.new(keys.pem) - Base64.encode64(public_key.public_encrypt(raw_payload)) - end - - before do - @subscription = CustomWizard::Subscription.new - sign_in(admin_user) - end - - it "#index" do - get "/admin/wizards/subscription.json" - expect(response.parsed_body['server']).to eq(@subscription.server) - expect(response.parsed_body['authentication'].deep_symbolize_keys).to eq(CustomWizard::Subscription::AuthenticationSerializer.new(@subscription.authentication, root: false).as_json) - expect(response.parsed_body['subscription'].deep_symbolize_keys).to eq(CustomWizard::Subscription::SubscriptionSerializer.new(@subscription.subscription, root: false).as_json) - end - - it "#authorize" do - get "/admin/wizards/subscription/authorize" - expect(response.status).to eq(302) - expect(cookies[:user_api_request_id].present?).to eq(true) - end - - it "#destroy_authentication" do - request_id = SecureRandom.hex(32) - payload = generate_payload(request_id, admin_user.id) - @subscription.authentication_response(request_id, payload) - - delete "/admin/wizards/subscription/authorize.json" - - expect(response.status).to eq(200) - expect(CustomWizard::Subscription.authorized?).to eq(false) - end - - context "subscription" do - before do - stub_subscription_request(200, valid_subscription) - end - - it "handles authentication response and the updates subscription" do - request_id = cookies[:user_api_request_id] = SecureRandom.hex(32) - payload = generate_payload(request_id, admin_user.id) - get "/admin/wizards/subscription/authorize/callback", params: { payload: payload } - - expect(response).to redirect_to("/admin/wizards/subscription") - expect(CustomWizard::Subscription.subscribed?).to eq(true) - end - - it "updates the subscription" do - authenticate_subscription - post "/admin/wizards/subscription.json" - - expect(response.status).to eq(200) - expect(CustomWizard::Subscription.subscribed?).to eq(true) - end - end -end diff --git a/spec/serializers/custom_wizard/notice_serializer_spec.rb b/spec/serializers/custom_wizard/notice_serializer_spec.rb deleted file mode 100644 index 5184d890..00000000 --- a/spec/serializers/custom_wizard/notice_serializer_spec.rb +++ /dev/null @@ -1,22 +0,0 @@ -# frozen_string_literal: true - -require_relative '../../plugin_helper' - -describe CustomWizard::NoticeSerializer do - before do - @notice = CustomWizard::Notice.new( - message: "Message about subscription", - type: "info", - created_at: Time.now - 3.day, - expired_at: nil - ) - @notice.save - end - - it 'should return notice attributes' do - serialized_notice = described_class.new(@notice) - expect(serialized_notice.message).to eq(@notice.message) - expect(serialized_notice.type).to eq(CustomWizard::Notice.types.key(@notice.type)) - expect(serialized_notice.dismissable).to eq(true) - end -end diff --git a/spec/serializers/custom_wizard/subscription/authentication_serializer_spec.rb b/spec/serializers/custom_wizard/subscription/authentication_serializer_spec.rb deleted file mode 100644 index ac29f95a..00000000 --- a/spec/serializers/custom_wizard/subscription/authentication_serializer_spec.rb +++ /dev/null @@ -1,17 +0,0 @@ -# frozen_string_literal: true - -require_relative '../../../plugin_helper' - -describe CustomWizard::Subscription::AuthenticationSerializer do - fab!(:user) { Fabricate(:user) } - - it 'should return subscription authentication attributes' do - auth = CustomWizard::Subscription::Authentication.new(OpenStruct.new(key: '1234', auth_at: Time.now, auth_by: user.id)) - serialized = described_class.new(auth, root: false).as_json - - expect(serialized[:active]).to eq(true) - expect(serialized[:client_id]).to eq(auth.client_id) - expect(serialized[:auth_by]).to eq(auth.auth_by) - expect(serialized[:auth_at]).to eq(auth.auth_at) - end -end diff --git a/spec/serializers/custom_wizard/subscription/subscription_serializer_spec.rb b/spec/serializers/custom_wizard/subscription/subscription_serializer_spec.rb deleted file mode 100644 index 91dc59b1..00000000 --- a/spec/serializers/custom_wizard/subscription/subscription_serializer_spec.rb +++ /dev/null @@ -1,14 +0,0 @@ -# frozen_string_literal: true - -require_relative '../../../plugin_helper' - -describe CustomWizard::Subscription::SubscriptionSerializer do - it 'should return subscription attributes' do - sub = CustomWizard::Subscription::Subscription.new(OpenStruct.new(type: 'standard', updated_at: Time.now)) - serialized = described_class.new(sub, root: false).as_json - - expect(serialized[:active]).to eq(true) - expect(serialized[:type]).to eq('standard') - expect(serialized[:updated_at]).to eq(sub.updated_at) - end -end diff --git a/spec/serializers/custom_wizard/subscription_serializer_spec.rb b/spec/serializers/custom_wizard/subscription_serializer_spec.rb deleted file mode 100644 index c6ea0ef2..00000000 --- a/spec/serializers/custom_wizard/subscription_serializer_spec.rb +++ /dev/null @@ -1,14 +0,0 @@ -# frozen_string_literal: true - -require_relative '../../plugin_helper' - -describe CustomWizard::SubscriptionSerializer do - it 'should return subscription attributes' do - subscription = CustomWizard::Subscription.new - serialized = described_class.new(subscription, root: false) - - expect(serialized.server).to eq(subscription.server) - expect(serialized.authentication.class).to eq(CustomWizard::Subscription::Authentication) - expect(serialized.subscription.class).to eq(CustomWizard::Subscription::Subscription) - end -end From 92219ace2f4b09b8f22cae86d5844c401c66ef25 Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Fri, 25 Mar 2022 12:22:27 +0100 Subject: [PATCH 117/160] Apply linting --- .../components/wizard-custom-action.js.es6 | 1 - .../wizard-subscription-badge.js.es6 | 4 +-- .../wizard-subscription-container.js.es6 | 2 +- .../wizard-subscription-selector.js.es6 | 28 ++++++++++++------ .../admin-wizards-wizard-show.js.es6 | 1 - .../controllers/admin-wizards.js.es6 | 4 +-- .../initializers/custom-wizard-edits.js.es6 | 2 -- .../discourse/lib/wizard-schema.js.es6 | 2 +- .../discourse/lib/wizard-subscription.js.es6 | 29 +++++++++++-------- .../discourse/mixins/subscription.js.es6 | 22 ++++++++------ .../routes/admin-wizards-custom-fields.js.es6 | 2 +- .../routes/admin-wizards-wizard-show.js.es6 | 2 +- .../discourse/routes/admin-wizards.js.es6 | 4 +-- assets/stylesheets/admin/admin.scss | 4 +-- lib/custom_wizard/subscription.rb | 1 + 15 files changed, 62 insertions(+), 46 deletions(-) diff --git a/assets/javascripts/discourse/components/wizard-custom-action.js.es6 b/assets/javascripts/discourse/components/wizard-custom-action.js.es6 index a0a9cc72..75667c2f 100644 --- a/assets/javascripts/discourse/components/wizard-custom-action.js.es6 +++ b/assets/javascripts/discourse/components/wizard-custom-action.js.es6 @@ -1,5 +1,4 @@ import { default as discourseComputed } from "discourse-common/utils/decorators"; -import wizardSchema from "discourse/plugins/discourse-custom-wizard/discourse/lib/wizard-schema"; import { subscriptionSelectKitContent } from "discourse/plugins/discourse-custom-wizard/discourse/lib/wizard-subscription"; import { empty, equal, or } from "@ember/object/computed"; import { notificationLevels, selectKitContent } from "../lib/wizard"; diff --git a/assets/javascripts/discourse/components/wizard-subscription-badge.js.es6 b/assets/javascripts/discourse/components/wizard-subscription-badge.js.es6 index 7a63f4d3..87c985a4 100644 --- a/assets/javascripts/discourse/components/wizard-subscription-badge.js.es6 +++ b/assets/javascripts/discourse/components/wizard-subscription-badge.js.es6 @@ -5,7 +5,7 @@ import DiscourseURL from "discourse/lib/url"; import I18n from "I18n"; export default Component.extend(Subscription, { - tagName: 'a', + tagName: "a", classNameBindings: [":wizard-subscription-badge", "subscriptionType"], attributeBindings: ["title"], @@ -26,5 +26,5 @@ export default Component.extend(Subscription, { click() { DiscourseURL.routeTo(this.subscriptionLink); - } + }, }); diff --git a/assets/javascripts/discourse/components/wizard-subscription-container.js.es6 b/assets/javascripts/discourse/components/wizard-subscription-container.js.es6 index 3569276c..ccaf2f48 100644 --- a/assets/javascripts/discourse/components/wizard-subscription-container.js.es6 +++ b/assets/javascripts/discourse/components/wizard-subscription-container.js.es6 @@ -22,5 +22,5 @@ export default Component.extend(Subscription, { return `admin.wizard.subscription_container.${ subscribed ? "subscribed" : "not_subscribed" }.title`; - } + }, }); diff --git a/assets/javascripts/discourse/components/wizard-subscription-selector.js.es6 b/assets/javascripts/discourse/components/wizard-subscription-selector.js.es6 index 59714578..2202f746 100644 --- a/assets/javascripts/discourse/components/wizard-subscription-selector.js.es6 +++ b/assets/javascripts/discourse/components/wizard-subscription-selector.js.es6 @@ -3,17 +3,18 @@ import Subscription from "../mixins/subscription"; import wizardSchema from "discourse/plugins/discourse-custom-wizard/discourse/lib/wizard-schema"; import { subscriptionTypeSufficient, - subscriptionTypes + subscriptionTypes, } from "discourse/plugins/discourse-custom-wizard/discourse/lib/wizard-subscription"; import discourseComputed from "discourse-common/utils/decorators"; +import I18n from "I18n"; -const nameKey = function(feature, attribute, value) { - if (feature === 'action') { +const nameKey = function (feature, attribute, value) { + if (feature === "action") { return `admin.wizard.action.${value}.label`; } else { return `admin.wizard.${feature}.${attribute}.${value}`; } -} +}; export default SingleSelectComponent.extend(Subscription, { classNames: ["combo-box", "wizard-subscription-selector"], @@ -30,10 +31,12 @@ export default SingleSelectComponent.extend(Subscription, { requiredSubscriptionType(feature, attribute, value) { let attributes = this.subscriptionAttributes[feature]; - if (!attributes || !attributes[attribute]) return null; + if (!attributes || !attributes[attribute]) { + return null; + } let requiredType = null; - Object.keys(attributes[attribute]).some(subscriptionType => { + Object.keys(attributes[attribute]).some((subscriptionType) => { let values = attributes[attribute][subscriptionType]; if (values[0] === "*" || values.includes(value)) { if (subscriptionTypes.includes(subscriptionType)) { @@ -47,15 +50,22 @@ export default SingleSelectComponent.extend(Subscription, { return requiredType; }, - @discourseComputed('feature', 'attribute') + @discourseComputed("feature", "attribute") content(feature, attribute) { return wizardSchema[feature][attribute].map((value) => { - let requiredSubscriptionType = this.requiredSubscriptionType(feature, attribute, value); + let requiredSubscriptionType = this.requiredSubscriptionType( + feature, + attribute, + value + ); return { id: value, name: I18n.t(nameKey(feature, attribute, value)), subscriptionType: requiredSubscriptionType, - disabled: !subscriptionTypeSufficient(this.subscriptionType, requiredSubscriptionType) + disabled: !subscriptionTypeSufficient( + this.subscriptionType, + requiredSubscriptionType + ), }; }); }, 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 2ecc42da..e6b0ad04 100644 --- a/assets/javascripts/discourse/controllers/admin-wizards-wizard-show.js.es6 +++ b/assets/javascripts/discourse/controllers/admin-wizards-wizard-show.js.es6 @@ -14,7 +14,6 @@ import I18n from "I18n"; export default Controller.extend({ hasName: notEmpty("wizard.name"), - @observes("currentStep") resetCurrentObjects() { const currentStep = this.currentStep; diff --git a/assets/javascripts/discourse/controllers/admin-wizards.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards.js.es6 index b69b878a..f99c06cc 100644 --- a/assets/javascripts/discourse/controllers/admin-wizards.js.es6 +++ b/assets/javascripts/discourse/controllers/admin-wizards.js.es6 @@ -2,6 +2,6 @@ import Controller from "@ember/controller"; import { equal } from "@ember/object/computed"; export default Controller.extend({ - businessSubscription: equal('subscriptionType', 'business'), - standardSubscription: equal('subscriptionType', 'standard') + businessSubscription: equal("subscriptionType", "business"), + standardSubscription: equal("subscriptionType", "standard"), }); diff --git a/assets/javascripts/discourse/initializers/custom-wizard-edits.js.es6 b/assets/javascripts/discourse/initializers/custom-wizard-edits.js.es6 index 40d21b35..6d1410ad 100644 --- a/assets/javascripts/discourse/initializers/custom-wizard-edits.js.es6 +++ b/assets/javascripts/discourse/initializers/custom-wizard-edits.js.es6 @@ -1,7 +1,5 @@ import DiscourseURL from "discourse/lib/url"; import { withPluginApi } from "discourse/lib/plugin-api"; -import { isPresent } from "@ember/utils"; -import { A } from "@ember/array"; import getUrl from "discourse-common/lib/get-url"; export default { diff --git a/assets/javascripts/discourse/lib/wizard-schema.js.es6 b/assets/javascripts/discourse/lib/wizard-schema.js.es6 index ca5c30ee..97477fe8 100644 --- a/assets/javascripts/discourse/lib/wizard-schema.js.es6 +++ b/assets/javascripts/discourse/lib/wizard-schema.js.es6 @@ -211,7 +211,7 @@ const wizardSchema = { step, field, custom_field, - action + action, }; export function buildFieldTypes(types) { diff --git a/assets/javascripts/discourse/lib/wizard-subscription.js.es6 b/assets/javascripts/discourse/lib/wizard-subscription.js.es6 index deff32ca..adecf063 100644 --- a/assets/javascripts/discourse/lib/wizard-subscription.js.es6 +++ b/assets/javascripts/discourse/lib/wizard-subscription.js.es6 @@ -1,17 +1,22 @@ -const subscriptionTypes = [ - 'standard', - 'business' -] +const subscriptionTypes = ["standard", "business"]; function subscriptionTypeSufficient(subscriptionType, requiredType) { - if (requiredType && !subscriptionType) return false; - if (requiredType === 'none' || requiredType === null) return true; - if (requiredType === 'standard' && subscriptionTypes.includes(subscriptionType)) return true; - if (requiredType === 'business' && subscriptionType === 'business') return true; + if (requiredType && !subscriptionType) { + return false; + } + if (requiredType === "none" || requiredType === null) { + return true; + } + if ( + requiredType === "standard" && + subscriptionTypes.includes(subscriptionType) + ) { + return true; + } + if (requiredType === "business" && subscriptionType === "business") { + return true; + } return false; } -export { - subscriptionTypeSufficient, - subscriptionTypes -} +export { subscriptionTypeSufficient, subscriptionTypes }; diff --git a/assets/javascripts/discourse/mixins/subscription.js.es6 b/assets/javascripts/discourse/mixins/subscription.js.es6 index 00f77bb2..574f8585 100644 --- a/assets/javascripts/discourse/mixins/subscription.js.es6 +++ b/assets/javascripts/discourse/mixins/subscription.js.es6 @@ -9,18 +9,22 @@ export default Mixin.create({ @discourseComputed adminWizards() { - return getOwner(this).lookup('controller:admin-wizards'); + return getOwner(this).lookup("controller:admin-wizards"); }, - subscribed: readOnly('adminWizards.subscribed'), - subscriptionType: readOnly('adminWizards.subscriptionType'), - businessSubscription: readOnly('adminWizards.businessSubscription'), - standardSubscription: readOnly('adminWizards.standardSubscription'), - subscriptionAttributes: readOnly('adminWizards.subscriptionAttributes'), - subscriptionClientInstalled: readOnly('adminWizards.subscriptionClientInstalled'), + subscribed: readOnly("adminWizards.subscribed"), + subscriptionType: readOnly("adminWizards.subscriptionType"), + businessSubscription: readOnly("adminWizards.businessSubscription"), + standardSubscription: readOnly("adminWizards.standardSubscription"), + subscriptionAttributes: readOnly("adminWizards.subscriptionAttributes"), + subscriptionClientInstalled: readOnly( + "adminWizards.subscriptionClientInstalled" + ), @discourseComputed("subscriptionClientInstalled") subscriptionLink(subscriptionClientInstalled) { - return subscriptionClientInstalled ? this.subscriptionClientUrl : this.subscriptionLandingUrl; - } + return subscriptionClientInstalled + ? this.subscriptionClientUrl + : this.subscriptionLandingUrl; + }, }); diff --git a/assets/javascripts/discourse/routes/admin-wizards-custom-fields.js.es6 b/assets/javascripts/discourse/routes/admin-wizards-custom-fields.js.es6 index 4d9de2c4..a04d36f9 100644 --- a/assets/javascripts/discourse/routes/admin-wizards-custom-fields.js.es6 +++ b/assets/javascripts/discourse/routes/admin-wizards-custom-fields.js.es6 @@ -11,7 +11,7 @@ export default DiscourseRoute.extend({ const customFields = A(model.custom_fields || []); controller.setProperties({ - customFields + customFields, }); }, }); diff --git a/assets/javascripts/discourse/routes/admin-wizards-wizard-show.js.es6 b/assets/javascripts/discourse/routes/admin-wizards-wizard-show.js.es6 index 63ff564e..cb2d54c3 100644 --- a/assets/javascripts/discourse/routes/admin-wizards-wizard-show.js.es6 +++ b/assets/javascripts/discourse/routes/admin-wizards-wizard-show.js.es6 @@ -38,7 +38,7 @@ export default DiscourseRoute.extend({ wizard, currentStep: wizard.steps[0], currentAction: wizard.actions[0], - creating: model.create + creating: model.create, }; controller.setProperties(props); diff --git a/assets/javascripts/discourse/routes/admin-wizards.js.es6 b/assets/javascripts/discourse/routes/admin-wizards.js.es6 index 0286926d..1fa786d3 100644 --- a/assets/javascripts/discourse/routes/admin-wizards.js.es6 +++ b/assets/javascripts/discourse/routes/admin-wizards.js.es6 @@ -11,7 +11,7 @@ export default DiscourseRoute.extend({ subscribed: model.subscribed, subscriptionType: model.subscription_type, subscriptionAttributes: model.subscription_attributes, - subscriptionClientInstalled: model.subscription_client_installed + subscriptionClientInstalled: model.subscription_client_installed, }); }, @@ -19,5 +19,5 @@ export default DiscourseRoute.extend({ if (transition.targetName === "adminWizards.index") { this.transitionTo("adminWizardsWizard"); } - } + }, }); diff --git a/assets/stylesheets/admin/admin.scss b/assets/stylesheets/admin/admin.scss index b8458163..f1911f2c 100644 --- a/assets/stylesheets/admin/admin.scss +++ b/assets/stylesheets/admin/admin.scss @@ -847,7 +847,7 @@ $error: #ef1700; .subscription-header { display: flex; justify-content: space-between; - margin-bottom: .25em; + margin-bottom: 0.25em; h3 { margin: 0; @@ -871,7 +871,7 @@ $error: #ef1700; box-sizing: border-box; position: relative; cursor: pointer; - padding: .5em .65em; + padding: 0.5em 0.65em; background-color: rgba($primary-medium, 0.05); border: 1.5px solid rgba($primary-medium, 0.5); color: $primary-medium; diff --git a/lib/custom_wizard/subscription.rb b/lib/custom_wizard/subscription.rb index b59791b3..3d2d665e 100644 --- a/lib/custom_wizard/subscription.rb +++ b/lib/custom_wizard/subscription.rb @@ -1,3 +1,4 @@ +# frozen_string_literal: true class CustomWizard::Subscription STANDARD_PRODUCT_ID = 'prod_LNAGVAaIqDsHmB' BUSINESS_PRODUCT_ID = 'prod_LNABQ50maBQ1pY' From 04f0d34ef35ed2f45610989018905629a38a524d Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Fri, 25 Mar 2022 17:08:24 +0100 Subject: [PATCH 118/160] Fix specs --- app/controllers/custom_wizard/wizard.rb | 2 +- config/locales/server.en.yml | 1 + lib/custom_wizard/custom_field.rb | 4 +- lib/custom_wizard/subscription.rb | 25 ++-- lib/custom_wizard/validators/template.rb | 15 +- spec/components/custom_wizard/action_spec.rb | 35 +++-- spec/components/custom_wizard/builder_spec.rb | 5 +- .../custom_wizard/subscription_spec.rb | 104 +++++++++++++ .../components/custom_wizard/template_spec.rb | 2 + .../custom_wizard/template_validator_spec.rb | 12 +- .../custom_wizard/update_validator_spec.rb | 61 ++++---- spec/components/custom_wizard/wizard_spec.rb | 139 +++++++++--------- spec/fixtures/actions/watch_categories.json | 23 +++ spec/fixtures/field/advanced_types.json | 27 ++++ spec/fixtures/field/composer_preview.json | 7 + spec/fixtures/field/url.json | 5 + spec/fixtures/subscription_client.rb | 4 + spec/fixtures/wizard.json | 69 --------- spec/plugin_helper.rb | 6 + .../admin/wizard_controller_spec.rb | 1 + .../custom_wizard/steps_controller_spec.rb | 1 + .../custom_wizard/wizard_controller_spec.rb | 3 +- .../wizard_field_serializer_spec.rb | 4 +- .../custom_wizard/wizard_serializer_spec.rb | 75 +++++----- 24 files changed, 379 insertions(+), 251 deletions(-) create mode 100644 spec/components/custom_wizard/subscription_spec.rb create mode 100644 spec/fixtures/actions/watch_categories.json create mode 100644 spec/fixtures/field/advanced_types.json create mode 100644 spec/fixtures/field/composer_preview.json create mode 100644 spec/fixtures/field/url.json create mode 100644 spec/fixtures/subscription_client.rb diff --git a/app/controllers/custom_wizard/wizard.rb b/app/controllers/custom_wizard/wizard.rb index bc0a06af..bb53670b 100644 --- a/app/controllers/custom_wizard/wizard.rb +++ b/app/controllers/custom_wizard/wizard.rb @@ -44,7 +44,7 @@ class CustomWizard::WizardController < ::ActionController::Base return render json: { error: I18n.t('wizard.no_skip') } end - result = success_json + result = { success: 'OK' } if current_user && wizard.can_access? if redirect_to = wizard.current_submission&.redirect_to diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index 2483503b..08cf5336 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -53,6 +53,7 @@ en: after_signup_after_time: "You can't use 'after time' and 'after signup' on the same wizard." after_time: "After time setting is invalid." liquid_syntax_error: "Liquid syntax error in %{attribute}: %{message}" + subscription: "%{type} %{property} is subscription only" site_settings: custom_wizard_enabled: "Enable custom wizards." diff --git a/lib/custom_wizard/custom_field.rb b/lib/custom_wizard/custom_field.rb index f26c11fa..29423ec6 100644 --- a/lib/custom_wizard/custom_field.rb +++ b/lib/custom_wizard/custom_field.rb @@ -84,7 +84,7 @@ class ::CustomWizard::CustomField next end - if attr == 'klass' && @subscription.includes?(:custom_field, :klass, value) + if attr == 'klass' && !@subscription.includes?(:custom_field, :klass, value) add_error(I18n.t("wizard.custom_field.error.subscription_type", type: value)) end @@ -99,7 +99,7 @@ class ::CustomWizard::CustomField add_error(I18n.t("#{i18n_key}.unsupported_type", type: value)) end - if attr == 'type' && @subscription.includes?(:custom_field, :type, value) + if attr == 'type' && !@subscription.includes?(:custom_field, :type, value) add_error(I18n.t("wizard.custom_field.error.subscription_type", type: value)) end diff --git a/lib/custom_wizard/subscription.rb b/lib/custom_wizard/subscription.rb index 3d2d665e..114f6743 100644 --- a/lib/custom_wizard/subscription.rb +++ b/lib/custom_wizard/subscription.rb @@ -51,16 +51,7 @@ class CustomWizard::Subscription business: ['*'] }, type: { - none: ['label', 'description', 'image', 'required', 'placeholder', 'file'], - standard: ['*'], - business: ['*'] - }, - prefill: { - standard: ['*'], - business: ['*'] - }, - content: { - none: [], + none: ['text', 'textarea', 'text_only', 'date', 'time', 'date_time', 'number', 'checkbox', 'dropdown', 'upload'], standard: ['*'], business: ['*'] }, @@ -72,8 +63,8 @@ class CustomWizard::Subscription }, action: { type: { - none: [], - standard: ['send_message', 'watch_categories', 'add_to_group'], + none: ['create_topic', 'update_profile', 'open_composer', 'route_to'], + standard: ['create_topic', 'update_profile', 'open_composer', 'route_to', 'send_message', 'watch_categories', 'add_to_group'], business: ['*'] } }, @@ -108,7 +99,7 @@ class CustomWizard::Subscription return false if values.blank? ## Subscription type supports all values of the attribute. - return true if values === "*" + return true if values.first === "*" ## Subscription type supports some values of the attributes. values.include?(value) @@ -168,4 +159,12 @@ class CustomWizard::Subscription def self.type new.type end + + def self.client_installed? + new.client_installed? + end + + def self.includes?(feature, attribute, value) + new.includes?(feature, attribute, value) + end end diff --git a/lib/custom_wizard/validators/template.rb b/lib/custom_wizard/validators/template.rb index 0ffc2608..079f9884 100644 --- a/lib/custom_wizard/validators/template.rb +++ b/lib/custom_wizard/validators/template.rb @@ -54,15 +54,6 @@ class CustomWizard::TemplateValidator } end - def self.subscription - { - wizard: ['save_submissions', 'restart_on_revisit'], - step: ['condition', 'index', 'required_data', 'permitted_params'], - field: ['condition', 'index'], - action: ['type'] - } - end - private def check_required(object, type) @@ -74,8 +65,10 @@ class CustomWizard::TemplateValidator end def validate_subscription(object, type) - self.class.subscription[type].each do |property| - if !@subscription.includes?(type, property, object[property]) + object.keys.each do |property| + value = object[property] + + if !@subscription.includes?(type, property.to_sym, value) errors.add :base, I18n.t("wizard.validation.subscription", type: type.to_s, property: property) end end diff --git a/spec/components/custom_wizard/action_spec.rb b/spec/components/custom_wizard/action_spec.rb index 6149272e..5155a7f4 100644 --- a/spec/components/custom_wizard/action_spec.rb +++ b/spec/components/custom_wizard/action_spec.rb @@ -8,6 +8,7 @@ describe CustomWizard::Action do let(:wizard_template) { get_wizard_fixture("wizard") } let(:open_composer) { get_wizard_fixture("actions/open_composer") } let(:create_category) { get_wizard_fixture("actions/create_category") } + let(:watch_categories) { get_wizard_fixture("actions/watch_categories") } let(:create_group) { get_wizard_fixture("actions/create_group") } let(:add_to_group) { get_wizard_fixture("actions/add_to_group") } let(:send_message) { get_wizard_fixture("actions/send_message") } @@ -158,17 +159,6 @@ describe CustomWizard::Action do end end - it 'watches categories' do - wizard = CustomWizard::Builder.new(@template[:id], user).build - wizard.create_updater(wizard.steps[0].id, step_1_field_1: "Text input").update - wizard.create_updater(wizard.steps[1].id, {}).update - - expect(CategoryUser.where( - category_id: category.id, - user_id: user.id - ).first.notification_level).to eq(0) - end - it 're-routes a user' do wizard = CustomWizard::Builder.new(@template[:id], user).build updater = wizard.create_updater(wizard.steps.last.id, {}) @@ -176,11 +166,25 @@ describe CustomWizard::Action do expect(updater.result[:redirect_on_next]).to eq("https://google.com") end - context "subscription actions" do + context "standard subscription actions" do before do enable_subscription("standard") end + it 'watches categories' do + watch_categories[:categories][0][:output] = category.id + wizard_template[:actions] << watch_categories + update_template(wizard_template) + + wizard = CustomWizard::Builder.new(@template[:id], user).build + wizard.create_updater(wizard.steps[0].id, step_1_field_1: "Text input").update + + expect(CategoryUser.where( + category_id: category.id, + user_id: user.id + ).first.notification_level).to eq(2) + end + it '#send_message' do wizard_template['actions'] << send_message update_template(wizard_template) @@ -233,9 +237,16 @@ describe CustomWizard::Action do expect(topic.first.allowed_groups.map(&:name)).to include('cool_group', 'cool_group_1') expect(post.exists?).to eq(true) end + end + + context "business subscription actions" do + before do + enable_subscription("business") + end it '#create_category' do wizard_template['actions'] << create_category + wizard_template['actions'] << create_group update_template(wizard_template) wizard = CustomWizard::Builder.new(@template[:id], user).build diff --git a/spec/components/custom_wizard/builder_spec.rb b/spec/components/custom_wizard/builder_spec.rb index 1d7b1213..ebcc355b 100644 --- a/spec/components/custom_wizard/builder_spec.rb +++ b/spec/components/custom_wizard/builder_spec.rb @@ -100,6 +100,7 @@ describe CustomWizard::Builder do context "with restricted permissions" do before do + enable_subscription("standard") @template[:permitted] = permitted_json["permitted"] CustomWizard::Template.save(@template.as_json) end @@ -304,7 +305,7 @@ describe CustomWizard::Builder do before do enable_subscription("standard") @template[:steps][0][:fields][0][:condition] = user_condition_json['condition'] - @template[:steps][2][:fields][5][:condition] = boolean_field_condition_json['condition'] + @template[:steps][2][:fields][0][:condition] = boolean_field_condition_json['condition'] CustomWizard::Template.save(@template.as_json) end @@ -325,7 +326,7 @@ describe CustomWizard::Builder do builder = CustomWizard::Builder.new(@template[:id], user) wizard = builder.build - expect(wizard.steps.last.fields.last.id).to eq(@template[:steps][2][:fields][5]['id']) + expect(wizard.steps.last.fields.last.id).to eq(@template[:steps][2][:fields][0]['id']) end end end diff --git a/spec/components/custom_wizard/subscription_spec.rb b/spec/components/custom_wizard/subscription_spec.rb new file mode 100644 index 00000000..984eff9f --- /dev/null +++ b/spec/components/custom_wizard/subscription_spec.rb @@ -0,0 +1,104 @@ +# frozen_string_literal: true + +describe CustomWizard::Subscription do + def undefine_client_classes + Object.send(:remove_const, :SubscriptionClient) if Object.constants.include?(:SubscriptionClient) + Object.send(:remove_const, :SubscriptionClientSubscription) if Object.constants.include?(:SubscriptionClientSubscription) + end + + def define_client_classes + load File.expand_path("#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/subscription_client.rb", __FILE__) + end + + def stub_client_methods + [:active, :where, :order, :first].each do |method| + SubscriptionClientSubscription.stubs(method) + .returns(SubscriptionClientSubscription) + end + SubscriptionClientSubscription.stubs(:product_id).returns(SecureRandom.hex(8)) + end + + after do + undefine_client_classes + end + + it "detects the subscription client" do + expect(described_class.client_installed?).to eq(false) + end + + context "without a subscription client" do + it "is not subscribed" do + expect(described_class.subscribed?).to eq(false) + end + + it "has none type" do + subscription = described_class.new + expect(subscription.type).to eq(:none) + end + + it "non subscriber features are included" do + expect(described_class.includes?(:wizard, :after_signup, true)).to eq(true) + end + + it "ubscriber features are not included" do + expect(described_class.includes?(:wizard, :permitted, {})).to eq(false) + end + end + + context "with subscription client" do + before do + define_client_classes + stub_client_methods + end + + it "detects the subscription client" do + expect(described_class.client_installed?).to eq(true) + end + + context "without a subscription" do + it "has none type" do + expect(described_class.type).to eq(:none) + end + + it "non subscriber features are included" do + expect(described_class.includes?(:wizard, :after_signup, true)).to eq(true) + end + + it "subscriber features are not included" do + expect(described_class.includes?(:wizard, :permitted, {})).to eq(false) + end + end + + context "with standard subscription" do + before do + SubscriptionClientSubscription.stubs(:product_id).returns(CustomWizard::Subscription::STANDARD_PRODUCT_ID) + end + + it "detects standard type" do + expect(described_class.type).to eq(:standard) + end + + it "standard features are included" do + expect(described_class.includes?(:wizard, :type, 'send_message')).to eq(true) + end + + it "business features are not included" do + expect(described_class.includes?(:action, :type, 'create_category')).to eq(false) + end + end + + context "with business subscription" do + before do + SubscriptionClientSubscription.stubs(:product_id).returns(CustomWizard::Subscription::BUSINESS_PRODUCT_ID) + end + + it "detects business type" do + expect(described_class.type).to eq(:business) + end + + it "business are included" do + expect(described_class.includes?(:action, :type, 'create_category')).to eq(true) + end + end + end +end diff --git a/spec/components/custom_wizard/template_spec.rb b/spec/components/custom_wizard/template_spec.rb index 14e1659b..63ea25c6 100644 --- a/spec/components/custom_wizard/template_spec.rb +++ b/spec/components/custom_wizard/template_spec.rb @@ -47,6 +47,8 @@ describe CustomWizard::Template do context "wizard template list" do before do + enable_subscription('standard') + template_json_2 = template_json.dup template_json_2["id"] = 'super_mega_fun_wizard_2' template_json_2["permitted"] = permitted_json['permitted'] diff --git a/spec/components/custom_wizard/template_validator_spec.rb b/spec/components/custom_wizard/template_validator_spec.rb index 3561e272..b149706f 100644 --- a/spec/components/custom_wizard/template_validator_spec.rb +++ b/spec/components/custom_wizard/template_validator_spec.rb @@ -5,6 +5,8 @@ describe CustomWizard::TemplateValidator do let(:template) { get_wizard_fixture("wizard") } let(:create_category) { get_wizard_fixture("actions/create_category") } let(:user_condition) { get_wizard_fixture("condition/user_condition") } + let(:permitted_json) { get_wizard_fixture("wizard/permitted") } + let(:composer_preview) { get_wizard_fixture("field/composer_preview") } let(:valid_liquid_template) { <<-LIQUID.strip @@ -104,7 +106,7 @@ describe CustomWizard::TemplateValidator do context "without subscription" do it "invalidates subscription wizard attributes" do - template[:save_submissions] = false + template[:permitted] = permitted_json['permitted'] expect( CustomWizard::TemplateValidator.new(template).perform ).to eq(false) @@ -134,11 +136,11 @@ describe CustomWizard::TemplateValidator do context "with subscription" do before do - enable_subscription("standard") + enable_subscription("business") end it "validates wizard attributes" do - template[:save_submissions] = false + template[:permitted] = permitted_json['permitted'] expect( CustomWizard::TemplateValidator.new(template).perform ).to eq(true) @@ -227,7 +229,9 @@ describe CustomWizard::TemplateValidator do end it "validates preview templates" do - template[:steps][0][:fields][4][:preview_template] = invalid_liquid_template + enable_subscription("standard") + template[:steps][0][:fields] << composer_preview + template[:steps][0][:fields][3][:preview_template] = invalid_liquid_template expect_validation_failure("step_1_field_5.preview_template", liquid_syntax_error) end end diff --git a/spec/components/custom_wizard/update_validator_spec.rb b/spec/components/custom_wizard/update_validator_spec.rb index a01f70d7..7caa1784 100644 --- a/spec/components/custom_wizard/update_validator_spec.rb +++ b/spec/components/custom_wizard/update_validator_spec.rb @@ -3,6 +3,7 @@ describe CustomWizard::UpdateValidator do fab!(:user) { Fabricate(:user) } let(:template) { get_wizard_fixture("wizard") } + let(:url_field) { get_wizard_fixture("field/url") } before do CustomWizard::Template.save(template, skip_jobs: true) @@ -21,7 +22,6 @@ describe CustomWizard::UpdateValidator do @template[:steps][0][:fields][0][:min_length] = min_length @template[:steps][0][:fields][1][:min_length] = min_length - @template[:steps][0][:fields][2][:min_length] = min_length CustomWizard::Template.save(@template) @@ -34,11 +34,6 @@ describe CustomWizard::UpdateValidator do expect( updater.errors.messages[:step_1_field_2].first ).to eq(I18n.t('wizard.field.too_short', label: 'Textarea', min: min_length)) - - updater = perform_validation('step_1', step_1_field_3: 'Te') - expect( - updater.errors.messages[:step_1_field_3].first - ).to eq(I18n.t('wizard.field.too_short', label: 'Composer', min: min_length)) end it 'prevents submission if the length is over the max length' do @@ -46,7 +41,6 @@ describe CustomWizard::UpdateValidator do @template[:steps][0][:fields][0][:max_length] = max_length @template[:steps][0][:fields][1][:max_length] = max_length - @template[:steps][0][:fields][2][:max_length] = max_length CustomWizard::Template.save(@template) long_string = "Our Competitive Capability solution offers platforms a suite of wholesale offerings. In the future, will you be able to effectively revolutionize synergies in your business? In the emerging market space, industry is ethically investing its mission critical executive searches. Key players will take ownership of their capabilities by iteratively right-sizing world-class visibilities. " @@ -59,11 +53,6 @@ describe CustomWizard::UpdateValidator do expect( updater.errors.messages[:step_1_field_2].first ).to eq(I18n.t('wizard.field.too_long', label: 'Textarea', max: max_length)) - - updater = perform_validation('step_1', step_1_field_3: long_string) - expect( - updater.errors.messages[:step_1_field_3].first - ).to eq(I18n.t('wizard.field.too_long', label: 'Composer', max: max_length)) end it "allows submission if the length is under or equal to the max length" do @@ -71,7 +60,6 @@ describe CustomWizard::UpdateValidator do @template[:steps][0][:fields][0][:max_length] = max_length @template[:steps][0][:fields][1][:max_length] = max_length - @template[:steps][0][:fields][2][:max_length] = max_length CustomWizard::Template.save(@template) hundred_chars_string = "This is a line, exactly hundred characters long and not more even a single character more than that." @@ -84,11 +72,6 @@ describe CustomWizard::UpdateValidator do expect( updater.errors.messages[:step_1_field_2].first ).to eq(nil) - - updater = perform_validation('step_1', step_1_field_3: hundred_chars_string) - expect( - updater.errors.messages[:step_1_field_3].first - ).to eq(nil) end it "applies min length only if the input is non-empty" do @@ -131,25 +114,33 @@ describe CustomWizard::UpdateValidator do ).to eq(I18n.t('wizard.field.required', label: 'Textarea')) end - it 'validates url fields' do - updater = perform_validation('step_2', step_2_field_6: 'https://discourse.com') - expect( - updater.errors.messages[:step_2_field_6].first - ).to eq(nil) - end + context "subscription fields" do + before do + enable_subscription("standard") + end - it 'does not validate url fields with non-url inputs' do - updater = perform_validation('step_2', step_2_field_6: 'discourse') - expect( - updater.errors.messages[:step_2_field_6].first - ).to eq(I18n.t('wizard.field.not_url', label: 'Url')) - end + it 'validates url fields' do + updater = perform_validation('step_2', step_2_field_6: 'https://discourse.com') + expect( + updater.errors.messages[:step_2_field_6].first + ).to eq(nil) + end - it 'validates empty url fields' do - updater = perform_validation('step_2', step_2_field_6: '') - expect( - updater.errors.messages[:step_2_field_6].first - ).to eq(nil) + it 'does not validate url fields with non-url inputs' do + template[:steps][0][:fields] << url_field + CustomWizard::Template.save(template) + updater = perform_validation('step_1', step_2_field_6: 'discourse') + expect( + updater.errors.messages[:step_2_field_6].first + ).to eq(I18n.t('wizard.field.not_url', label: 'Url')) + end + + it 'validates empty url fields' do + updater = perform_validation('step_2', step_2_field_6: '') + expect( + updater.errors.messages[:step_2_field_6].first + ).to eq(nil) + end end it 'validates date fields' do diff --git a/spec/components/custom_wizard/wizard_spec.rb b/spec/components/custom_wizard/wizard_spec.rb index 00354bf1..5dc1cb95 100644 --- a/spec/components/custom_wizard/wizard_spec.rb +++ b/spec/components/custom_wizard/wizard_spec.rb @@ -113,69 +113,91 @@ describe CustomWizard::Wizard do expect(wizard.completed?).to eq(false) end - it "permits admins" do - expect( - CustomWizard::Wizard.new(@permitted_template, admin_user).permitted? - ).to eq(true) - end + context "with subscription" do + before do + enable_subscription("standard") + end - it "permits permitted users" do - expect( - CustomWizard::Wizard.new(@permitted_template, trusted_user).permitted? - ).to eq(true) - end + it "permits admins" do + expect( + CustomWizard::Wizard.new(@permitted_template, admin_user).permitted? + ).to eq(true) + end - it "permits everyone if everyone is permitted" do - @permitted_template['permitted'][0]['output'] = Group::AUTO_GROUPS[:everyone] - expect( - CustomWizard::Wizard.new(@permitted_template, user).permitted? - ).to eq(true) - end + it "permits permitted users" do + expect( + CustomWizard::Wizard.new(@permitted_template, trusted_user).permitted? + ).to eq(true) + end - it "does not permit unpermitted users" do - expect( - CustomWizard::Wizard.new(@permitted_template, user).permitted? - ).to eq(false) - end + it "permits everyone if everyone is permitted" do + @permitted_template['permitted'][0]['output'] = Group::AUTO_GROUPS[:everyone] + expect( + CustomWizard::Wizard.new(@permitted_template, user).permitted? + ).to eq(true) + end - it "does not let an unpermitted user access a wizard" do - expect( - CustomWizard::Wizard.new(@permitted_template, user).can_access? - ).to eq(false) - end + it "does not permit unpermitted users" do + expect( + CustomWizard::Wizard.new(@permitted_template, user).permitted? + ).to eq(false) + end - it "lets a permitted user access an incomplete wizard" do - expect( - CustomWizard::Wizard.new(@permitted_template, trusted_user).can_access? - ).to eq(true) - end + it "does not let an unpermitted user access a wizard" do + expect( + CustomWizard::Wizard.new(@permitted_template, user).can_access? + ).to eq(false) + end - it "lets a permitted user access a complete wizard with multiple submissions" do - append_steps + it "lets a permitted user access an incomplete wizard" do + expect( + CustomWizard::Wizard.new(@permitted_template, trusted_user).can_access? + ).to eq(true) + end - progress_step("step_1", acting_user: trusted_user) - progress_step("step_2", acting_user: trusted_user) - progress_step("step_3", acting_user: trusted_user) + it "lets a permitted user access a complete wizard with multiple submissions" do + append_steps - @permitted_template["multiple_submissions"] = true + progress_step("step_1", acting_user: trusted_user) + progress_step("step_2", acting_user: trusted_user) + progress_step("step_3", acting_user: trusted_user) - expect( - CustomWizard::Wizard.new(@permitted_template, trusted_user).can_access? - ).to eq(true) - end + @permitted_template["multiple_submissions"] = true - it "does not let an unpermitted user access a complete wizard without multiple submissions" do - append_steps + expect( + CustomWizard::Wizard.new(@permitted_template, trusted_user).can_access? + ).to eq(true) + end - progress_step("step_1", acting_user: trusted_user) - progress_step("step_2", acting_user: trusted_user) - progress_step("step_3", acting_user: trusted_user) + it "does not let an unpermitted user access a complete wizard without multiple submissions" do + append_steps - @permitted_template['multiple_submissions'] = false + progress_step("step_1", acting_user: trusted_user) + progress_step("step_2", acting_user: trusted_user) + progress_step("step_3", acting_user: trusted_user) - expect( - CustomWizard::Wizard.new(@permitted_template, trusted_user).can_access? - ).to eq(false) + @permitted_template['multiple_submissions'] = false + + expect( + CustomWizard::Wizard.new(@permitted_template, trusted_user).can_access? + ).to eq(false) + end + + it "sets wizard redirects if user is permitted" do + CustomWizard::Template.save(@permitted_template, skip_jobs: true) + CustomWizard::Wizard.set_user_redirect('super_mega_fun_wizard', trusted_user) + expect( + trusted_user.custom_fields['redirect_to_wizard'] + ).to eq("super_mega_fun_wizard") + end + + it "does not set a wizard redirect if user is not permitted" do + CustomWizard::Template.save(@permitted_template, skip_jobs: true) + CustomWizard::Wizard.set_user_redirect('super_mega_fun_wizard', user) + expect( + trusted_user.custom_fields['redirect_to_wizard'] + ).to eq(nil) + end end it "lists the site groups" do @@ -203,6 +225,7 @@ describe CustomWizard::Wizard do context "class methods" do before do + enable_subscription("standard") CustomWizard::Template.save(@permitted_template, skip_jobs: true) template_json_2 = template_json.dup @@ -238,20 +261,4 @@ describe CustomWizard::Wizard do expect(CustomWizard::Wizard.prompt_completion(user).length).to eq(1) end end - - it "sets wizard redirects if user is permitted" do - CustomWizard::Template.save(@permitted_template, skip_jobs: true) - CustomWizard::Wizard.set_user_redirect('super_mega_fun_wizard', trusted_user) - expect( - trusted_user.custom_fields['redirect_to_wizard'] - ).to eq("super_mega_fun_wizard") - end - - it "does not set a wizard redirect if user is not permitted" do - CustomWizard::Template.save(@permitted_template, skip_jobs: true) - CustomWizard::Wizard.set_user_redirect('super_mega_fun_wizard', user) - expect( - trusted_user.custom_fields['redirect_to_wizard'] - ).to eq(nil) - end end diff --git a/spec/fixtures/actions/watch_categories.json b/spec/fixtures/actions/watch_categories.json new file mode 100644 index 00000000..20644f44 --- /dev/null +++ b/spec/fixtures/actions/watch_categories.json @@ -0,0 +1,23 @@ +{ + "id": "action_5", + "run_after": "step_1", + "type": "watch_categories", + "notification_level": "tracking", + "wizard_user": true, + "categories": [ + { + "type": "assignment", + "output": "", + "output_type": "text", + "output_connector": "set" + } + ], + "mute_remainder": [ + { + "type": "assignment", + "output": "true", + "output_type": "text", + "output_connector": "set" + } + ] +} diff --git a/spec/fixtures/field/advanced_types.json b/spec/fixtures/field/advanced_types.json new file mode 100644 index 00000000..63357cdb --- /dev/null +++ b/spec/fixtures/field/advanced_types.json @@ -0,0 +1,27 @@ +{ + "fields": [ + { + "id": "step_3_field_2", + "label": "Tag", + "type": "tag" + }, + { + "id": "step_3_field_3", + "label": "Category", + "type": "category", + "limit": 1, + "property": "id" + }, + { + "id": "step_3_field_4", + "label": "Group", + "type": "group" + }, + { + "id": "step_3_field_5", + "label": "User Selector", + "description": "", + "type": "user_selector" + } + ] +} diff --git a/spec/fixtures/field/composer_preview.json b/spec/fixtures/field/composer_preview.json new file mode 100644 index 00000000..f4bec04a --- /dev/null +++ b/spec/fixtures/field/composer_preview.json @@ -0,0 +1,7 @@ +{ + "id": "step_1_field_5", + "label": "I'm a preview", + "description": "", + "type": "composer_preview", + "preview_template": "w{step_1_field_1}" +} diff --git a/spec/fixtures/field/url.json b/spec/fixtures/field/url.json new file mode 100644 index 00000000..e3153fe9 --- /dev/null +++ b/spec/fixtures/field/url.json @@ -0,0 +1,5 @@ +{ + "id": "step_2_field_6", + "label": "Url", + "type": "url" +} diff --git a/spec/fixtures/subscription_client.rb b/spec/fixtures/subscription_client.rb new file mode 100644 index 00000000..a041b507 --- /dev/null +++ b/spec/fixtures/subscription_client.rb @@ -0,0 +1,4 @@ +# frozen_string_literal: true + +module SubscriptionClient; end +class SubscriptionClientSubscription; end diff --git a/spec/fixtures/wizard.json b/spec/fixtures/wizard.json index a05c1782..de5e636e 100644 --- a/spec/fixtures/wizard.json +++ b/spec/fixtures/wizard.json @@ -33,23 +33,11 @@ "type": "textarea", "min_length": "" }, - { - "id": "step_1_field_3", - "label": "Composer", - "type": "composer" - }, { "id": "step_1_field_4", "label": "I'm only text", "description": "", "type": "text_only" - }, - { - "id": "step_1_field_5", - "label": "I'm a preview", - "description": "", - "type": "composer_preview", - "preview_template": "w{step_1_field_1}" } ], "description": "Text inputs!" @@ -87,11 +75,6 @@ "label": "Checkbox", "type": "checkbox" }, - { - "id": "step_2_field_6", - "label": "Url", - "type": "url" - }, { "id": "step_2_field_7", "label": "Upload", @@ -141,35 +124,6 @@ ] } ] - }, - { - "id": "step_3_field_2", - "label": "Tag", - "type": "tag" - }, - { - "id": "step_3_field_3", - "label": "Category", - "type": "category", - "limit": 1, - "property": "id" - }, - { - "id": "step_3_field_4", - "label": "Group", - "type": "group" - }, - { - "id": "step_3_field_5", - "label": "User Selector", - "description": "", - "type": "user_selector" - }, - { - "id": "step_3_field_6", - "label": "Conditional User Selector", - "description": "Shown when checkbox in step_2_field_5 is true", - "type": "user_selector" } ], "description": "Unfortunately not the edible type :sushi: " @@ -264,29 +218,6 @@ } ] }, - { - "id": "action_5", - "run_after": "step_1", - "type": "watch_categories", - "notification_level": "tracking", - "wizard_user": true, - "categories": [ - { - "type": "assignment", - "output": "action_8", - "output_type": "wizard_action", - "output_connector": "set" - } - ], - "mute_remainder": [ - { - "type": "assignment", - "output": "true", - "output_type": "text", - "output_connector": "set" - } - ] - }, { "id": "action_4", "run_after": "step_2", diff --git a/spec/plugin_helper.rb b/spec/plugin_helper.rb index f7818296..a0189de1 100644 --- a/spec/plugin_helper.rb +++ b/spec/plugin_helper.rb @@ -7,3 +7,9 @@ def get_wizard_fixture(path) ).read ).with_indifferent_access end + +def enable_subscription(type) + CustomWizard::Subscription.stubs(:client_installed?).returns(true) + CustomWizard::Subscription.stubs("#{type}?".to_sym).returns(true) + CustomWizard::Subscription.any_instance.stubs("#{type}?".to_sym).returns(true) +end diff --git a/spec/requests/custom_wizard/admin/wizard_controller_spec.rb b/spec/requests/custom_wizard/admin/wizard_controller_spec.rb index fd2fa006..9f63cb6b 100644 --- a/spec/requests/custom_wizard/admin/wizard_controller_spec.rb +++ b/spec/requests/custom_wizard/admin/wizard_controller_spec.rb @@ -8,6 +8,7 @@ describe CustomWizard::AdminWizardController do before do CustomWizard::Template.save(template, skip_jobs: true) + enable_subscription("standard") template_2 = template.dup template_2["id"] = 'super_mega_fun_wizard_2' diff --git a/spec/requests/custom_wizard/steps_controller_spec.rb b/spec/requests/custom_wizard/steps_controller_spec.rb index 26ba817a..e05ba917 100644 --- a/spec/requests/custom_wizard/steps_controller_spec.rb +++ b/spec/requests/custom_wizard/steps_controller_spec.rb @@ -34,6 +34,7 @@ describe CustomWizard::StepsController do end it "when the user cant access the wizard" do + enable_subscription("standard") new_template = wizard_template.dup new_template["permitted"] = permitted_json["permitted"] CustomWizard::Template.save(new_template, skip_jobs: true) diff --git a/spec/requests/custom_wizard/wizard_controller_spec.rb b/spec/requests/custom_wizard/wizard_controller_spec.rb index 3d081b8a..aa1f479b 100644 --- a/spec/requests/custom_wizard/wizard_controller_spec.rb +++ b/spec/requests/custom_wizard/wizard_controller_spec.rb @@ -40,14 +40,15 @@ describe CustomWizard::WizardController do end it 'lets user skip if user cant access wizard' do + enable_subscription("standard") @template["permitted"] = permitted_json["permitted"] CustomWizard::Template.save(@template, skip_jobs: true) - put '/w/super-mega-fun-wizard/skip.json' expect(response.status).to eq(200) end it 'returns a no skip message if user is not allowed to skip' do + enable_subscription("standard") @template['required'] = 'true' CustomWizard::Template.save(@template) put '/w/super-mega-fun-wizard/skip.json' diff --git a/spec/serializers/custom_wizard/wizard_field_serializer_spec.rb b/spec/serializers/custom_wizard/wizard_field_serializer_spec.rb index c30279b7..1ac2579e 100644 --- a/spec/serializers/custom_wizard/wizard_field_serializer_spec.rb +++ b/spec/serializers/custom_wizard/wizard_field_serializer_spec.rb @@ -19,7 +19,7 @@ describe CustomWizard::FieldSerializer do expect(json_array.size).to eq(@wizard.steps.first.fields.size) expect(json_array[0][:label]).to eq("

Text

") expect(json_array[0][:description]).to eq("Text field description.") - expect(json_array[3][:index]).to eq(3) + expect(json_array[2][:index]).to eq(2) end it "should return optional field attributes" do @@ -29,6 +29,6 @@ describe CustomWizard::FieldSerializer do scope: Guardian.new(user) ).as_json expect(json_array[0][:format]).to eq("YYYY-MM-DD") - expect(json_array[6][:file_types]).to eq(".jpg,.jpeg,.png") + expect(json_array[5][:file_types]).to eq(".jpg,.jpeg,.png") end end diff --git a/spec/serializers/custom_wizard/wizard_serializer_spec.rb b/spec/serializers/custom_wizard/wizard_serializer_spec.rb index 030f109e..526f5259 100644 --- a/spec/serializers/custom_wizard/wizard_serializer_spec.rb +++ b/spec/serializers/custom_wizard/wizard_serializer_spec.rb @@ -5,6 +5,7 @@ describe CustomWizard::WizardSerializer do fab!(:category) { Fabricate(:category) } let(:template) { get_wizard_fixture("wizard") } let(:similar_topics_validation) { get_wizard_fixture("field/validation/similar_topics") } + let(:advanced_fields) { get_wizard_fixture("field/advanced_types") } before do CustomWizard::Template.save(template, skip_jobs: true) @@ -52,43 +53,51 @@ describe CustomWizard::WizardSerializer do expect(json[:wizard][:uncategorized_category_id].present?).to eq(false) end - it "should return categories if there is a category selector field" do - json = CustomWizard::WizardSerializer.new( - CustomWizard::Builder.new(@template[:id], user).build, - scope: Guardian.new(user) - ).as_json - expect(json[:wizard][:categories].present?).to eq(true) - expect(json[:wizard][:uncategorized_category_id].present?).to eq(true) - end + context "advanced fields" do + before do + enable_subscription("standard") + @template[:steps][0][:fields].push(*advanced_fields['fields']) + CustomWizard::Template.save(@template, skip_jobs: true) + end - it "should return categories if there is a similar topics validation scoped to category(s)" do - @template[:steps][0][:fields][0][:validations] = similar_topics_validation[:validations] - CustomWizard::Template.save(@template) + it "should return categories if there is a category selector field" do + json = CustomWizard::WizardSerializer.new( + CustomWizard::Builder.new(@template[:id], user).build, + scope: Guardian.new(user) + ).as_json + expect(json[:wizard][:categories].present?).to eq(true) + expect(json[:wizard][:uncategorized_category_id].present?).to eq(true) + end - json = CustomWizard::WizardSerializer.new( - CustomWizard::Builder.new(@template[:id], user).build, - scope: Guardian.new(user) - ).as_json - expect(json[:wizard][:categories].present?).to eq(true) - expect(json[:wizard][:uncategorized_category_id].present?).to eq(true) - end + it "should return categories if there is a similar topics validation scoped to category(s)" do + @template[:steps][0][:fields][0][:validations] = similar_topics_validation[:validations] + CustomWizard::Template.save(@template) - it 'should return groups if there is a group selector field' do - json = CustomWizard::WizardSerializer.new( - CustomWizard::Builder.new(@template[:id], user).build, - scope: Guardian.new(user) - ).as_json - expect(json[:wizard][:groups].length).to eq(8) - end + json = CustomWizard::WizardSerializer.new( + CustomWizard::Builder.new(@template[:id], user).build, + scope: Guardian.new(user) + ).as_json + expect(json[:wizard][:categories].present?).to eq(true) + expect(json[:wizard][:uncategorized_category_id].present?).to eq(true) + end - it 'should not return groups if there is not a group selector field' do - @template[:steps][2][:fields].delete_at(3) - CustomWizard::Template.save(@template) + it 'should return groups if there is a group selector field' do + json = CustomWizard::WizardSerializer.new( + CustomWizard::Builder.new(@template[:id], user).build, + scope: Guardian.new(user) + ).as_json + expect(json[:wizard][:groups].length).to eq(8) + end - json = CustomWizard::WizardSerializer.new( - CustomWizard::Builder.new(@template[:id], user).build, - scope: Guardian.new(user) - ).as_json - expect(json[:wizard][:groups].present?).to eq(false) + it 'should not return groups if there is not a group selector field' do + @template[:steps][0][:fields].reject! { |f| f["type"] === "group" } + CustomWizard::Template.save(@template) + + json = CustomWizard::WizardSerializer.new( + CustomWizard::Builder.new(@template[:id], user).build, + scope: Guardian.new(user) + ).as_json + expect(json[:wizard][:groups].present?).to eq(false) + end end end From a0534404663c7f3d2b3b4d575a2bcb6d716e0cb4 Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Tue, 29 Mar 2022 18:09:12 +0200 Subject: [PATCH 119/160] Update plugin.rb --- plugin.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugin.rb b/plugin.rb index ebd5c6d5..cb79f052 100644 --- a/plugin.rb +++ b/plugin.rb @@ -13,10 +13,11 @@ register_asset 'stylesheets/admin/admin.scss', :desktop enabled_site_setting :custom_wizard_enabled if Rails.env.production? - config.assets.precompile += %w{ + Rails.application.config.assets.precompile += %w{ wizard-custom-guest.js wizard-custom-start.js wizard-custom.js + wizard-qunit.js wizard-plugin.js.erb wizard-raw-templates.js.erb } From 466c7a7a496963ad00236ea7393fe1f000a15c81 Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Tue, 29 Mar 2022 20:17:59 +0200 Subject: [PATCH 120/160] Explicitly load asset paths for tests --- plugin.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/plugin.rb b/plugin.rb index cb79f052..4c941d48 100644 --- a/plugin.rb +++ b/plugin.rb @@ -23,6 +23,13 @@ if Rails.env.production? } end +if Rails.env.test? + config = Rails.application.config + plugin_asset_path = "#{Rails.root}/plugins/discourse-custom-wizard/assets" + config.assets.paths << "#{plugin_asset_path}/javascripts" + config.assets.paths << "#{plugin_asset_path}/stylesheets/wizard" +end + if respond_to?(:register_svg_icon) register_svg_icon "far-calendar" register_svg_icon "chevron-right" From 1296d3bff8dbed2ea7dc1b8f099f415c1136ed5c Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Tue, 29 Mar 2022 21:15:09 +0200 Subject: [PATCH 121/160] Update action usage --- .../discourse/templates/admin-wizards-api.hbs | 2 +- .../templates/admin-wizards-custom-fields.hbs | 2 +- .../discourse/templates/admin-wizards-logs-show.hbs | 2 +- .../discourse/templates/admin-wizards-wizard-show.hbs | 4 ++-- .../discourse/templates/admin-wizards-wizard.hbs | 2 +- .../templates/components/custom-field-input.hbs | 8 ++++---- .../templates/components/wizard-custom-action.hbs | 2 +- .../templates/components/wizard-custom-field.hbs | 2 +- .../discourse/templates/components/wizard-links.hbs | 11 +++++------ .../discourse/templates/components/wizard-mapper.hbs | 2 +- .../templates/components/wizard-text-editor.hbs | 4 ++-- .../templates/modal/next-session-scheduled.hbs | 2 +- 12 files changed, 21 insertions(+), 22 deletions(-) diff --git a/assets/javascripts/discourse/templates/admin-wizards-api.hbs b/assets/javascripts/discourse/templates/admin-wizards-api.hbs index 00d8ad60..f5e3214d 100644 --- a/assets/javascripts/discourse/templates/admin-wizards-api.hbs +++ b/assets/javascripts/discourse/templates/admin-wizards-api.hbs @@ -8,7 +8,7 @@ )}} {{d-button - action="createApi" + action=(action "createApi") label="admin.wizard.api.create" icon="plus"}}
diff --git a/assets/javascripts/discourse/templates/admin-wizards-custom-fields.hbs b/assets/javascripts/discourse/templates/admin-wizards-custom-fields.hbs index 10501498..a90f6299 100644 --- a/assets/javascripts/discourse/templates/admin-wizards-custom-fields.hbs +++ b/assets/javascripts/discourse/templates/admin-wizards-custom-fields.hbs @@ -5,7 +5,7 @@ {{d-button label="admin.wizard.custom_field.add" icon="plus" - action="addField"}} + action=(action "addField")}}
diff --git a/assets/javascripts/discourse/templates/admin-wizards-logs-show.hbs b/assets/javascripts/discourse/templates/admin-wizards-logs-show.hbs index 898198d1..270d5c21 100644 --- a/assets/javascripts/discourse/templates/admin-wizards-logs-show.hbs +++ b/assets/javascripts/discourse/templates/admin-wizards-logs-show.hbs @@ -8,7 +8,7 @@ {{d-button label="refresh" icon="sync" - action="refresh" + action=(action "refresh") class="refresh"}}
diff --git a/assets/javascripts/discourse/templates/admin-wizards-wizard-show.hbs b/assets/javascripts/discourse/templates/admin-wizards-wizard-show.hbs index 5d1c2166..8dfb4aac 100644 --- a/assets/javascripts/discourse/templates/admin-wizards-wizard-show.hbs +++ b/assets/javascripts/discourse/templates/admin-wizards-wizard-show.hbs @@ -101,7 +101,7 @@ {{input type="checkbox" checked=wizard.after_time}} {{i18n "admin.wizard.after_time_label"}} {{d-button - action="setNextSessionScheduled" + action=(action "setNextSessionScheduled") translatedLabel=nextSessionScheduledLabel class="btn-after-time" icon="far-calendar"}} @@ -176,7 +176,7 @@ currentActionId=currentAction.id wizard=wizard apis=apis - removeAction="removeAction" + removeAction=(action "removeAction") wizardFields=wizardFields}} {{/each}} diff --git a/assets/javascripts/discourse/templates/admin-wizards-wizard.hbs b/assets/javascripts/discourse/templates/admin-wizards-wizard.hbs index 081cd5f3..11b0cd11 100644 --- a/assets/javascripts/discourse/templates/admin-wizards-wizard.hbs +++ b/assets/javascripts/discourse/templates/admin-wizards-wizard.hbs @@ -8,7 +8,7 @@ )}} {{d-button - action="createWizard" + action=(action "createWizard") label="admin.wizard.create" icon="plus"}}
diff --git a/assets/javascripts/discourse/templates/components/custom-field-input.hbs b/assets/javascripts/discourse/templates/components/custom-field-input.hbs index 335a9b9d..c0bdaaff 100644 --- a/assets/javascripts/discourse/templates/components/custom-field-input.hbs +++ b/assets/javascripts/discourse/templates/components/custom-field-input.hbs @@ -42,17 +42,17 @@ {{/if}} {{/if}} {{d-button - action="destroy" + action=(action "destroy") icon="trash-alt" class="destroy" disabled=destroyDisabled}} {{d-button icon="save" - action="save" + action=(action "save") disabled=saveDisabled class="save"}} {{d-button - action="close" + action=(action "close") icon="times" disabled=closeDisabled}} @@ -77,7 +77,7 @@ {{else}} - {{d-button action="edit" icon="pencil-alt"}} + {{d-button action=(action "edit") icon="pencil-alt"}} {{/if}} {{/if}} diff --git a/assets/javascripts/discourse/templates/components/wizard-custom-action.hbs b/assets/javascripts/discourse/templates/components/wizard-custom-action.hbs index 818c525d..be9c14c5 100644 --- a/assets/javascripts/discourse/templates/components/wizard-custom-action.hbs +++ b/assets/javascripts/discourse/templates/components/wizard-custom-action.hbs @@ -1,6 +1,6 @@ {{#if showUndo}} {{d-button - action="undoChanges" + action=(action "undoChanges") icon=undoIcon label=undoKey class="undo-changes"}} diff --git a/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs b/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs index 00c20153..3bb32c05 100644 --- a/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs +++ b/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs @@ -1,6 +1,6 @@ {{#if showUndo}} {{d-button - action="undoChanges" + action=(action "undoChanges") icon=undoIcon label=undoKey class="undo-changes"}} diff --git a/assets/javascripts/discourse/templates/components/wizard-links.hbs b/assets/javascripts/discourse/templates/components/wizard-links.hbs index a7a7662e..368acd35 100644 --- a/assets/javascripts/discourse/templates/components/wizard-links.hbs +++ b/assets/javascripts/discourse/templates/components/wizard-links.hbs @@ -4,17 +4,16 @@ {{#if anyLinks}} {{#each links as |link|}}
- {{d-button action="change" actionParam=link.id translatedLabel=link.label class=link.classes}} + {{d-button action=(action "change") actionParam=link.id translatedLabel=link.label class=link.classes}} {{#unless link.first}} - {{d-button action="back" actionParam=link icon="arrow-left" class="back"}} + {{d-button action=(action "back") actionParam=link icon="arrow-left" class="back"}} {{/unless}} {{#unless link.last}} - {{d-button action="forward" actionParam=link icon="arrow-right" class="forward"}} + {{d-button action=(action "forward") actionParam=link icon="arrow-right" class="forward"}} {{/unless}} - {{d-button action="remove" actionParam=link.id icon="times" class="remove"}} + {{d-button action=(action "remove") actionParam=link.id icon="times" class="remove"}}
{{/each}} {{/if}} - {{d-button action="add" label="admin.wizard.add" icon="plus"}} + {{d-button action=(action "add") label="admin.wizard.add" icon="plus"}}
- diff --git a/assets/javascripts/discourse/templates/components/wizard-mapper.hbs b/assets/javascripts/discourse/templates/components/wizard-mapper.hbs index 2de35e0d..c0cc6818 100644 --- a/assets/javascripts/discourse/templates/components/wizard-mapper.hbs +++ b/assets/javascripts/discourse/templates/components/wizard-mapper.hbs @@ -15,6 +15,6 @@ {{#if canAdd}} - {{d-button action="add" label="admin.wizard.add" icon="plus"}} + {{d-button action=(action "add") label="admin.wizard.add" icon="plus"}} {{/if}} diff --git a/assets/javascripts/discourse/templates/components/wizard-text-editor.hbs b/assets/javascripts/discourse/templates/components/wizard-text-editor.hbs index c657049d..37a3e549 100644 --- a/assets/javascripts/discourse/templates/components/wizard-text-editor.hbs +++ b/assets/javascripts/discourse/templates/components/wizard-text-editor.hbs @@ -6,13 +6,13 @@
{{#if previewEnabled}} {{d-button - action="togglePreview" + action=(action "togglePreview") translatedLabel=previewLabel}} {{/if}} {{#if fieldsEnabled}} {{d-button - action="togglePopover" + action=(action "togglePopover") translatedLabel=popoverLabel}} {{#if showPopover}} diff --git a/assets/javascripts/discourse/templates/modal/next-session-scheduled.hbs b/assets/javascripts/discourse/templates/modal/next-session-scheduled.hbs index 1b138360..cbc9d610 100644 --- a/assets/javascripts/discourse/templates/modal/next-session-scheduled.hbs +++ b/assets/javascripts/discourse/templates/modal/next-session-scheduled.hbs @@ -9,7 +9,7 @@ From ef22c1d33dfd328e6c190a7b234d702334c25b0a Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Fri, 8 Apr 2022 16:35:40 +0200 Subject: [PATCH 125/160] Increase qunit timeout --- .github/workflows/plugin-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/plugin-tests.yml b/.github/workflows/plugin-tests.yml index 19524973..68c482ee 100644 --- a/.github/workflows/plugin-tests.yml +++ b/.github/workflows/plugin-tests.yml @@ -139,5 +139,5 @@ jobs: - name: Plugin QUnit if: matrix.build_type == 'frontend' - run: QUNIT_SKIP_CORE=1 LOAD_PLUGINS=1 QUNIT_EMBER_CLI=0 bundle exec rake "qunit:test['600000','/w/qunit']" + run: QUNIT_SKIP_CORE=1 LOAD_PLUGINS=1 QUNIT_EMBER_CLI=0 bundle exec rake "qunit:test['1200000','/w/qunit']" timeout-minutes: 30 From 9e8878e17457d298bd54674adf185dfcdbf3c203 Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Fri, 8 Apr 2022 16:50:49 +0200 Subject: [PATCH 126/160] Move to new workflow approach (based on updated Discourse approach) --- .github/workflows/plugin-tests.yml | 104 ++++++++++++++++------------- 1 file changed, 59 insertions(+), 45 deletions(-) diff --git a/.github/workflows/plugin-tests.yml b/.github/workflows/plugin-tests.yml index 68c482ee..b0fc7aec 100644 --- a/.github/workflows/plugin-tests.yml +++ b/.github/workflows/plugin-tests.yml @@ -4,23 +4,26 @@ on: push: branches: - stable - - master - main pull_request: schedule: - cron: '0 */12 * * *' +concurrency: + group: plugin-tests-${{ format('{0}-{1}', github.head_ref || github.run_number, github.job) }} + cancel-in-progress: true + jobs: build: name: ${{ matrix.build_type }} runs-on: ubuntu-latest - timeout-minutes: 60 + container: discourse/discourse_test:slim${{ startsWith(matrix.build_type, 'frontend') && '-browsers' || '' }} + timeout-minutes: 30 env: DISCOURSE_HOSTNAME: www.example.com RUBY_GLOBAL_METHOD_CACHE_SIZE: 131072 RAILS_ENV: test - PGHOST: localhost PGUSER: discourse PGPASSWORD: discourse @@ -29,24 +32,6 @@ jobs: matrix: build_type: ["backend", "frontend"] - ruby: ["2.7"] - postgres: ["12"] - redis: ["6.x"] - - services: - postgres: - image: postgres:${{ matrix.postgres }} - ports: - - 5432:5432 - env: - POSTGRES_USER: discourse - POSTGRES_PASSWORD: discourse - options: >- - --mount type=tmpfs,destination=/var/lib/postgresql/data - --health-cmd pg_isready - --health-interval 10s - --health-timeout 5s - --health-retries 5 steps: - uses: haya14busa/action-cond@v1 @@ -78,29 +63,32 @@ jobs: git config --global user.email "ci@ci.invalid" git config --global user.name "Discourse CI" - - name: Setup packages + - name: Start redis run: | - sudo apt-get update - sudo apt-get -yqq install postgresql-client libpq-dev gifsicle jpegoptim optipng jhead - wget -qO- https://raw.githubusercontent.com/discourse/discourse_docker/master/image/base/install-pngquant | sudo sh + redis-server /etc/redis/redis.conf & - - name: Update imagemagick - if: matrix.build_type == 'backend' + - name: Start Postgres run: | - wget https://raw.githubusercontent.com/discourse/discourse_docker/master/image/base/install-imagemagick - chmod +x install-imagemagick - sudo ./install-imagemagick + chown -R postgres /var/run/postgresql + sudo -E -u postgres script/start_test_db.rb + sudo -u postgres psql -c "CREATE ROLE $PGUSER LOGIN SUPERUSER PASSWORD '$PGPASSWORD';" - - name: Setup redis - uses: shogo82148/actions-setup-redis@v1 + - name: Bundler cache + uses: actions/cache@v3 with: - redis-version: ${{ matrix.redis }} + path: vendor/bundle + key: ${{ runner.os }}-gem-${{ hashFiles('**/Gemfile.lock') }} + restore-keys: | + ${{ runner.os }}-gem- - - name: Setup ruby - uses: ruby/setup-ruby@v1 - with: - ruby-version: ${{ matrix.ruby }} - bundler-cache: true + - name: Setup gems + run: | + gem install bundler --conservative -v $(awk '/BUNDLED WITH/ { getline; gsub(/ /,""); print $0 }' Gemfile.lock) + bundle config --local path vendor/bundle + bundle config --local deployment true + bundle config --local without development + bundle install --jobs 4 + bundle clean - name: Lint English locale if: matrix.build_type == 'backend' @@ -111,23 +99,49 @@ jobs: run: echo "::set-output name=dir::$(yarn cache dir)" - name: Yarn cache - uses: actions/cache@v2 + uses: actions/cache@v3 id: yarn-cache with: path: ${{ steps.yarn-cache-dir.outputs.dir }} - key: ${{ runner.os }}-${{ matrix.os }}-yarn-${{ hashFiles('**/yarn.lock') }} + key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} restore-keys: | - ${{ runner.os }}-${{ matrix.os }}-yarn- + ${{ runner.os }}-yarn- - name: Yarn install - run: yarn install --dev + run: yarn install - - name: Migrate database + - name: Fetch app state cache + uses: actions/cache@v3 + id: app-cache + with: + path: tmp/app-cache + key: >- + ${{ hashFiles('.github/workflows/tests.yml') }}- + ${{ hashFiles('db/**/*', 'plugins/**/db/**/*') }}- + + - name: Restore database from cache + if: steps.app-cache.outputs.cache-hit == 'true' + run: psql -f tmp/app-cache/cache.sql postgres + + - name: Restore uploads from cache + if: steps.app-cache.outputs.cache-hit == 'true' + run: rm -rf public/uploads && cp -r tmp/app-cache/uploads public/uploads + + - name: Create and migrate database + if: steps.app-cache.outputs.cache-hit != 'true' run: | bin/rake db:create bin/rake db:migrate - - name: Plugin RSpec with Coverage + - name: Dump database for cache + if: steps.app-cache.outputs.cache-hit != 'true' + run: mkdir -p tmp/app-cache && pg_dumpall > tmp/app-cache/cache.sql + + - name: Dump uploads for cache + if: steps.app-cache.outputs.cache-hit != 'true' + run: rm -rf tmp/app-cache/uploads && cp -r public/uploads tmp/app-cache/uploads + + - name: RSpec with Coverage if: matrix.build_type == 'backend' run: | if [ -e plugins/${{ steps.repo-name.outputs.value }}/.simplecov ] @@ -137,7 +151,7 @@ jobs: fi bin/rake plugin:spec[${{ steps.repo-name.outputs.value }}] - - name: Plugin QUnit + - name: Wizard QUnit if: matrix.build_type == 'frontend' run: QUNIT_SKIP_CORE=1 LOAD_PLUGINS=1 QUNIT_EMBER_CLI=0 bundle exec rake "qunit:test['1200000','/w/qunit']" timeout-minutes: 30 From b57bf8e0fe012f3bc06c1fe6230f03f09ff3af76 Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Mon, 11 Apr 2022 09:51:01 +0200 Subject: [PATCH 127/160] tweak qunit format --- .github/workflows/plugin-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/plugin-tests.yml b/.github/workflows/plugin-tests.yml index b0fc7aec..24cb326d 100644 --- a/.github/workflows/plugin-tests.yml +++ b/.github/workflows/plugin-tests.yml @@ -153,5 +153,5 @@ jobs: - name: Wizard QUnit if: matrix.build_type == 'frontend' - run: QUNIT_SKIP_CORE=1 LOAD_PLUGINS=1 QUNIT_EMBER_CLI=0 bundle exec rake "qunit:test['1200000','/w/qunit']" + run: QUNIT_SKIP_CORE=1 LOAD_PLUGINS=1 QUNIT_EMBER_CLI=0 bin/rake qunit:test['1200000','/w/qunit'] timeout-minutes: 30 From 84d21f78e5df723de8d842f47608e1c562c09db8 Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Mon, 11 Apr 2022 10:01:28 +0200 Subject: [PATCH 128/160] Udpate version --- plugin.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin.rb b/plugin.rb index 4c941d48..176bb019 100644 --- a/plugin.rb +++ b/plugin.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true # name: discourse-custom-wizard # about: Create custom wizards for topic creation, onboarding, user surveys and much more. -# version: 1.19.0 +# version: 2.0.0.beta # authors: Angus McLeod, Faizaan Gagan, Robert Barrow, Keegan George # contact_emails: support@thepavilion.io # url: https://github.com/paviliondev/discourse-custom-wizard From 305f94e9d9e30d7cb8cbccfd55986188dac080bf Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Mon, 11 Apr 2022 11:02:21 +0200 Subject: [PATCH 129/160] Minor config updates --- .github/workflows/plugin-tests.yml | 10 +- .../javascripts/wizard/tests/bootstrap.js.es6 | 7 + yarn.lock | 1639 +++++++++++++++++ 3 files changed, 1649 insertions(+), 7 deletions(-) create mode 100644 yarn.lock diff --git a/.github/workflows/plugin-tests.yml b/.github/workflows/plugin-tests.yml index 24cb326d..94357cb7 100644 --- a/.github/workflows/plugin-tests.yml +++ b/.github/workflows/plugin-tests.yml @@ -41,20 +41,16 @@ jobs: if_true: "stable" if_false: "tests-passed" - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: repository: discourse/discourse ref: ${{ steps.discourse_branch.outputs.value }} fetch-depth: 1 - - name: Fetch Repo Name - id: repo-name - run: echo "::set-output name=value::$(echo '${{ github.repository }}' | awk -F '/' '{print $2}')" - - name: Install plugin uses: actions/checkout@v2 with: - path: plugins/${{ steps.repo-name.outputs.value }} + path: plugins/${{ github.event.repository.name }} ref: "${{ github.base_ref }}" fetch-depth: 1 @@ -154,4 +150,4 @@ jobs: - name: Wizard QUnit if: matrix.build_type == 'frontend' run: QUNIT_SKIP_CORE=1 LOAD_PLUGINS=1 QUNIT_EMBER_CLI=0 bin/rake qunit:test['1200000','/w/qunit'] - timeout-minutes: 30 + timeout-minutes: 10 diff --git a/assets/javascripts/wizard/tests/bootstrap.js.es6 b/assets/javascripts/wizard/tests/bootstrap.js.es6 index dec0c4b4..29cdfe80 100644 --- a/assets/javascripts/wizard/tests/bootstrap.js.es6 +++ b/assets/javascripts/wizard/tests/bootstrap.js.es6 @@ -1,4 +1,5 @@ // discourse-skip-module +/*global document, Logster, QUnit */ if (window.location.pathname.indexOf("/w/") > -1 && Ember.testing) { document.addEventListener("DOMContentLoaded", function () { @@ -16,4 +17,10 @@ if (window.location.pathname.indexOf("/w/") > -1 && Ember.testing) { requirejs(entry); } }); + + if (window.Logster) { + Logster.enabled = false; + } else { + window.Logster = { enabled: false }; + } } diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 00000000..b9d7fc0e --- /dev/null +++ b/yarn.lock @@ -0,0 +1,1639 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.7.tgz#44416b6bd7624b998f5b1af5d470856c40138789" + integrity sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg== + dependencies: + "@babel/highlight" "^7.16.7" + +"@babel/generator@^7.17.9": + version "7.17.9" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.17.9.tgz#f4af9fd38fa8de143c29fce3f71852406fc1e2fc" + integrity sha512-rAdDousTwxbIxbz5I7GEQ3lUip+xVCXooZNbsydCWs3xA7ZsYOv+CFRdzGxRX78BmQHu9B1Eso59AOZQOJDEdQ== + dependencies: + "@babel/types" "^7.17.0" + jsesc "^2.5.1" + source-map "^0.5.0" + +"@babel/helper-environment-visitor@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz#ff484094a839bde9d89cd63cba017d7aae80ecd7" + integrity sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag== + dependencies: + "@babel/types" "^7.16.7" + +"@babel/helper-function-name@^7.17.9": + version "7.17.9" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.17.9.tgz#136fcd54bc1da82fcb47565cf16fd8e444b1ff12" + integrity sha512-7cRisGlVtiVqZ0MW0/yFB4atgpGLWEHUVYnb448hZK4x+vih0YO5UoS11XIYtZYqHd0dIPMdUSv8q5K4LdMnIg== + dependencies: + "@babel/template" "^7.16.7" + "@babel/types" "^7.17.0" + +"@babel/helper-hoist-variables@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz#86bcb19a77a509c7b77d0e22323ef588fa58c246" + integrity sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg== + dependencies: + "@babel/types" "^7.16.7" + +"@babel/helper-split-export-declaration@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz#0b648c0c42da9d3920d85ad585f2778620b8726b" + integrity sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw== + dependencies: + "@babel/types" "^7.16.7" + +"@babel/helper-validator-identifier@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz#e8c602438c4a8195751243da9031d1607d247cad" + integrity sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw== + +"@babel/highlight@^7.16.7": + version "7.17.9" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.17.9.tgz#61b2ee7f32ea0454612def4fccdae0de232b73e3" + integrity sha512-J9PfEKCbFIv2X5bjTMiZu6Vf341N05QIY+d6FvVKynkG1S7G0j3I0QoRtWIrXhZ+/Nlb5Q0MzqL7TokEJ5BNHg== + dependencies: + "@babel/helper-validator-identifier" "^7.16.7" + chalk "^2.0.0" + js-tokens "^4.0.0" + +"@babel/parser@^7.16.7", "@babel/parser@^7.17.9", "@babel/parser@^7.7.0": + version "7.17.9" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.17.9.tgz#9c94189a6062f0291418ca021077983058e171ef" + integrity sha512-vqUSBLP8dQHFPdPi9bc5GK9vRkYHJ49fsZdtoJ8EQ8ibpwk5rPKfvNIwChB0KVXcIjcepEBBd2VHC5r9Gy8ueg== + +"@babel/template@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.16.7.tgz#8d126c8701fde4d66b264b3eba3d96f07666d155" + integrity sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w== + dependencies: + "@babel/code-frame" "^7.16.7" + "@babel/parser" "^7.16.7" + "@babel/types" "^7.16.7" + +"@babel/traverse@^7.7.0": + version "7.17.9" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.17.9.tgz#1f9b207435d9ae4a8ed6998b2b82300d83c37a0d" + integrity sha512-PQO8sDIJ8SIwipTPiR71kJQCKQYB5NGImbOviK8K+kg5xkNSYXLBupuX9QhatFowrsvo9Hj8WgArg3W7ijNAQw== + dependencies: + "@babel/code-frame" "^7.16.7" + "@babel/generator" "^7.17.9" + "@babel/helper-environment-visitor" "^7.16.7" + "@babel/helper-function-name" "^7.17.9" + "@babel/helper-hoist-variables" "^7.16.7" + "@babel/helper-split-export-declaration" "^7.16.7" + "@babel/parser" "^7.17.9" + "@babel/types" "^7.17.0" + debug "^4.1.0" + globals "^11.1.0" + +"@babel/types@^7.16.7", "@babel/types@^7.17.0", "@babel/types@^7.7.0": + version "7.17.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.17.0.tgz#a826e368bccb6b3d84acd76acad5c0d87342390b" + integrity sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw== + dependencies: + "@babel/helper-validator-identifier" "^7.16.7" + to-fast-properties "^2.0.0" + +"@ember-data/rfc395-data@^0.0.4": + version "0.0.4" + resolved "https://registry.yarnpkg.com/@ember-data/rfc395-data/-/rfc395-data-0.0.4.tgz#ecb86efdf5d7733a76ff14ea651a1b0ed1f8a843" + integrity sha512-tGRdvgC9/QMQSuSuJV45xoyhI0Pzjm7A9o/MVVA3HakXIImJbbzx/k/6dO9CUEQXIyS2y0fW6C1XaYOG7rY0FQ== + +"@glimmer/env@0.1.7", "@glimmer/env@^0.1.7": + version "0.1.7" + resolved "https://registry.yarnpkg.com/@glimmer/env/-/env-0.1.7.tgz#fd2d2b55a9029c6b37a6c935e8c8871ae70dfa07" + integrity sha1-/S0rVakCnGs3psk16MiHGucN+gc= + +"@glimmer/global-context@0.65.4": + version "0.65.4" + resolved "https://registry.yarnpkg.com/@glimmer/global-context/-/global-context-0.65.4.tgz#1da1d59dd4260ce912c40e474cd39c2e82de51b8" + integrity sha512-RSYCPG/uVR5XCDcPREBclncU7R0zkjACbADP+n3FWAH1TfWbXRMDIkvO/ZlwHkjHoCZf6tIM6p5S/MoFzfJEJA== + dependencies: + "@glimmer/env" "^0.1.7" + +"@glimmer/interfaces@0.65.4": + version "0.65.4" + resolved "https://registry.yarnpkg.com/@glimmer/interfaces/-/interfaces-0.65.4.tgz#d298cc2b12b8ebcf269f39246ca7ab92816f6680" + integrity sha512-R0kby79tGNKZOojVJa/7y0JH9Eq4SV+L1s6GcZy30QUZ1g1AAGS5XwCIXc9Sc09coGcv//q+6NLeSw7nlx1y4A== + dependencies: + "@simple-dom/interface" "^1.4.0" + +"@glimmer/reference@^0.65.0": + version "0.65.4" + resolved "https://registry.yarnpkg.com/@glimmer/reference/-/reference-0.65.4.tgz#bbc8becd6a1bf01fc189b6489e27446437194711" + integrity sha512-yuRVE4qyqrlCndDMrHKDWUbDmGDCjPzsFtlTmxxnhDMJAdQsnr2cRLITHvQRDm1tXfigVvyKnomeuYhRRbBqYQ== + dependencies: + "@glimmer/env" "^0.1.7" + "@glimmer/global-context" "0.65.4" + "@glimmer/interfaces" "0.65.4" + "@glimmer/util" "0.65.4" + "@glimmer/validator" "0.65.4" + +"@glimmer/syntax@^0.65.0": + version "0.65.4" + resolved "https://registry.yarnpkg.com/@glimmer/syntax/-/syntax-0.65.4.tgz#49164de5dc9e8b67084ec009bdd865e379d8a971" + integrity sha512-y+/C3e8w96efk3a/Z5If9o4ztKJwrr8RtDpbhV2J8X+DUsn5ic2N3IIdlThbt/Zn6tkP1K3dY6uaFUx3pGTvVQ== + dependencies: + "@glimmer/interfaces" "0.65.4" + "@glimmer/util" "0.65.4" + "@handlebars/parser" "^1.1.0" + simple-html-tokenizer "^0.5.10" + +"@glimmer/util@0.65.4": + version "0.65.4" + resolved "https://registry.yarnpkg.com/@glimmer/util/-/util-0.65.4.tgz#e464145078f3f40da9013ff2590a6016515455d2" + integrity sha512-aofe+rdBhkREKP2GZta6jy1UcbRRMfWx7M18zxGxspPoeD08NscD04Kx+WiOKXmC1TcrfITr8jvqMfrKrMzYWQ== + dependencies: + "@glimmer/env" "0.1.7" + "@glimmer/interfaces" "0.65.4" + "@simple-dom/interface" "^1.4.0" + +"@glimmer/validator@0.65.4", "@glimmer/validator@^0.65.0": + version "0.65.4" + resolved "https://registry.yarnpkg.com/@glimmer/validator/-/validator-0.65.4.tgz#12c27a9a63706c60e6499fd687940e9d1affb32c" + integrity sha512-0YUjAyo45DF5JkQxdv5kHn96nMNhvZiEwsAD4Jme0kk5Q9MQcPOUtN76pQAS4f+C6GdF9DeUr2yGXZLFMmb+LA== + dependencies: + "@glimmer/env" "^0.1.7" + "@glimmer/global-context" "0.65.4" + +"@handlebars/parser@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@handlebars/parser/-/parser-1.1.0.tgz#d6dbc7574774b238114582410e8fee0dc3532bdf" + integrity sha512-rR7tJoSwJ2eooOpYGxGGW95sLq6GXUaS1UtWvN7pei6n2/okYvCGld9vsUTvkl2migxbkszsycwtMf/GEc1k1A== + +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== + +"@nodelib/fs.walk@^1.2.3": + version "1.2.8" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" + +"@simple-dom/interface@^1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@simple-dom/interface/-/interface-1.4.0.tgz#e8feea579232017f89b0138e2726facda6fbb71f" + integrity sha512-l5qumKFWU0S+4ZzMaLXFU8tQZsicHEMEyAxI5kDFGhJsRqDwe0a7/iPA/GdxlGyDKseQQAgIz5kzU7eXTrlSpA== + +acorn-jsx@^5.2.0: + version "5.3.2" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== + +acorn@^7.1.1: + version "7.4.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" + integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== + +ajv@^6.10.0, ajv@^6.10.2: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ansi-escapes@^4.2.1: + version "4.3.2" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" + integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== + dependencies: + type-fest "^0.21.3" + +ansi-regex@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.1.tgz#164daac87ab2d6f6db3a29875e2d1766582dabed" + integrity sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g== + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-styles@^3.2.0, ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +ansi-styles@^4.0.0, ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + +array-union@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" + integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== + +astral-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" + integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== + +async-promise-queue@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/async-promise-queue/-/async-promise-queue-1.0.5.tgz#cb23bce9fce903a133946a700cc85f27f09ea49d" + integrity sha512-xi0aQ1rrjPWYmqbwr18rrSKbSaXIeIwSd1J4KAgVfkq8utNbdZoht7GfvfY6swFUAMJ9obkc4WPJmtGwl+B8dw== + dependencies: + async "^2.4.1" + debug "^2.6.8" + +async@^2.4.1: + version "2.6.3" + resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff" + integrity sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg== + dependencies: + lodash "^4.17.14" + +babel-eslint@^10.1.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-10.1.0.tgz#6968e568a910b78fb3779cdd8b6ac2f479943232" + integrity sha512-ifWaTHQ0ce+448CYop8AdrQiBsGrnC+bMgfyKFdi6EsPLTAWG+QfyDeM6OH+FmWnKvEq5NnBMLvlBUPKQZoDSg== + dependencies: + "@babel/code-frame" "^7.0.0" + "@babel/parser" "^7.7.0" + "@babel/traverse" "^7.7.0" + "@babel/types" "^7.7.0" + eslint-visitor-keys "^1.0.0" + resolve "^1.12.0" + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +base64-js@^1.3.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + +bl@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" + integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== + dependencies: + buffer "^5.5.0" + inherits "^2.0.4" + readable-stream "^3.4.0" + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + +buffer@^5.5.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.1.13" + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +chalk@^2.0.0, chalk@^2.1.0: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@^4.0.0, chalk@^4.1.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chardet@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" + integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== + +cli-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" + integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== + dependencies: + restore-cursor "^3.1.0" + +cli-spinners@^2.5.0: + version "2.6.1" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.6.1.tgz#adc954ebe281c37a6319bfa401e6dd2488ffb70d" + integrity sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g== + +cli-width@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" + integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== + +cliui@^7.0.2: + version "7.0.4" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" + integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^7.0.0" + +clone@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" + integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4= + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +colors@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" + integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== + +commander@^6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c" + integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + +cross-spawn@^6.0.5: + version "6.0.5" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" + integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== + dependencies: + nice-try "^1.0.4" + path-key "^2.0.1" + semver "^5.5.0" + shebang-command "^1.2.0" + which "^1.2.9" + +debug@^2.6.8: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@^4.0.1, debug@^4.1.0: + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + +deep-is@~0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" + integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== + +defaults@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" + integrity sha1-xlYFHpgX2f8I7YgUd/P+QBnz730= + dependencies: + clone "^1.0.2" + +dir-glob@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" + integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== + dependencies: + path-type "^4.0.0" + +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== + dependencies: + esutils "^2.0.2" + +ember-rfc176-data@^0.3.11: + version "0.3.17" + resolved "https://registry.yarnpkg.com/ember-rfc176-data/-/ember-rfc176-data-0.3.17.tgz#d4fc6c33abd6ef7b3440c107a28e04417b49860a" + integrity sha512-EVzTTKqxv9FZbEh6Ktw56YyWRAA0MijKvl7H8C06wVF+8f/cRRz3dXxa4nkwjzyVwx4rzKGuIGq77hxJAQhWWw== + +ember-template-lint-plugin-discourse@latest: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ember-template-lint-plugin-discourse/-/ember-template-lint-plugin-discourse-2.0.0.tgz#9805dff60763fae68b5df82b92fb431eb739c13e" + integrity sha512-2bPz/47OfuYGj4w2RNyDcXCYA/4JtRAXRIsaA6PTm4Uc44exK/GBd4RfT2ywmq0CImvj2kGkqpuUgkAtVf6aZQ== + +ember-template-lint@^2.11.0: + version "2.21.0" + resolved "https://registry.yarnpkg.com/ember-template-lint/-/ember-template-lint-2.21.0.tgz#7e120abf309a8810eeed26c52377943faf15a95b" + integrity sha512-19QbEqJQdMfcRS7PsQsubflRowEtnkbD0tpYR4q/xq4lodmhU7hhOFvlTQgbxD/jwW5Ur+tkOwH4KFy9JwOyXA== + dependencies: + chalk "^4.0.0" + ember-template-recast "^5.0.1" + find-up "^5.0.0" + fuse.js "^6.4.6" + get-stdin "^8.0.0" + globby "^11.0.2" + is-glob "^4.0.1" + micromatch "^4.0.2" + resolve "^1.20.0" + v8-compile-cache "^2.2.0" + yargs "^16.2.0" + +ember-template-recast@^5.0.1: + version "5.0.3" + resolved "https://registry.yarnpkg.com/ember-template-recast/-/ember-template-recast-5.0.3.tgz#79df27a70bdce7be17f14db13886afde1e9d02d6" + integrity sha512-qsJYQhf29Dk6QMfviXhUPE+byMOs6iRQxUDHgkj8yqjeppvjHaFG96hZi/NAXJTm/M7o3PpfF5YlmeaKtI9UeQ== + dependencies: + "@glimmer/reference" "^0.65.0" + "@glimmer/syntax" "^0.65.0" + "@glimmer/validator" "^0.65.0" + async-promise-queue "^1.0.5" + colors "^1.4.0" + commander "^6.2.1" + globby "^11.0.3" + ora "^5.4.0" + slash "^3.0.0" + tmp "^0.2.1" + workerpool "^6.1.4" + +emoji-regex@^7.0.1: + version "7.0.3" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" + integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== + +escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= + +eslint-config-discourse@^1.1.8: + version "1.1.9" + resolved "https://registry.yarnpkg.com/eslint-config-discourse/-/eslint-config-discourse-1.1.9.tgz#9a5ee6b3a4b986e5243f517e7945d1709c4e22df" + integrity sha512-a4KG+/9/7ZhYVV0URGK70K7QtxlydYKjie0ZssHEGxl2auOUTDcdRMBfZQBtIxQr9X8TF0+eeUUsScBXNU6xZw== + dependencies: + babel-eslint "^10.1.0" + ember-template-lint "^2.11.0" + ember-template-lint-plugin-discourse latest + eslint "^6.8.0" + eslint-plugin-discourse-ember latest + eslint-plugin-ember "^6.10.0" + eslint-plugin-lodash "^7.1.0" + eslint-plugin-node "^8.0.0" + prettier "2.2.1" + +eslint-plugin-discourse-ember@latest: + version "0.0.3" + resolved "https://registry.yarnpkg.com/eslint-plugin-discourse-ember/-/eslint-plugin-discourse-ember-0.0.3.tgz#69e5876c2ece38ab3d6c4a05f0a20a7dc4c21e37" + integrity sha512-EFwWU4FlOSLBa4RolKZL8QD1eGOnvSkACLt4Big+o1ZUIpW7gGvfnJPtxkbaQ4XmhtZ5HetYt6862vVqhUMv9A== + dependencies: + requireindex "~1.1.0" + +eslint-plugin-ember@^6.10.0: + version "6.10.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-ember/-/eslint-plugin-ember-6.10.1.tgz#ca7a5cc28b91a247c31b1686421a66281467f238" + integrity sha512-RZI0+UoR4xeD6UE3KQCUwbN2nZOIIPaFCCXqBIRXDr0rFuwvknAHqYtDPJVZicvTzNHa4TEZvAKqfbE8t7SztQ== + dependencies: + "@ember-data/rfc395-data" "^0.0.4" + ember-rfc176-data "^0.3.11" + snake-case "^2.1.0" + +eslint-plugin-es@^1.3.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-es/-/eslint-plugin-es-1.4.1.tgz#12acae0f4953e76ba444bfd1b2271081ac620998" + integrity sha512-5fa/gR2yR3NxQf+UXkeLeP8FBBl6tSgdrAz1+cF84v1FMM4twGwQoqTnn+QxFLcPOrF4pdKEJKDB/q9GoyJrCA== + dependencies: + eslint-utils "^1.4.2" + regexpp "^2.0.1" + +eslint-plugin-lodash@^7.1.0: + version "7.4.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-lodash/-/eslint-plugin-lodash-7.4.0.tgz#14a761547f126c92ff56789662a20a44f8bb6290" + integrity sha512-Tl83UwVXqe1OVeBRKUeWcfg6/pCW1GTRObbdnbEJgYwjxp5Q92MEWQaH9+dmzbRt6kvYU1Mp893E79nJiCSM8A== + dependencies: + lodash "^4.17.21" + +eslint-plugin-node@^8.0.0: + version "8.0.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-node/-/eslint-plugin-node-8.0.1.tgz#55ae3560022863d141fa7a11799532340a685964" + integrity sha512-ZjOjbjEi6jd82rIpFSgagv4CHWzG9xsQAVp1ZPlhRnnYxcTgENUVBvhYmkQ7GvT1QFijUSo69RaiOJKhMu6i8w== + dependencies: + eslint-plugin-es "^1.3.1" + eslint-utils "^1.3.1" + ignore "^5.0.2" + minimatch "^3.0.4" + resolve "^1.8.1" + semver "^5.5.0" + +eslint-scope@^5.0.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" + integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== + dependencies: + esrecurse "^4.3.0" + estraverse "^4.1.1" + +eslint-utils@^1.3.1, eslint-utils@^1.4.2, eslint-utils@^1.4.3: + version "1.4.3" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.3.tgz#74fec7c54d0776b6f67e0251040b5806564e981f" + integrity sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q== + dependencies: + eslint-visitor-keys "^1.1.0" + +eslint-visitor-keys@^1.0.0, eslint-visitor-keys@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" + integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== + +eslint@^6.8.0: + version "6.8.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-6.8.0.tgz#62262d6729739f9275723824302fb227c8c93ffb" + integrity sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig== + dependencies: + "@babel/code-frame" "^7.0.0" + ajv "^6.10.0" + chalk "^2.1.0" + cross-spawn "^6.0.5" + debug "^4.0.1" + doctrine "^3.0.0" + eslint-scope "^5.0.0" + eslint-utils "^1.4.3" + eslint-visitor-keys "^1.1.0" + espree "^6.1.2" + esquery "^1.0.1" + esutils "^2.0.2" + file-entry-cache "^5.0.1" + functional-red-black-tree "^1.0.1" + glob-parent "^5.0.0" + globals "^12.1.0" + ignore "^4.0.6" + import-fresh "^3.0.0" + imurmurhash "^0.1.4" + inquirer "^7.0.0" + is-glob "^4.0.0" + js-yaml "^3.13.1" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.3.0" + lodash "^4.17.14" + minimatch "^3.0.4" + mkdirp "^0.5.1" + natural-compare "^1.4.0" + optionator "^0.8.3" + progress "^2.0.0" + regexpp "^2.0.1" + semver "^6.1.2" + strip-ansi "^5.2.0" + strip-json-comments "^3.0.1" + table "^5.2.3" + text-table "^0.2.0" + v8-compile-cache "^2.0.3" + +espree@^6.1.2: + version "6.2.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-6.2.1.tgz#77fc72e1fd744a2052c20f38a5b575832e82734a" + integrity sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw== + dependencies: + acorn "^7.1.1" + acorn-jsx "^5.2.0" + eslint-visitor-keys "^1.1.0" + +esprima@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +esquery@^1.0.1: + version "1.4.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5" + integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w== + dependencies: + estraverse "^5.1.0" + +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^4.1.1: + version "4.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + +estraverse@^5.1.0, estraverse@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +external-editor@^3.0.3: + version "3.1.0" + resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" + integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== + dependencies: + chardet "^0.7.0" + iconv-lite "^0.4.24" + tmp "^0.0.33" + +fast-deep-equal@^3.1.1: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-glob@^3.2.9: + version "3.2.11" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.11.tgz#a1172ad95ceb8a16e20caa5c5e56480e5129c1d9" + integrity sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-levenshtein@~2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= + +fastq@^1.6.0: + version "1.13.0" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.13.0.tgz#616760f88a7526bdfc596b7cab8c18938c36b98c" + integrity sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw== + dependencies: + reusify "^1.0.4" + +figures@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" + integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== + dependencies: + escape-string-regexp "^1.0.5" + +file-entry-cache@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-5.0.1.tgz#ca0f6efa6dd3d561333fb14515065c2fafdf439c" + integrity sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g== + dependencies: + flat-cache "^2.0.1" + +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + +find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +flat-cache@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0" + integrity sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA== + dependencies: + flatted "^2.0.0" + rimraf "2.6.3" + write "1.0.3" + +flatted@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138" + integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA== + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +functional-red-black-tree@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" + integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= + +fuse.js@^6.4.6: + version "6.5.3" + resolved "https://registry.yarnpkg.com/fuse.js/-/fuse.js-6.5.3.tgz#7446c0acbc4ab0ab36fa602e97499bdb69452b93" + integrity sha512-sA5etGE7yD/pOqivZRBvUBd/NaL2sjAu6QuSaFoe1H2BrJSkH/T/UXAJ8CdXdw7DvY3Hs8CXKYkDWX7RiP5KOg== + +get-caller-file@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +get-stdin@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-8.0.0.tgz#cbad6a73feb75f6eeb22ba9e01f89aa28aa97a53" + integrity sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg== + +glob-parent@^5.0.0, glob-parent@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob@^7.1.3: + version "7.2.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" + integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +globals@^11.1.0: + version "11.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== + +globals@^12.1.0: + version "12.4.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-12.4.0.tgz#a18813576a41b00a24a97e7f815918c2e19925f8" + integrity sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg== + dependencies: + type-fest "^0.8.1" + +globby@^11.0.2, globby@^11.0.3: + version "11.1.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" + integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== + dependencies: + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.2.9" + ignore "^5.2.0" + merge2 "^1.4.1" + slash "^3.0.0" + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + +iconv-lite@^0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +ieee754@^1.1.13: + version "1.2.1" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== + +ignore@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" + integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== + +ignore@^5.0.2, ignore@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" + integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== + +import-fresh@^3.0.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" + integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@^2.0.3, inherits@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +inquirer@^7.0.0: + version "7.3.3" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.3.3.tgz#04d176b2af04afc157a83fd7c100e98ee0aad003" + integrity sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA== + dependencies: + ansi-escapes "^4.2.1" + chalk "^4.1.0" + cli-cursor "^3.1.0" + cli-width "^3.0.0" + external-editor "^3.0.3" + figures "^3.0.0" + lodash "^4.17.19" + mute-stream "0.0.8" + run-async "^2.4.0" + rxjs "^6.6.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + through "^2.3.6" + +is-core-module@^2.8.1: + version "2.8.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.1.tgz#f59fdfca701d5879d0a6b100a40aa1560ce27211" + integrity sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA== + dependencies: + has "^1.0.3" + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= + +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-glob@^4.0.0, is-glob@^4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-interactive@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" + integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-unicode-supported@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" + integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= + +js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-yaml@^3.13.1: + version "3.14.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" + integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +jsesc@^2.5.1: + version "2.5.2" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" + integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= + +levn@^0.3.0, levn@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" + +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + +lodash@^4.17.14, lodash@^4.17.19, lodash@^4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +log-symbols@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" + integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== + dependencies: + chalk "^4.1.0" + is-unicode-supported "^0.1.0" + +lower-case@^1.1.1: + version "1.1.4" + resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac" + integrity sha1-miyr0bno4K6ZOkv31YdcOcQujqw= + +lru-cache@^7.4.0: + version "7.8.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.8.1.tgz#68ee3f4807a57d2ba185b7fd90827d5c21ce82bb" + integrity sha512-E1v547OCgJvbvevfjgK9sNKIVXO96NnsTsFPBlg4ZxjhsJSODoH9lk8Bm0OxvHNm6Vm5Yqkl/1fErDxhYL8Skg== + +merge2@^1.3.0, merge2@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + +micromatch@^4.0.2, micromatch@^4.0.4: + version "4.0.5" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" + integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== + dependencies: + braces "^3.0.2" + picomatch "^2.3.1" + +mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + +minimatch@^3.0.4: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimist@^1.2.6: + version "1.2.6" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" + integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== + +mkdirp@^0.5.1: + version "0.5.6" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" + integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== + dependencies: + minimist "^1.2.6" + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +mute-stream@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" + integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= + +nice-try@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" + integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== + +no-case@^2.2.0: + version "2.3.2" + resolved "https://registry.yarnpkg.com/no-case/-/no-case-2.3.2.tgz#60b813396be39b3f1288a4c1ed5d1e7d28b464ac" + integrity sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ== + dependencies: + lower-case "^1.1.1" + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + dependencies: + wrappy "1" + +onetime@^5.1.0: + version "5.1.2" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== + dependencies: + mimic-fn "^2.1.0" + +optionator@^0.8.3: + version "0.8.3" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" + integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== + dependencies: + deep-is "~0.1.3" + fast-levenshtein "~2.0.6" + levn "~0.3.0" + prelude-ls "~1.1.2" + type-check "~0.3.2" + word-wrap "~1.2.3" + +ora@^5.4.0: + version "5.4.1" + resolved "https://registry.yarnpkg.com/ora/-/ora-5.4.1.tgz#1b2678426af4ac4a509008e5e4ac9e9959db9e18" + integrity sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ== + dependencies: + bl "^4.1.0" + chalk "^4.1.0" + cli-cursor "^3.1.0" + cli-spinners "^2.5.0" + is-interactive "^1.0.0" + is-unicode-supported "^0.1.0" + log-symbols "^4.1.0" + strip-ansi "^6.0.0" + wcwidth "^1.0.1" + +os-tmpdir@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= + +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + +path-key@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" + integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= + +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +path-type@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== + +picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +prelude-ls@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= + +prettier@2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.2.1.tgz#795a1a78dd52f073da0cd42b21f9c91381923ff5" + integrity sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q== + +progress@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" + integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== + +punycode@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + +queue-microtask@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + +readable-stream@^3.4.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" + integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +regexpp@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" + integrity sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw== + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= + +requireindex@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/requireindex/-/requireindex-1.1.0.tgz#e5404b81557ef75db6e49c5a72004893fe03e162" + integrity sha1-5UBLgVV+91225JxacgBIk/4D4WI= + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +resolve@^1.12.0, resolve@^1.20.0, resolve@^1.8.1: + version "1.22.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198" + integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw== + dependencies: + is-core-module "^2.8.1" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +restore-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" + integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== + dependencies: + onetime "^5.1.0" + signal-exit "^3.0.2" + +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== + +rimraf@2.6.3: + version "2.6.3" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" + integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== + dependencies: + glob "^7.1.3" + +rimraf@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + +run-async@^2.4.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" + integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== + +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + dependencies: + queue-microtask "^1.2.2" + +rxjs@^6.6.0: + version "6.6.7" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" + integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== + dependencies: + tslib "^1.9.0" + +safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +"safer-buffer@>= 2.1.2 < 3": + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +semver@^5.5.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + +semver@^6.1.2: + version "6.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + +semver@^7.3.5: + version "7.3.6" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.6.tgz#5d73886fb9c0c6602e79440b97165c29581cbb2b" + integrity sha512-HZWqcgwLsjaX1HBD31msI/rXktuIhS+lWvdE4kN9z+8IVT4Itc7vqU2WvYsyD6/sjYCt4dEKH/m1M3dwI9CC5w== + dependencies: + lru-cache "^7.4.0" + +shebang-command@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= + dependencies: + shebang-regex "^1.0.0" + +shebang-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= + +signal-exit@^3.0.2: + version "3.0.7" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== + +simple-html-tokenizer@^0.5.10: + version "0.5.11" + resolved "https://registry.yarnpkg.com/simple-html-tokenizer/-/simple-html-tokenizer-0.5.11.tgz#4c5186083c164ba22a7b477b7687ac056ad6b1d9" + integrity sha512-C2WEK/Z3HoSFbYq8tI7ni3eOo/NneSPRoPpcM7WdLjFOArFuyXEjAoCdOC3DgMfRyziZQ1hCNR4mrNdWEvD0og== + +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + +slice-ansi@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" + integrity sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ== + dependencies: + ansi-styles "^3.2.0" + astral-regex "^1.0.0" + is-fullwidth-code-point "^2.0.0" + +snake-case@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/snake-case/-/snake-case-2.1.0.tgz#41bdb1b73f30ec66a04d4e2cad1b76387d4d6d9f" + integrity sha1-Qb2xtz8w7GagTU4srRt2OH1NbZ8= + dependencies: + no-case "^2.2.0" + +source-map@^0.5.0: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= + +string-width@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" + integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== + dependencies: + emoji-regex "^7.0.1" + is-fullwidth-code-point "^2.0.0" + strip-ansi "^5.1.0" + +string-width@^4.1.0, string-width@^4.2.0: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +strip-ansi@^5.1.0, strip-ansi@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" + integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== + dependencies: + ansi-regex "^4.1.0" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-json-comments@^3.0.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +table@^5.2.3: + version "5.4.6" + resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e" + integrity sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug== + dependencies: + ajv "^6.10.2" + lodash "^4.17.14" + slice-ansi "^2.1.0" + string-width "^3.0.0" + +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= + +through@^2.3.6: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= + +tmp@^0.0.33: + version "0.0.33" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== + dependencies: + os-tmpdir "~1.0.2" + +tmp@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14" + integrity sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ== + dependencies: + rimraf "^3.0.0" + +to-fast-properties@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" + integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +tslib@^1.9.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" + integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== + +type-check@~0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= + dependencies: + prelude-ls "~1.1.2" + +type-fest@^0.21.3: + version "0.21.3" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" + integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== + +type-fest@^0.8.1: + version "0.8.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" + integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== + +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + +util-deprecate@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= + +v8-compile-cache@^2.0.3, v8-compile-cache@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" + integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== + +wcwidth@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" + integrity sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g= + dependencies: + defaults "^1.0.3" + +which@^1.2.9: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" + +word-wrap@~1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" + integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== + +workerpool@^6.1.4: + version "6.2.0" + resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.0.tgz#827d93c9ba23ee2019c3ffaff5c27fccea289e8b" + integrity sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A== + +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + +write@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/write/-/write-1.0.3.tgz#0800e14523b923a387e415123c865616aae0f5c3" + integrity sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig== + dependencies: + mkdirp "^0.5.1" + +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== + +yargs-parser@^20.2.2: + version "20.2.9" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" + integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== + +yargs@^16.2.0: + version "16.2.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" + integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== + dependencies: + cliui "^7.0.2" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.0" + y18n "^5.0.5" + yargs-parser "^20.2.2" + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== From 8b5b9624d74f5726ea604969d29d1599a9c911b2 Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Mon, 11 Apr 2022 11:08:07 +0200 Subject: [PATCH 130/160] Update wizard-qunit.js --- assets/javascripts/wizard-qunit.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/assets/javascripts/wizard-qunit.js b/assets/javascripts/wizard-qunit.js index 2866cb5d..8aaf23ab 100644 --- a/assets/javascripts/wizard-qunit.js +++ b/assets/javascripts/wizard-qunit.js @@ -1,11 +1,12 @@ +//= require env +//= require jquery.debug +//= require ember.debug //= require route-recognizer //= require fake_xml_http_request //= require pretender //= require qunit //= require ember-qunit //= require test-shims -//= require jquery.debug -//= require ember.debug //= require ember-template-compiler //= require_tree ./wizard/tests/fixtures From 7d941eaf5e98f18b85a83e5390a33e19c9e168a5 Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Mon, 11 Apr 2022 11:10:00 +0200 Subject: [PATCH 131/160] Update bootstrap.js.es6 --- assets/javascripts/wizard/tests/bootstrap.js.es6 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/javascripts/wizard/tests/bootstrap.js.es6 b/assets/javascripts/wizard/tests/bootstrap.js.es6 index 29cdfe80..a3343b9a 100644 --- a/assets/javascripts/wizard/tests/bootstrap.js.es6 +++ b/assets/javascripts/wizard/tests/bootstrap.js.es6 @@ -1,5 +1,5 @@ // discourse-skip-module -/*global document, Logster, QUnit */ +/*global document, Logster */ if (window.location.pathname.indexOf("/w/") > -1 && Ember.testing) { document.addEventListener("DOMContentLoaded", function () { From 1f899935a00087410942324e0eb77cf17196169e Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Mon, 11 Apr 2022 11:28:51 +0200 Subject: [PATCH 132/160] Update plugin-tests.yml --- .github/workflows/plugin-tests.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/plugin-tests.yml b/.github/workflows/plugin-tests.yml index 94357cb7..de1133e4 100644 --- a/.github/workflows/plugin-tests.yml +++ b/.github/workflows/plugin-tests.yml @@ -48,7 +48,7 @@ jobs: fetch-depth: 1 - name: Install plugin - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: path: plugins/${{ github.event.repository.name }} ref: "${{ github.base_ref }}" @@ -140,12 +140,12 @@ jobs: - name: RSpec with Coverage if: matrix.build_type == 'backend' run: | - if [ -e plugins/${{ steps.repo-name.outputs.value }}/.simplecov ] + if [ -e plugins/${{ github.event.repository.name }}/.simplecov ] then - cp plugins/${{ steps.repo-name.outputs.value }}/.simplecov .simplecov + cp plugins/${{ github.event.repository.name }}/.simplecov .simplecov export COVERAGE=1 fi - bin/rake plugin:spec[${{ steps.repo-name.outputs.value }}] + bin/rake plugin:spec[${{ github.event.repository.name }}] - name: Wizard QUnit if: matrix.build_type == 'frontend' From b0ea1081a276138ef938bbe6c5a04a0518cdcd86 Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Mon, 11 Apr 2022 11:31:11 +0200 Subject: [PATCH 133/160] Update plugin-tests.yml --- .github/workflows/plugin-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/plugin-tests.yml b/.github/workflows/plugin-tests.yml index de1133e4..c76eaff6 100644 --- a/.github/workflows/plugin-tests.yml +++ b/.github/workflows/plugin-tests.yml @@ -88,7 +88,7 @@ jobs: - name: Lint English locale if: matrix.build_type == 'backend' - run: bundle exec ruby script/i18n_lint.rb "plugins/${{ steps.repo-name.outputs.value }}/locales/{client,server}.en.yml" + run: bundle exec ruby script/i18n_lint.rb "plugins/${{ github.event.repository.name }}/locales/{client,server}.en.yml" - name: Get yarn cache directory id: yarn-cache-dir From 3c0d256e4e2db1d2cee33e35d7c12f941a33888e Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Mon, 11 Apr 2022 11:35:00 +0200 Subject: [PATCH 134/160] Update plugin.rb --- plugin.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin.rb b/plugin.rb index 176bb019..3ebde0d0 100644 --- a/plugin.rb +++ b/plugin.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true # name: discourse-custom-wizard # about: Create custom wizards for topic creation, onboarding, user surveys and much more. -# version: 2.0.0.beta +# version: 2.0.0-beta.1 # authors: Angus McLeod, Faizaan Gagan, Robert Barrow, Keegan George # contact_emails: support@thepavilion.io # url: https://github.com/paviliondev/discourse-custom-wizard From cebc63b6d87a0024d761b7d32f2a25fdd3e0b4bf Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Mon, 11 Apr 2022 11:47:33 +0200 Subject: [PATCH 135/160] Try removing conditional from bootstrap --- .../javascripts/wizard/tests/bootstrap.js.es6 | 38 +++++++++---------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/assets/javascripts/wizard/tests/bootstrap.js.es6 b/assets/javascripts/wizard/tests/bootstrap.js.es6 index a3343b9a..3083fb76 100644 --- a/assets/javascripts/wizard/tests/bootstrap.js.es6 +++ b/assets/javascripts/wizard/tests/bootstrap.js.es6 @@ -1,26 +1,24 @@ // discourse-skip-module /*global document, Logster */ -if (window.location.pathname.indexOf("/w/") > -1 && Ember.testing) { - document.addEventListener("DOMContentLoaded", function () { - document.body.insertAdjacentHTML( - "afterbegin", - ` -
- - ` - ); - }); +document.addEventListener("DOMContentLoaded", function () { + document.body.insertAdjacentHTML( + "afterbegin", + ` +
+ + ` + ); +}); - Object.keys(requirejs.entries).forEach(function (entry) { - if (/\-test/.test(entry)) { - requirejs(entry); - } - }); - - if (window.Logster) { - Logster.enabled = false; - } else { - window.Logster = { enabled: false }; +Object.keys(requirejs.entries).forEach(function (entry) { + if (/\-test/.test(entry)) { + requirejs(entry); } +}); + +if (window.Logster) { + Logster.enabled = false; +} else { + window.Logster = { enabled: false }; } From 9b96c02d0f8871a25a4cefc9eabbe3046fa65313 Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Mon, 11 Apr 2022 12:17:11 +0200 Subject: [PATCH 136/160] re-add qunit bootstrap conditional --- .../javascripts/wizard/tests/bootstrap.js.es6 | 38 ++++++++++--------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/assets/javascripts/wizard/tests/bootstrap.js.es6 b/assets/javascripts/wizard/tests/bootstrap.js.es6 index 3083fb76..a3343b9a 100644 --- a/assets/javascripts/wizard/tests/bootstrap.js.es6 +++ b/assets/javascripts/wizard/tests/bootstrap.js.es6 @@ -1,24 +1,26 @@ // discourse-skip-module /*global document, Logster */ -document.addEventListener("DOMContentLoaded", function () { - document.body.insertAdjacentHTML( - "afterbegin", - ` -
- - ` - ); -}); +if (window.location.pathname.indexOf("/w/") > -1 && Ember.testing) { + document.addEventListener("DOMContentLoaded", function () { + document.body.insertAdjacentHTML( + "afterbegin", + ` +
+ + ` + ); + }); -Object.keys(requirejs.entries).forEach(function (entry) { - if (/\-test/.test(entry)) { - requirejs(entry); + Object.keys(requirejs.entries).forEach(function (entry) { + if (/\-test/.test(entry)) { + requirejs(entry); + } + }); + + if (window.Logster) { + Logster.enabled = false; + } else { + window.Logster = { enabled: false }; } -}); - -if (window.Logster) { - Logster.enabled = false; -} else { - window.Logster = { enabled: false }; } From a87dc19eb3a874afadbdfa792732ec478789bcd8 Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Mon, 11 Apr 2022 12:21:38 +0200 Subject: [PATCH 137/160] Add qunit CSP extension --- .../extensions/content_security_policy_extension.rb | 12 ++++++++++++ plugin.rb | 3 ++- 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 lib/custom_wizard/extensions/content_security_policy_extension.rb diff --git a/lib/custom_wizard/extensions/content_security_policy_extension.rb b/lib/custom_wizard/extensions/content_security_policy_extension.rb new file mode 100644 index 00000000..fa7a166c --- /dev/null +++ b/lib/custom_wizard/extensions/content_security_policy_extension.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +module CustomWizardContentSecurityPolicyExtension + def path_specific_extension(path_info) + super.tap do |obj| + for_wizard_qunit_route = !Rails.env.production? && ["/w/qunit"].include?(path_info) + puts "PATH INFO: #{path_info}" + puts "FOR WIZARD QUNIT ROUTE: #{for_wizard_qunit_route}" + obj[:script_src] = :unsafe_eval if for_wizard_qunit_route + end + end +end diff --git a/plugin.rb b/plugin.rb index 3ebde0d0..a0ec79e5 100644 --- a/plugin.rb +++ b/plugin.rb @@ -17,7 +17,6 @@ if Rails.env.production? wizard-custom-guest.js wizard-custom-start.js wizard-custom.js - wizard-qunit.js wizard-plugin.js.erb wizard-raw-templates.js.erb } @@ -145,6 +144,7 @@ after_initialize do ../lib/custom_wizard/extensions/custom_field/serializer.rb ../lib/custom_wizard/extensions/custom_field/extension.rb ../lib/custom_wizard/extensions/discourse_tagging.rb + ../lib/custom_wizard/extensions/content_security_policy_extension.rb ].each do |path| load File.expand_path(path, __FILE__) end @@ -250,6 +250,7 @@ after_initialize do ::InvitesController.prepend InvitesControllerCustomWizard ::UsersController.prepend CustomWizardUsersController ::Guardian.prepend CustomWizardGuardian + ::ContentSecurityPolicy::Extension.singleton_class.prepend CustomWizardContentSecurityPolicyExtension full_path = "#{Rails.root}/plugins/discourse-custom-wizard/assets/stylesheets/wizard/wizard_custom.scss" if Stylesheet::Importer.respond_to?(:plugin_assets) From 0783cac9e01e8bd83323e69973463e9f3dda46a8 Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Mon, 11 Apr 2022 12:42:26 +0200 Subject: [PATCH 138/160] Add LOAD_PLUGINS to env in action workflow --- .github/workflows/plugin-tests.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/plugin-tests.yml b/.github/workflows/plugin-tests.yml index c76eaff6..f33f7bdd 100644 --- a/.github/workflows/plugin-tests.yml +++ b/.github/workflows/plugin-tests.yml @@ -26,6 +26,7 @@ jobs: RAILS_ENV: test PGUSER: discourse PGPASSWORD: discourse + LOAD_PLUGINS: 1 strategy: fail-fast: false From 86b0df89d18739e1b4e276d49b00b5a9a737ed13 Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Mon, 11 Apr 2022 13:42:32 +0200 Subject: [PATCH 139/160] Test config --- .github/workflows/plugin-tests.yml | 4 ++-- plugin.rb | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/plugin-tests.yml b/.github/workflows/plugin-tests.yml index f33f7bdd..00eaf7a0 100644 --- a/.github/workflows/plugin-tests.yml +++ b/.github/workflows/plugin-tests.yml @@ -26,7 +26,7 @@ jobs: RAILS_ENV: test PGUSER: discourse PGPASSWORD: discourse - LOAD_PLUGINS: 1 + LOAD_PLUGINS: "1" strategy: fail-fast: false @@ -150,5 +150,5 @@ jobs: - name: Wizard QUnit if: matrix.build_type == 'frontend' - run: QUNIT_SKIP_CORE=1 LOAD_PLUGINS=1 QUNIT_EMBER_CLI=0 bin/rake qunit:test['1200000','/w/qunit'] + run: QUNIT_SKIP_CORE=1 LOAD_PLUGINS=1 QUNIT_EMBER_CLI=0 bin/rake qunit:test['600000','/w/qunit'] timeout-minutes: 10 diff --git a/plugin.rb b/plugin.rb index a0ec79e5..e20c056d 100644 --- a/plugin.rb +++ b/plugin.rb @@ -80,6 +80,8 @@ def each_globbed_asset end end +puts "LOADING CUSTOM WIZARD PLUGIN" + after_initialize do %w[ ../lib/custom_wizard/engine.rb From 15c0245edf07defc5984a888b7c26bcc33d227c6 Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Mon, 11 Apr 2022 14:00:12 +0200 Subject: [PATCH 140/160] Test config --- .github/workflows/plugin-tests.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/plugin-tests.yml b/.github/workflows/plugin-tests.yml index 00eaf7a0..51d26303 100644 --- a/.github/workflows/plugin-tests.yml +++ b/.github/workflows/plugin-tests.yml @@ -26,7 +26,6 @@ jobs: RAILS_ENV: test PGUSER: discourse PGPASSWORD: discourse - LOAD_PLUGINS: "1" strategy: fail-fast: false @@ -150,5 +149,5 @@ jobs: - name: Wizard QUnit if: matrix.build_type == 'frontend' - run: QUNIT_SKIP_CORE=1 LOAD_PLUGINS=1 QUNIT_EMBER_CLI=0 bin/rake qunit:test['600000','/w/qunit'] + run: LOAD_PLUGINS=1 QUNIT_EMBER_CLI=0 bin/rake qunit:test['600000','/w/qunit'] timeout-minutes: 10 From 4348a4ee68cf1d8076de6d903d910a629e0f9bb8 Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Mon, 11 Apr 2022 14:36:04 +0200 Subject: [PATCH 141/160] Add more logs --- .github/workflows/plugin-tests.yml | 2 +- plugin.rb | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/plugin-tests.yml b/.github/workflows/plugin-tests.yml index 51d26303..970a3f6f 100644 --- a/.github/workflows/plugin-tests.yml +++ b/.github/workflows/plugin-tests.yml @@ -112,7 +112,7 @@ jobs: with: path: tmp/app-cache key: >- - ${{ hashFiles('.github/workflows/tests.yml') }}- + ${{ hashFiles('.github/workflows/plugin-tests.yml') }}- ${{ hashFiles('db/**/*', 'plugins/**/db/**/*') }}- - name: Restore database from cache diff --git a/plugin.rb b/plugin.rb index e20c056d..3c0a840a 100644 --- a/plugin.rb +++ b/plugin.rb @@ -22,6 +22,8 @@ if Rails.env.production? } end +puts "BEFORE ADD ASSET PATHS" + if Rails.env.test? config = Rails.application.config plugin_asset_path = "#{Rails.root}/plugins/discourse-custom-wizard/assets" @@ -252,6 +254,7 @@ after_initialize do ::InvitesController.prepend InvitesControllerCustomWizard ::UsersController.prepend CustomWizardUsersController ::Guardian.prepend CustomWizardGuardian + puts "PREPENDING CSP EXTENSION" ::ContentSecurityPolicy::Extension.singleton_class.prepend CustomWizardContentSecurityPolicyExtension full_path = "#{Rails.root}/plugins/discourse-custom-wizard/assets/stylesheets/wizard/wizard_custom.scss" From 7904bb5b3a3d83a9fe1e01180a1f716e796d83e9 Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Mon, 11 Apr 2022 14:37:04 +0200 Subject: [PATCH 142/160] Add more env variables --- .github/workflows/plugin-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/plugin-tests.yml b/.github/workflows/plugin-tests.yml index 970a3f6f..8a45bf62 100644 --- a/.github/workflows/plugin-tests.yml +++ b/.github/workflows/plugin-tests.yml @@ -149,5 +149,5 @@ jobs: - name: Wizard QUnit if: matrix.build_type == 'frontend' - run: LOAD_PLUGINS=1 QUNIT_EMBER_CLI=0 bin/rake qunit:test['600000','/w/qunit'] + run: LOAD_PLUGINS=1 QUNIT_EMBER_CLI=0 RAILS_ENABLE_TEST_LOG=1 QUNIT_SKIP_CORE=1 bin/rake qunit:test['600000','/w/qunit'] timeout-minutes: 10 From 0313e1be959557a1fa75e04a1beb22636d52a03e Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Mon, 11 Apr 2022 14:56:19 +0200 Subject: [PATCH 143/160] Add backslash to qunit path --- config/routes.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/routes.rb b/config/routes.rb index 2d9f52c6..5f124001 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true CustomWizard::Engine.routes.draw do - get 'qunit' => 'wizard#qunit' + get '/qunit' => 'wizard#qunit' get ':wizard_id' => 'wizard#index' put ':wizard_id/skip' => 'wizard#skip' get ':wizard_id/steps' => 'wizard#index' From 1f320aac937f6ada2e20dedc41236cd2cff8704a Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Mon, 11 Apr 2022 15:16:29 +0200 Subject: [PATCH 144/160] Fix tests --- .github/workflows/plugin-tests.yml | 4 ++-- config/routes.rb | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/plugin-tests.yml b/.github/workflows/plugin-tests.yml index 8a45bf62..6049931d 100644 --- a/.github/workflows/plugin-tests.yml +++ b/.github/workflows/plugin-tests.yml @@ -51,7 +51,7 @@ jobs: uses: actions/checkout@v3 with: path: plugins/${{ github.event.repository.name }} - ref: "${{ github.base_ref }}" + ref: "${{ github.head_ref }}" fetch-depth: 1 - name: Setup Git @@ -149,5 +149,5 @@ jobs: - name: Wizard QUnit if: matrix.build_type == 'frontend' - run: LOAD_PLUGINS=1 QUNIT_EMBER_CLI=0 RAILS_ENABLE_TEST_LOG=1 QUNIT_SKIP_CORE=1 bin/rake qunit:test['600000','/w/qunit'] + run: LOAD_PLUGINS=1 QUNIT_EMBER_CLI=0 QUNIT_SKIP_CORE=1 bin/rake qunit:test['600000','/w/qunit'] timeout-minutes: 10 diff --git a/config/routes.rb b/config/routes.rb index 5f124001..2d9f52c6 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true CustomWizard::Engine.routes.draw do - get '/qunit' => 'wizard#qunit' + get 'qunit' => 'wizard#qunit' get ':wizard_id' => 'wizard#index' put ':wizard_id/skip' => 'wizard#skip' get ':wizard_id/steps' => 'wizard#index' From 0970b49db8de1f0953e73224a19f74195c45ea1c Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Mon, 11 Apr 2022 15:24:10 +0200 Subject: [PATCH 145/160] Remove unnecessary extensions --- .../extensions/content_security_policy_extension.rb | 12 ------------ plugin.rb | 7 ------- 2 files changed, 19 deletions(-) delete mode 100644 lib/custom_wizard/extensions/content_security_policy_extension.rb diff --git a/lib/custom_wizard/extensions/content_security_policy_extension.rb b/lib/custom_wizard/extensions/content_security_policy_extension.rb deleted file mode 100644 index fa7a166c..00000000 --- a/lib/custom_wizard/extensions/content_security_policy_extension.rb +++ /dev/null @@ -1,12 +0,0 @@ -# frozen_string_literal: true - -module CustomWizardContentSecurityPolicyExtension - def path_specific_extension(path_info) - super.tap do |obj| - for_wizard_qunit_route = !Rails.env.production? && ["/w/qunit"].include?(path_info) - puts "PATH INFO: #{path_info}" - puts "FOR WIZARD QUNIT ROUTE: #{for_wizard_qunit_route}" - obj[:script_src] = :unsafe_eval if for_wizard_qunit_route - end - end -end diff --git a/plugin.rb b/plugin.rb index 3c0a840a..a397e3a7 100644 --- a/plugin.rb +++ b/plugin.rb @@ -22,8 +22,6 @@ if Rails.env.production? } end -puts "BEFORE ADD ASSET PATHS" - if Rails.env.test? config = Rails.application.config plugin_asset_path = "#{Rails.root}/plugins/discourse-custom-wizard/assets" @@ -82,8 +80,6 @@ def each_globbed_asset end end -puts "LOADING CUSTOM WIZARD PLUGIN" - after_initialize do %w[ ../lib/custom_wizard/engine.rb @@ -148,7 +144,6 @@ after_initialize do ../lib/custom_wizard/extensions/custom_field/serializer.rb ../lib/custom_wizard/extensions/custom_field/extension.rb ../lib/custom_wizard/extensions/discourse_tagging.rb - ../lib/custom_wizard/extensions/content_security_policy_extension.rb ].each do |path| load File.expand_path(path, __FILE__) end @@ -254,8 +249,6 @@ after_initialize do ::InvitesController.prepend InvitesControllerCustomWizard ::UsersController.prepend CustomWizardUsersController ::Guardian.prepend CustomWizardGuardian - puts "PREPENDING CSP EXTENSION" - ::ContentSecurityPolicy::Extension.singleton_class.prepend CustomWizardContentSecurityPolicyExtension full_path = "#{Rails.root}/plugins/discourse-custom-wizard/assets/stylesheets/wizard/wizard_custom.scss" if Stylesheet::Importer.respond_to?(:plugin_assets) From 45ab9b1b8053aaab1182821e67f546bc6f3ca298 Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Fri, 13 May 2022 11:52:11 +0200 Subject: [PATCH 146/160] Remove unconnected action --- .../discourse/templates/admin-wizards-wizard-show.hbs | 1 - 1 file changed, 1 deletion(-) diff --git a/assets/javascripts/discourse/templates/admin-wizards-wizard-show.hbs b/assets/javascripts/discourse/templates/admin-wizards-wizard-show.hbs index e10fd91d..e7fd0865 100644 --- a/assets/javascripts/discourse/templates/admin-wizards-wizard-show.hbs +++ b/assets/javascripts/discourse/templates/admin-wizards-wizard-show.hbs @@ -176,7 +176,6 @@ currentActionId=currentAction.id wizard=wizard apis=apis - removeAction=(action "removeAction") wizardFields=wizardFields}} {{/each}} From 45f52d56c0cb01c452d16e047c3d482320908f85 Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Fri, 13 May 2022 12:04:53 +0200 Subject: [PATCH 147/160] Sort actions by subscription type --- .../components/wizard-subscription-selector.js.es6 | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/assets/javascripts/discourse/components/wizard-subscription-selector.js.es6 b/assets/javascripts/discourse/components/wizard-subscription-selector.js.es6 index 2202f746..d6db8875 100644 --- a/assets/javascripts/discourse/components/wizard-subscription-selector.js.es6 +++ b/assets/javascripts/discourse/components/wizard-subscription-selector.js.es6 @@ -67,6 +67,18 @@ export default SingleSelectComponent.extend(Subscription, { requiredSubscriptionType ), }; + }).sort(function(a, b) { + if (a.subscriptionType && !b.subscriptionType) { + return 1; + } + if (!a.subscriptionType && b.subscriptionType) { + return -1; + } + if (a.subscriptionType == b.subscriptionType) { + return a.subscriptionType ? a.subscriptionType.localeCompare(b.subscriptionType) : 0; + } else { + return a.subscriptionType === 'standard' ? -1 : 0; + } }); }, From 1c9d52bcfc672fe988e6a55fd747234b58544a67 Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Fri, 13 May 2022 12:06:38 +0200 Subject: [PATCH 148/160] Fix linting --- .../wizard-subscription-selector.js.es6 | 60 ++++++++++--------- 1 file changed, 32 insertions(+), 28 deletions(-) diff --git a/assets/javascripts/discourse/components/wizard-subscription-selector.js.es6 b/assets/javascripts/discourse/components/wizard-subscription-selector.js.es6 index d6db8875..8ef6896a 100644 --- a/assets/javascripts/discourse/components/wizard-subscription-selector.js.es6 +++ b/assets/javascripts/discourse/components/wizard-subscription-selector.js.es6 @@ -52,34 +52,38 @@ export default SingleSelectComponent.extend(Subscription, { @discourseComputed("feature", "attribute") content(feature, attribute) { - return wizardSchema[feature][attribute].map((value) => { - let requiredSubscriptionType = this.requiredSubscriptionType( - feature, - attribute, - value - ); - return { - id: value, - name: I18n.t(nameKey(feature, attribute, value)), - subscriptionType: requiredSubscriptionType, - disabled: !subscriptionTypeSufficient( - this.subscriptionType, - requiredSubscriptionType - ), - }; - }).sort(function(a, b) { - if (a.subscriptionType && !b.subscriptionType) { - return 1; - } - if (!a.subscriptionType && b.subscriptionType) { - return -1; - } - if (a.subscriptionType == b.subscriptionType) { - return a.subscriptionType ? a.subscriptionType.localeCompare(b.subscriptionType) : 0; - } else { - return a.subscriptionType === 'standard' ? -1 : 0; - } - }); + return wizardSchema[feature][attribute] + .map((value) => { + let requiredSubscriptionType = this.requiredSubscriptionType( + feature, + attribute, + value + ); + return { + id: value, + name: I18n.t(nameKey(feature, attribute, value)), + subscriptionType: requiredSubscriptionType, + disabled: !subscriptionTypeSufficient( + this.subscriptionType, + requiredSubscriptionType + ), + }; + }) + .sort(function (a, b) { + if (a.subscriptionType && !b.subscriptionType) { + return 1; + } + if (!a.subscriptionType && b.subscriptionType) { + return -1; + } + if (a.subscriptionType === b.subscriptionType) { + return a.subscriptionType + ? a.subscriptionType.localeCompare(b.subscriptionType) + : 0; + } else { + return a.subscriptionType === "standard" ? -1 : 0; + } + }); }, modifyComponentForRow() { From 8fd5ae71695f5314a71a93c327cdd663424e87f7 Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Tue, 31 May 2022 13:04:35 +0200 Subject: [PATCH 149/160] Ensure wizard asset paths are included --- plugin.rb | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/plugin.rb b/plugin.rb index a397e3a7..e764c146 100644 --- a/plugin.rb +++ b/plugin.rb @@ -22,12 +22,10 @@ if Rails.env.production? } end -if Rails.env.test? - config = Rails.application.config - plugin_asset_path = "#{Rails.root}/plugins/discourse-custom-wizard/assets" - config.assets.paths << "#{plugin_asset_path}/javascripts" - config.assets.paths << "#{plugin_asset_path}/stylesheets/wizard" -end +config = Rails.application.config +plugin_asset_path = "#{Rails.root}/plugins/discourse-custom-wizard/assets" +config.assets.paths << "#{plugin_asset_path}/javascripts" +config.assets.paths << "#{plugin_asset_path}/stylesheets/wizard" if respond_to?(:register_svg_icon) register_svg_icon "far-calendar" From 1d7d9d111942b3956199155e3aabfe7338cde09a Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Tue, 31 May 2022 13:16:28 +0200 Subject: [PATCH 150/160] Fix failing specs --- spec/components/custom_wizard/custom_field_spec.rb | 4 ++-- .../custom_wizard/admin/custom_fields_controller_spec.rb | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/components/custom_wizard/custom_field_spec.rb b/spec/components/custom_wizard/custom_field_spec.rb index ff47585b..4b8d43e2 100644 --- a/spec/components/custom_wizard/custom_field_spec.rb +++ b/spec/components/custom_wizard/custom_field_spec.rb @@ -260,11 +260,11 @@ describe CustomWizard::CustomField do end it "lists custom field records added by other plugins " do - expect(CustomWizard::CustomField.external_list.length).to eq(8) + expect(CustomWizard::CustomField.external_list.length).to be > 2 end it "lists all custom field records" do - expect(CustomWizard::CustomField.full_list.length).to eq(12) + expect(CustomWizard::CustomField.full_list.length).to be > 2 end end diff --git a/spec/requests/custom_wizard/admin/custom_fields_controller_spec.rb b/spec/requests/custom_wizard/admin/custom_fields_controller_spec.rb index bc2dbb9d..7aef791c 100644 --- a/spec/requests/custom_wizard/admin/custom_fields_controller_spec.rb +++ b/spec/requests/custom_wizard/admin/custom_fields_controller_spec.rb @@ -13,7 +13,7 @@ describe CustomWizard::AdminCustomFieldsController do it "returns the full list of custom fields" do get "/admin/wizards/custom-fields.json" - expect(response.parsed_body.length).to eq(12) + expect(response.parsed_body["custom_fields"].length).to be > 2 end it "saves custom fields" do From eb8b289b502c57fc161910ab39d9ba3237e40a9e Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Tue, 31 May 2022 13:37:34 +0200 Subject: [PATCH 151/160] Fix create api action --- assets/javascripts/discourse/templates/admin-wizards-api.hbs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/javascripts/discourse/templates/admin-wizards-api.hbs b/assets/javascripts/discourse/templates/admin-wizards-api.hbs index f5e3214d..af91d0fb 100644 --- a/assets/javascripts/discourse/templates/admin-wizards-api.hbs +++ b/assets/javascripts/discourse/templates/admin-wizards-api.hbs @@ -8,7 +8,7 @@ )}} {{d-button - action=(action "createApi") + action=(route-action "createApi") label="admin.wizard.api.create" icon="plus"}}
From cf33fb97920c10a13811c988fe40b5c188b50a71 Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Thu, 2 Jun 2022 16:45:20 +0200 Subject: [PATCH 152/160] Style and other fixes for API features --- .../controllers/admin-wizards-api-show.js.es6 | 5 ++++ .../templates/admin-wizards-api-show.hbs | 30 +++++++++++-------- assets/stylesheets/admin/admin.scss | 10 +++++-- assets/stylesheets/admin/wizard/api.scss | 19 +++++++----- config/locales/client.en.yml | 1 + lib/custom_wizard/api/log_entry.rb | 2 +- 6 files changed, 43 insertions(+), 24 deletions(-) diff --git a/assets/javascripts/discourse/controllers/admin-wizards-api-show.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards-api-show.js.es6 index 7d3c1084..f41c3ada 100644 --- a/assets/javascripts/discourse/controllers/admin-wizards-api-show.js.es6 +++ b/assets/javascripts/discourse/controllers/admin-wizards-api-show.js.es6 @@ -88,6 +88,11 @@ export default Controller.extend({ twoLeggedOauth: equal("api.authType", "oauth_2"), threeLeggedOauth: equal("api.authType", "oauth_3"), + @discourseComputed("api.isNew") + nameClass(isNew) { + return isNew ? "new" : "saved"; + }, + actions: { addParam() { this.get("api.authParams").pushObject({}); diff --git a/assets/javascripts/discourse/templates/admin-wizards-api-show.hbs b/assets/javascripts/discourse/templates/admin-wizards-api-show.hbs index 4d3def3d..303b3f6d 100644 --- a/assets/javascripts/discourse/templates/admin-wizards-api-show.hbs +++ b/assets/javascripts/discourse/templates/admin-wizards-api-show.hbs @@ -21,11 +21,11 @@ {{/if}}
-
+
{{#if api.isNew}} {{i18n "admin.wizard.api.new"}} {{else}} - {{api.title}} + {{api.title}} {{/if}}
@@ -35,12 +35,12 @@ {{input value=api.title placeholder=(i18n "admin.wizard.api.title_placeholder")}}
-
+
{{#if api.isNew}} {{input value=api.name placeholder=(i18n "admin.wizard.api.name_placeholder")}} {{else}} - {{api.name}} + {{api.name}} {{/if}}
@@ -63,7 +63,7 @@ {{/if}}
-
+
{{i18n "admin.wizard.api.auth.label"}}
@@ -71,7 +71,7 @@
-
+
{{i18n "admin.wizard.api.auth.settings"}}
@@ -174,7 +174,7 @@ {{/if}}
-
+
{{i18n "admin.wizard.api.status.label"}}
@@ -220,7 +220,7 @@ {{/if}}
-
+
{{i18n "admin.wizard.api.endpoint.label"}}
@@ -277,11 +277,15 @@ {{/if}}
-
+
{{i18n "admin.wizard.api.log.label"}} - {{d-button action=(action "clearLogs") - icon="trash-alt" - class="clear-logs"}} + +
+ {{d-button + action=(action "clearLogs") + class="clear-logs" + label="admin.wizard.api.log.clear"}} +
@@ -300,7 +304,7 @@ {{logentry.time}} {{logentry.status}} diff --git a/assets/stylesheets/admin/admin.scss b/assets/stylesheets/admin/admin.scss index 70d5d539..d0106337 100644 --- a/assets/stylesheets/admin/admin.scss +++ b/assets/stylesheets/admin/admin.scss @@ -79,7 +79,8 @@ $error: #ef1700; } } - & + div { + & + div, + & + div + div { margin-top: 30px; } } @@ -212,6 +213,7 @@ $error: #ef1700; .wizard-header { margin-bottom: 20px; + display: flex; &.large { font-size: 1.5em; @@ -253,11 +255,12 @@ $error: #ef1700; } &.medium { - font-size: 1.3em; + font-size: 1.2em; } &.small { font-size: 1em; + font-weight: 700; margin-bottom: 5px; } @@ -266,8 +269,9 @@ $error: #ef1700; } .controls { + font-size: 1rem; + display: flex; margin-left: auto; - margin-right: 0.5rem; } } diff --git a/assets/stylesheets/admin/wizard/api.scss b/assets/stylesheets/admin/wizard/api.scss index 9d0ad261..29aff22d 100644 --- a/assets/stylesheets/admin/wizard/api.scss +++ b/assets/stylesheets/admin/wizard/api.scss @@ -40,9 +40,18 @@ float: right; } - .wizard-header { - overflow: hidden; - font-size: 1.3em; + .metadata { + display: flex; + + .title { + margin-right: 1em; + } + + .name.saved span { + display: inline-block; + padding: 6px 12px; + background-color: var(--primary-low); + } } } @@ -74,10 +83,6 @@ width: 50%; max-width: 50%; - .wizard-header { - overflow: hidden; - } - .authorization { float: right; } diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 45efabb8..6fe9e5ee 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -447,6 +447,7 @@ en: log: label: "Logs" + clear: clear log: nav_label: "Logs" diff --git a/lib/custom_wizard/api/log_entry.rb b/lib/custom_wizard/api/log_entry.rb index 3fe2969e..31af514e 100644 --- a/lib/custom_wizard/api/log_entry.rb +++ b/lib/custom_wizard/api/log_entry.rb @@ -27,7 +27,7 @@ class CustomWizard::Api::LogEntry log_id = new_data['log_id'] else data = {} - log_id = SecureRandom.hex(3) + log_id = SecureRandom.hex(8) end new_data.each do |k, v| From 717aedb6075ee683cee6a720bec06d72282aada2 Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Thu, 2 Jun 2022 18:00:01 +0200 Subject: [PATCH 153/160] Tweak checkbox field description css --- assets/stylesheets/wizard/custom/field.scss | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/assets/stylesheets/wizard/custom/field.scss b/assets/stylesheets/wizard/custom/field.scss index cb6f0635..3ef3010d 100644 --- a/assets/stylesheets/wizard/custom/field.scss +++ b/assets/stylesheets/wizard/custom/field.scss @@ -59,13 +59,16 @@ & > label.field-label { order: 1; - font-weight: 400; } & > .field-description { margin-top: 0; order: 2; } + + & > .field-label + .field-description { + margin-left: .5em; + } } .url-field input { From e88fff7e25f0d595a81bf0aac2d0df8cc7015682 Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Thu, 2 Jun 2022 18:05:17 +0200 Subject: [PATCH 154/160] Tweak preview toggle text --- config/locales/client.af.yml | 4 ++-- config/locales/client.ar.yml | 4 ++-- config/locales/client.az.yml | 4 ++-- config/locales/client.be.yml | 4 ++-- config/locales/client.bg.yml | 4 ++-- config/locales/client.bn.yml | 4 ++-- config/locales/client.bo.yml | 4 ++-- config/locales/client.bs.yml | 4 ++-- config/locales/client.ca.yml | 4 ++-- config/locales/client.cs.yml | 4 ++-- config/locales/client.cy.yml | 4 ++-- config/locales/client.da.yml | 4 ++-- config/locales/client.de.yml | 4 ++-- config/locales/client.el.yml | 4 ++-- config/locales/client.en.yml | 4 ++-- config/locales/client.eo.yml | 4 ++-- config/locales/client.es.yml | 4 ++-- config/locales/client.et.yml | 4 ++-- config/locales/client.eu.yml | 4 ++-- config/locales/client.fa.yml | 4 ++-- config/locales/client.fi.yml | 4 ++-- config/locales/client.gl.yml | 4 ++-- config/locales/client.he.yml | 4 ++-- config/locales/client.hi.yml | 4 ++-- config/locales/client.hr.yml | 4 ++-- config/locales/client.hu.yml | 4 ++-- config/locales/client.hy.yml | 4 ++-- config/locales/client.id.yml | 4 ++-- config/locales/client.is.yml | 4 ++-- config/locales/client.it.yml | 4 ++-- config/locales/client.ja.yml | 4 ++-- config/locales/client.ka.yml | 4 ++-- config/locales/client.kk.yml | 4 ++-- config/locales/client.km.yml | 4 ++-- config/locales/client.kn.yml | 4 ++-- config/locales/client.ko.yml | 4 ++-- config/locales/client.ku.yml | 4 ++-- config/locales/client.lo.yml | 4 ++-- config/locales/client.lt.yml | 4 ++-- config/locales/client.lv.yml | 4 ++-- config/locales/client.mk.yml | 4 ++-- config/locales/client.ml.yml | 4 ++-- config/locales/client.mn.yml | 4 ++-- config/locales/client.ms.yml | 4 ++-- config/locales/client.ne.yml | 4 ++-- config/locales/client.nl.yml | 4 ++-- config/locales/client.no.yml | 4 ++-- config/locales/client.om.yml | 4 ++-- config/locales/client.pa.yml | 4 ++-- config/locales/client.pl.yml | 4 ++-- config/locales/client.pt.yml | 4 ++-- config/locales/client.ro.yml | 4 ++-- config/locales/client.ru.yml | 4 ++-- config/locales/client.sd.yml | 4 ++-- config/locales/client.sk.yml | 4 ++-- config/locales/client.sl.yml | 4 ++-- config/locales/client.sq.yml | 4 ++-- config/locales/client.sr.yml | 4 ++-- config/locales/client.sv.yml | 4 ++-- config/locales/client.sw.yml | 4 ++-- config/locales/client.ta.yml | 4 ++-- config/locales/client.te.yml | 4 ++-- config/locales/client.th.yml | 4 ++-- config/locales/client.tl.yml | 4 ++-- config/locales/client.tr.yml | 4 ++-- config/locales/client.tt.yml | 4 ++-- config/locales/client.uk.yml | 4 ++-- config/locales/client.ur.yml | 4 ++-- config/locales/client.vi.yml | 4 ++-- config/locales/client.yi.yml | 4 ++-- config/locales/client.zh.yml | 4 ++-- config/locales/client.zu.yml | 4 ++-- 72 files changed, 144 insertions(+), 144 deletions(-) diff --git a/config/locales/client.af.yml b/config/locales/client.af.yml index 2f399b99..6bacad0a 100644 --- a/config/locales/client.af.yml +++ b/config/locales/client.af.yml @@ -486,8 +486,8 @@ af: one: "%{count} Character" other: "%{count} Characters" wizard_composer: - show_preview: "Preview Post" - hide_preview: "Edit Post" + show_preview: "Preview" + hide_preview: "Edit" quote_post_title: "Quote whole post" bold_label: "B" bold_title: "Strong" diff --git a/config/locales/client.ar.yml b/config/locales/client.ar.yml index b7253bba..39ca7df6 100644 --- a/config/locales/client.ar.yml +++ b/config/locales/client.ar.yml @@ -498,8 +498,8 @@ ar: many: "%{count} Characters" other: "%{count} Characters" wizard_composer: - show_preview: "Preview Post" - hide_preview: "Edit Post" + show_preview: "Preview" + hide_preview: "Edit" quote_post_title: "Quote whole post" bold_label: "B" bold_title: "Strong" diff --git a/config/locales/client.az.yml b/config/locales/client.az.yml index 990f6a37..02686c04 100644 --- a/config/locales/client.az.yml +++ b/config/locales/client.az.yml @@ -486,8 +486,8 @@ az: one: "%{count} Character" other: "%{count} Characters" wizard_composer: - show_preview: "Preview Post" - hide_preview: "Edit Post" + show_preview: "Preview" + hide_preview: "Edit" quote_post_title: "Quote whole post" bold_label: "B" bold_title: "Strong" diff --git a/config/locales/client.be.yml b/config/locales/client.be.yml index f6fd6764..392dd4c8 100644 --- a/config/locales/client.be.yml +++ b/config/locales/client.be.yml @@ -492,8 +492,8 @@ be: many: "%{count} Characters" other: "%{count} Characters" wizard_composer: - show_preview: "Preview Post" - hide_preview: "Edit Post" + show_preview: "Preview" + hide_preview: "Edit" quote_post_title: "Quote whole post" bold_label: "B" bold_title: "Strong" diff --git a/config/locales/client.bg.yml b/config/locales/client.bg.yml index 058baf4d..61303c04 100644 --- a/config/locales/client.bg.yml +++ b/config/locales/client.bg.yml @@ -486,8 +486,8 @@ bg: one: "%{count} Character" other: "%{count} Characters" wizard_composer: - show_preview: "Preview Post" - hide_preview: "Edit Post" + show_preview: "Preview" + hide_preview: "Edit" quote_post_title: "Quote whole post" bold_label: "B" bold_title: "Strong" diff --git a/config/locales/client.bn.yml b/config/locales/client.bn.yml index ca21e418..5e1e799e 100644 --- a/config/locales/client.bn.yml +++ b/config/locales/client.bn.yml @@ -486,8 +486,8 @@ bn: one: "%{count} Character" other: "%{count} Characters" wizard_composer: - show_preview: "Preview Post" - hide_preview: "Edit Post" + show_preview: "Preview" + hide_preview: "Edit" quote_post_title: "Quote whole post" bold_label: "B" bold_title: "Strong" diff --git a/config/locales/client.bo.yml b/config/locales/client.bo.yml index cd8c7924..8fc80249 100644 --- a/config/locales/client.bo.yml +++ b/config/locales/client.bo.yml @@ -483,8 +483,8 @@ bo: x_characters: other: "%{count} Characters" wizard_composer: - show_preview: "Preview Post" - hide_preview: "Edit Post" + show_preview: "Preview" + hide_preview: "Edit" quote_post_title: "Quote whole post" bold_label: "B" bold_title: "Strong" diff --git a/config/locales/client.bs.yml b/config/locales/client.bs.yml index e188aece..4cd291b9 100644 --- a/config/locales/client.bs.yml +++ b/config/locales/client.bs.yml @@ -489,8 +489,8 @@ bs: few: "%{count} Characters" other: "%{count} Characters" wizard_composer: - show_preview: "Preview Post" - hide_preview: "Edit Post" + show_preview: "Preview" + hide_preview: "Edit" quote_post_title: "Quote whole post" bold_label: "B" bold_title: "Strong" diff --git a/config/locales/client.ca.yml b/config/locales/client.ca.yml index 7d80c17b..54ee95ae 100644 --- a/config/locales/client.ca.yml +++ b/config/locales/client.ca.yml @@ -486,8 +486,8 @@ ca: one: "%{count} Character" other: "%{count} Characters" wizard_composer: - show_preview: "Preview Post" - hide_preview: "Edit Post" + show_preview: "Preview" + hide_preview: "Edit" quote_post_title: "Quote whole post" bold_label: "B" bold_title: "Strong" diff --git a/config/locales/client.cs.yml b/config/locales/client.cs.yml index 018e4236..45e0980e 100644 --- a/config/locales/client.cs.yml +++ b/config/locales/client.cs.yml @@ -492,8 +492,8 @@ cs: many: "%{count} Characters" other: "%{count} Characters" wizard_composer: - show_preview: "Preview Post" - hide_preview: "Edit Post" + show_preview: "Preview" + hide_preview: "Edit" quote_post_title: "Quote whole post" bold_label: "B" bold_title: "Strong" diff --git a/config/locales/client.cy.yml b/config/locales/client.cy.yml index 4510470b..6049360e 100644 --- a/config/locales/client.cy.yml +++ b/config/locales/client.cy.yml @@ -498,8 +498,8 @@ cy: many: "%{count} Characters" other: "%{count} Characters" wizard_composer: - show_preview: "Preview Post" - hide_preview: "Edit Post" + show_preview: "Preview" + hide_preview: "Edit" quote_post_title: "Quote whole post" bold_label: "B" bold_title: "Strong" diff --git a/config/locales/client.da.yml b/config/locales/client.da.yml index 4b254517..281bd56d 100644 --- a/config/locales/client.da.yml +++ b/config/locales/client.da.yml @@ -486,8 +486,8 @@ da: one: "%{count} Character" other: "%{count} Characters" wizard_composer: - show_preview: "Preview Post" - hide_preview: "Edit Post" + show_preview: "Preview" + hide_preview: "Edit" quote_post_title: "Quote whole post" bold_label: "B" bold_title: "Strong" diff --git a/config/locales/client.de.yml b/config/locales/client.de.yml index a69bf37f..7921b8f8 100644 --- a/config/locales/client.de.yml +++ b/config/locales/client.de.yml @@ -486,8 +486,8 @@ de: one: "%{count} Character" other: "%{count} Characters" wizard_composer: - show_preview: "Preview Post" - hide_preview: "Edit Post" + show_preview: "Preview" + hide_preview: "Edit" quote_post_title: "Quote whole post" bold_label: "B" bold_title: "Strong" diff --git a/config/locales/client.el.yml b/config/locales/client.el.yml index 5a49f0c8..2bf699d1 100644 --- a/config/locales/client.el.yml +++ b/config/locales/client.el.yml @@ -486,8 +486,8 @@ el: one: "%{count} Character" other: "%{count} Characters" wizard_composer: - show_preview: "Preview Post" - hide_preview: "Edit Post" + show_preview: "Preview" + hide_preview: "Edit" quote_post_title: "Quote whole post" bold_label: "B" bold_title: "Strong" diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 6fe9e5ee..897049ed 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -565,8 +565,8 @@ en: other: "%{count} Characters" wizard_composer: - show_preview: "Preview Post" - hide_preview: "Edit Post" + show_preview: "Preview" + hide_preview: "Edit" quote_post_title: "Quote whole post" bold_label: "B" bold_title: "Strong" diff --git a/config/locales/client.eo.yml b/config/locales/client.eo.yml index ee59949b..685c9f98 100644 --- a/config/locales/client.eo.yml +++ b/config/locales/client.eo.yml @@ -486,8 +486,8 @@ eo: one: "%{count} Character" other: "%{count} Characters" wizard_composer: - show_preview: "Preview Post" - hide_preview: "Edit Post" + show_preview: "Preview" + hide_preview: "Edit" quote_post_title: "Quote whole post" bold_label: "B" bold_title: "Strong" diff --git a/config/locales/client.es.yml b/config/locales/client.es.yml index b9d08e0b..2e7a106a 100644 --- a/config/locales/client.es.yml +++ b/config/locales/client.es.yml @@ -486,8 +486,8 @@ es: one: "%{count} Character" other: "%{count} Characters" wizard_composer: - show_preview: "Preview Post" - hide_preview: "Edit Post" + show_preview: "Preview" + hide_preview: "Edit" quote_post_title: "Quote whole post" bold_label: "B" bold_title: "Strong" diff --git a/config/locales/client.et.yml b/config/locales/client.et.yml index 490c3afc..173700e3 100644 --- a/config/locales/client.et.yml +++ b/config/locales/client.et.yml @@ -486,8 +486,8 @@ et: one: "%{count} Character" other: "%{count} Characters" wizard_composer: - show_preview: "Preview Post" - hide_preview: "Edit Post" + show_preview: "Preview" + hide_preview: "Edit" quote_post_title: "Quote whole post" bold_label: "B" bold_title: "Strong" diff --git a/config/locales/client.eu.yml b/config/locales/client.eu.yml index a12a029c..f1099dfa 100644 --- a/config/locales/client.eu.yml +++ b/config/locales/client.eu.yml @@ -486,8 +486,8 @@ eu: one: "%{count} Character" other: "%{count} Characters" wizard_composer: - show_preview: "Preview Post" - hide_preview: "Edit Post" + show_preview: "Preview" + hide_preview: "Edit" quote_post_title: "Quote whole post" bold_label: "B" bold_title: "Strong" diff --git a/config/locales/client.fa.yml b/config/locales/client.fa.yml index 68f2a0c8..29db8d16 100644 --- a/config/locales/client.fa.yml +++ b/config/locales/client.fa.yml @@ -486,8 +486,8 @@ fa: one: "%{count} Character" other: "%{count} Characters" wizard_composer: - show_preview: "Preview Post" - hide_preview: "Edit Post" + show_preview: "Preview" + hide_preview: "Edit" quote_post_title: "Quote whole post" bold_label: "B" bold_title: "Strong" diff --git a/config/locales/client.fi.yml b/config/locales/client.fi.yml index 95a82ae9..ebbd70c1 100644 --- a/config/locales/client.fi.yml +++ b/config/locales/client.fi.yml @@ -486,8 +486,8 @@ fi: one: "%{count} Character" other: "%{count} Characters" wizard_composer: - show_preview: "Preview Post" - hide_preview: "Edit Post" + show_preview: "Preview" + hide_preview: "Edit" quote_post_title: "Quote whole post" bold_label: "B" bold_title: "Strong" diff --git a/config/locales/client.gl.yml b/config/locales/client.gl.yml index 0767376d..7617e006 100644 --- a/config/locales/client.gl.yml +++ b/config/locales/client.gl.yml @@ -486,8 +486,8 @@ gl: one: "%{count} Character" other: "%{count} Characters" wizard_composer: - show_preview: "Preview Post" - hide_preview: "Edit Post" + show_preview: "Preview" + hide_preview: "Edit" quote_post_title: "Quote whole post" bold_label: "B" bold_title: "Strong" diff --git a/config/locales/client.he.yml b/config/locales/client.he.yml index 4fc73f73..1e7b5efb 100644 --- a/config/locales/client.he.yml +++ b/config/locales/client.he.yml @@ -492,8 +492,8 @@ he: many: "%{count} Characters" other: "%{count} Characters" wizard_composer: - show_preview: "Preview Post" - hide_preview: "Edit Post" + show_preview: "Preview" + hide_preview: "Edit" quote_post_title: "Quote whole post" bold_label: "B" bold_title: "Strong" diff --git a/config/locales/client.hi.yml b/config/locales/client.hi.yml index 0bdd2a8f..b2e1bfc2 100644 --- a/config/locales/client.hi.yml +++ b/config/locales/client.hi.yml @@ -486,8 +486,8 @@ hi: one: "%{count} Character" other: "%{count} Characters" wizard_composer: - show_preview: "Preview Post" - hide_preview: "Edit Post" + show_preview: "Preview" + hide_preview: "Edit" quote_post_title: "Quote whole post" bold_label: "B" bold_title: "Strong" diff --git a/config/locales/client.hr.yml b/config/locales/client.hr.yml index a9b9e8ff..5cc2ae85 100644 --- a/config/locales/client.hr.yml +++ b/config/locales/client.hr.yml @@ -489,8 +489,8 @@ hr: few: "%{count} Characters" other: "%{count} Characters" wizard_composer: - show_preview: "Preview Post" - hide_preview: "Edit Post" + show_preview: "Preview" + hide_preview: "Edit" quote_post_title: "Quote whole post" bold_label: "B" bold_title: "Strong" diff --git a/config/locales/client.hu.yml b/config/locales/client.hu.yml index a70682f0..c8d0df17 100644 --- a/config/locales/client.hu.yml +++ b/config/locales/client.hu.yml @@ -486,8 +486,8 @@ hu: one: "%{count} Character" other: "%{count} Characters" wizard_composer: - show_preview: "Preview Post" - hide_preview: "Edit Post" + show_preview: "Preview" + hide_preview: "Edit" quote_post_title: "Quote whole post" bold_label: "B" bold_title: "Strong" diff --git a/config/locales/client.hy.yml b/config/locales/client.hy.yml index 028b9f42..59a3cf36 100644 --- a/config/locales/client.hy.yml +++ b/config/locales/client.hy.yml @@ -486,8 +486,8 @@ hy: one: "%{count} Character" other: "%{count} Characters" wizard_composer: - show_preview: "Preview Post" - hide_preview: "Edit Post" + show_preview: "Preview" + hide_preview: "Edit" quote_post_title: "Quote whole post" bold_label: "B" bold_title: "Strong" diff --git a/config/locales/client.id.yml b/config/locales/client.id.yml index 5a24a279..29a0df74 100644 --- a/config/locales/client.id.yml +++ b/config/locales/client.id.yml @@ -483,8 +483,8 @@ id: x_characters: other: "%{count} Characters" wizard_composer: - show_preview: "Preview Post" - hide_preview: "Edit Post" + show_preview: "Preview" + hide_preview: "Edit" quote_post_title: "Quote whole post" bold_label: "B" bold_title: "Strong" diff --git a/config/locales/client.is.yml b/config/locales/client.is.yml index 383c3f0a..444abda0 100644 --- a/config/locales/client.is.yml +++ b/config/locales/client.is.yml @@ -486,8 +486,8 @@ is: one: "%{count} Character" other: "%{count} Characters" wizard_composer: - show_preview: "Preview Post" - hide_preview: "Edit Post" + show_preview: "Preview" + hide_preview: "Edit" quote_post_title: "Quote whole post" bold_label: "B" bold_title: "Strong" diff --git a/config/locales/client.it.yml b/config/locales/client.it.yml index 6a4afdec..5496dc3a 100644 --- a/config/locales/client.it.yml +++ b/config/locales/client.it.yml @@ -486,8 +486,8 @@ it: one: "%{count} Character" other: "%{count} Characters" wizard_composer: - show_preview: "Preview Post" - hide_preview: "Edit Post" + show_preview: "Preview" + hide_preview: "Edit" quote_post_title: "Quote whole post" bold_label: "B" bold_title: "Strong" diff --git a/config/locales/client.ja.yml b/config/locales/client.ja.yml index 3b18dfe7..2b0a2744 100644 --- a/config/locales/client.ja.yml +++ b/config/locales/client.ja.yml @@ -483,8 +483,8 @@ ja: x_characters: other: "%{count} Characters" wizard_composer: - show_preview: "Preview Post" - hide_preview: "Edit Post" + show_preview: "Preview" + hide_preview: "Edit" quote_post_title: "Quote whole post" bold_label: "B" bold_title: "Strong" diff --git a/config/locales/client.ka.yml b/config/locales/client.ka.yml index ee95af36..a8afb778 100644 --- a/config/locales/client.ka.yml +++ b/config/locales/client.ka.yml @@ -486,8 +486,8 @@ ka: one: "%{count} Character" other: "%{count} Characters" wizard_composer: - show_preview: "Preview Post" - hide_preview: "Edit Post" + show_preview: "Preview" + hide_preview: "Edit" quote_post_title: "Quote whole post" bold_label: "B" bold_title: "Strong" diff --git a/config/locales/client.kk.yml b/config/locales/client.kk.yml index d268cbfd..551b779b 100644 --- a/config/locales/client.kk.yml +++ b/config/locales/client.kk.yml @@ -486,8 +486,8 @@ kk: one: "%{count} Character" other: "%{count} Characters" wizard_composer: - show_preview: "Preview Post" - hide_preview: "Edit Post" + show_preview: "Preview" + hide_preview: "Edit" quote_post_title: "Quote whole post" bold_label: "B" bold_title: "Strong" diff --git a/config/locales/client.km.yml b/config/locales/client.km.yml index d64b2d1c..d6aed018 100644 --- a/config/locales/client.km.yml +++ b/config/locales/client.km.yml @@ -483,8 +483,8 @@ km: x_characters: other: "%{count} Characters" wizard_composer: - show_preview: "Preview Post" - hide_preview: "Edit Post" + show_preview: "Preview" + hide_preview: "Edit" quote_post_title: "Quote whole post" bold_label: "B" bold_title: "Strong" diff --git a/config/locales/client.kn.yml b/config/locales/client.kn.yml index 8950065b..842155bf 100644 --- a/config/locales/client.kn.yml +++ b/config/locales/client.kn.yml @@ -486,8 +486,8 @@ kn: one: "%{count} Character" other: "%{count} Characters" wizard_composer: - show_preview: "Preview Post" - hide_preview: "Edit Post" + show_preview: "Preview" + hide_preview: "Edit" quote_post_title: "Quote whole post" bold_label: "B" bold_title: "Strong" diff --git a/config/locales/client.ko.yml b/config/locales/client.ko.yml index 4d3edcc8..3e036dc9 100644 --- a/config/locales/client.ko.yml +++ b/config/locales/client.ko.yml @@ -483,8 +483,8 @@ ko: x_characters: other: "%{count} Characters" wizard_composer: - show_preview: "Preview Post" - hide_preview: "Edit Post" + show_preview: "Preview" + hide_preview: "Edit" quote_post_title: "Quote whole post" bold_label: "B" bold_title: "Strong" diff --git a/config/locales/client.ku.yml b/config/locales/client.ku.yml index 69694c34..547bce19 100644 --- a/config/locales/client.ku.yml +++ b/config/locales/client.ku.yml @@ -486,8 +486,8 @@ ku: one: "%{count} Character" other: "%{count} Characters" wizard_composer: - show_preview: "Preview Post" - hide_preview: "Edit Post" + show_preview: "Preview" + hide_preview: "Edit" quote_post_title: "Quote whole post" bold_label: "B" bold_title: "Strong" diff --git a/config/locales/client.lo.yml b/config/locales/client.lo.yml index 56206ebb..f347a26b 100644 --- a/config/locales/client.lo.yml +++ b/config/locales/client.lo.yml @@ -483,8 +483,8 @@ lo: x_characters: other: "%{count} Characters" wizard_composer: - show_preview: "Preview Post" - hide_preview: "Edit Post" + show_preview: "Preview" + hide_preview: "Edit" quote_post_title: "Quote whole post" bold_label: "B" bold_title: "Strong" diff --git a/config/locales/client.lt.yml b/config/locales/client.lt.yml index 01de5326..ad5153e5 100644 --- a/config/locales/client.lt.yml +++ b/config/locales/client.lt.yml @@ -492,8 +492,8 @@ lt: many: "%{count} Characters" other: "%{count} Characters" wizard_composer: - show_preview: "Preview Post" - hide_preview: "Edit Post" + show_preview: "Preview" + hide_preview: "Edit" quote_post_title: "Quote whole post" bold_label: "B" bold_title: "Strong" diff --git a/config/locales/client.lv.yml b/config/locales/client.lv.yml index eaaf4e35..c6742f92 100644 --- a/config/locales/client.lv.yml +++ b/config/locales/client.lv.yml @@ -489,8 +489,8 @@ lv: one: "%{count} Character" other: "%{count} Characters" wizard_composer: - show_preview: "Preview Post" - hide_preview: "Edit Post" + show_preview: "Preview" + hide_preview: "Edit" quote_post_title: "Quote whole post" bold_label: "B" bold_title: "Strong" diff --git a/config/locales/client.mk.yml b/config/locales/client.mk.yml index aacd434f..152505a5 100644 --- a/config/locales/client.mk.yml +++ b/config/locales/client.mk.yml @@ -486,8 +486,8 @@ mk: one: "%{count} Character" other: "%{count} Characters" wizard_composer: - show_preview: "Preview Post" - hide_preview: "Edit Post" + show_preview: "Preview" + hide_preview: "Edit" quote_post_title: "Quote whole post" bold_label: "B" bold_title: "Strong" diff --git a/config/locales/client.ml.yml b/config/locales/client.ml.yml index f417dd95..c929f592 100644 --- a/config/locales/client.ml.yml +++ b/config/locales/client.ml.yml @@ -486,8 +486,8 @@ ml: one: "%{count} Character" other: "%{count} Characters" wizard_composer: - show_preview: "Preview Post" - hide_preview: "Edit Post" + show_preview: "Preview" + hide_preview: "Edit" quote_post_title: "Quote whole post" bold_label: "B" bold_title: "Strong" diff --git a/config/locales/client.mn.yml b/config/locales/client.mn.yml index f6493025..736ebbab 100644 --- a/config/locales/client.mn.yml +++ b/config/locales/client.mn.yml @@ -486,8 +486,8 @@ mn: one: "%{count} Character" other: "%{count} Characters" wizard_composer: - show_preview: "Preview Post" - hide_preview: "Edit Post" + show_preview: "Preview" + hide_preview: "Edit" quote_post_title: "Quote whole post" bold_label: "B" bold_title: "Strong" diff --git a/config/locales/client.ms.yml b/config/locales/client.ms.yml index 4f8c9cad..25da928f 100644 --- a/config/locales/client.ms.yml +++ b/config/locales/client.ms.yml @@ -483,8 +483,8 @@ ms: x_characters: other: "%{count} Characters" wizard_composer: - show_preview: "Preview Post" - hide_preview: "Edit Post" + show_preview: "Preview" + hide_preview: "Edit" quote_post_title: "Quote whole post" bold_label: "B" bold_title: "Strong" diff --git a/config/locales/client.ne.yml b/config/locales/client.ne.yml index 33f52ead..004ca84f 100644 --- a/config/locales/client.ne.yml +++ b/config/locales/client.ne.yml @@ -486,8 +486,8 @@ ne: one: "%{count} Character" other: "%{count} Characters" wizard_composer: - show_preview: "Preview Post" - hide_preview: "Edit Post" + show_preview: "Preview" + hide_preview: "Edit" quote_post_title: "Quote whole post" bold_label: "B" bold_title: "Strong" diff --git a/config/locales/client.nl.yml b/config/locales/client.nl.yml index 9e8cfabb..1a4dce06 100644 --- a/config/locales/client.nl.yml +++ b/config/locales/client.nl.yml @@ -486,8 +486,8 @@ nl: one: "%{count} Character" other: "%{count} Characters" wizard_composer: - show_preview: "Preview Post" - hide_preview: "Edit Post" + show_preview: "Preview" + hide_preview: "Edit" quote_post_title: "Quote whole post" bold_label: "B" bold_title: "Strong" diff --git a/config/locales/client.no.yml b/config/locales/client.no.yml index 4e0d5cab..4aec9787 100644 --- a/config/locales/client.no.yml +++ b/config/locales/client.no.yml @@ -486,8 +486,8 @@ one: "%{count} Character" other: "%{count} Characters" wizard_composer: - show_preview: "Preview Post" - hide_preview: "Edit Post" + show_preview: "Preview" + hide_preview: "Edit" quote_post_title: "Quote whole post" bold_label: "B" bold_title: "Strong" diff --git a/config/locales/client.om.yml b/config/locales/client.om.yml index 3c7a7e46..714725e0 100644 --- a/config/locales/client.om.yml +++ b/config/locales/client.om.yml @@ -486,8 +486,8 @@ om: one: "%{count} Character" other: "%{count} Characters" wizard_composer: - show_preview: "Preview Post" - hide_preview: "Edit Post" + show_preview: "Preview" + hide_preview: "Edit" quote_post_title: "Quote whole post" bold_label: "B" bold_title: "Strong" diff --git a/config/locales/client.pa.yml b/config/locales/client.pa.yml index 00236d10..e04954ef 100644 --- a/config/locales/client.pa.yml +++ b/config/locales/client.pa.yml @@ -486,8 +486,8 @@ pa: one: "%{count} Character" other: "%{count} Characters" wizard_composer: - show_preview: "Preview Post" - hide_preview: "Edit Post" + show_preview: "Preview" + hide_preview: "Edit" quote_post_title: "Quote whole post" bold_label: "B" bold_title: "Strong" diff --git a/config/locales/client.pl.yml b/config/locales/client.pl.yml index b665aaf5..4574439c 100644 --- a/config/locales/client.pl.yml +++ b/config/locales/client.pl.yml @@ -492,8 +492,8 @@ pl: many: "%{count} Characters" other: "%{count} Characters" wizard_composer: - show_preview: "Preview Post" - hide_preview: "Edit Post" + show_preview: "Preview" + hide_preview: "Edit" quote_post_title: "Quote whole post" bold_label: "B" bold_title: "Strong" diff --git a/config/locales/client.pt.yml b/config/locales/client.pt.yml index eba3a5ac..48ea6400 100644 --- a/config/locales/client.pt.yml +++ b/config/locales/client.pt.yml @@ -486,8 +486,8 @@ pt: one: "%{count} Character" other: "%{count} Characters" wizard_composer: - show_preview: "Preview Post" - hide_preview: "Edit Post" + show_preview: "Preview" + hide_preview: "Edit" quote_post_title: "Quote whole post" bold_label: "B" bold_title: "Strong" diff --git a/config/locales/client.ro.yml b/config/locales/client.ro.yml index a5136d01..4a910409 100644 --- a/config/locales/client.ro.yml +++ b/config/locales/client.ro.yml @@ -489,8 +489,8 @@ ro: few: "%{count} Characters" other: "%{count} Characters" wizard_composer: - show_preview: "Preview Post" - hide_preview: "Edit Post" + show_preview: "Preview" + hide_preview: "Edit" quote_post_title: "Quote whole post" bold_label: "B" bold_title: "Strong" diff --git a/config/locales/client.ru.yml b/config/locales/client.ru.yml index 85e8ff50..28042ba4 100644 --- a/config/locales/client.ru.yml +++ b/config/locales/client.ru.yml @@ -492,8 +492,8 @@ ru: many: "%{count} Characters" other: "%{count} Characters" wizard_composer: - show_preview: "Preview Post" - hide_preview: "Edit Post" + show_preview: "Preview" + hide_preview: "Edit" quote_post_title: "Quote whole post" bold_label: "B" bold_title: "Strong" diff --git a/config/locales/client.sd.yml b/config/locales/client.sd.yml index add951c7..e41466d1 100644 --- a/config/locales/client.sd.yml +++ b/config/locales/client.sd.yml @@ -486,8 +486,8 @@ sd: one: "%{count} Character" other: "%{count} Characters" wizard_composer: - show_preview: "Preview Post" - hide_preview: "Edit Post" + show_preview: "Preview" + hide_preview: "Edit" quote_post_title: "Quote whole post" bold_label: "B" bold_title: "Strong" diff --git a/config/locales/client.sk.yml b/config/locales/client.sk.yml index c762d3bb..e933063f 100644 --- a/config/locales/client.sk.yml +++ b/config/locales/client.sk.yml @@ -492,8 +492,8 @@ sk: many: "%{count} Characters" other: "%{count} Characters" wizard_composer: - show_preview: "Preview Post" - hide_preview: "Edit Post" + show_preview: "Preview" + hide_preview: "Edit" quote_post_title: "Quote whole post" bold_label: "B" bold_title: "Strong" diff --git a/config/locales/client.sl.yml b/config/locales/client.sl.yml index 80912cb1..cabe86b2 100644 --- a/config/locales/client.sl.yml +++ b/config/locales/client.sl.yml @@ -492,8 +492,8 @@ sl: few: "%{count} Characters" other: "%{count} Characters" wizard_composer: - show_preview: "Preview Post" - hide_preview: "Edit Post" + show_preview: "Preview" + hide_preview: "Edit" quote_post_title: "Quote whole post" bold_label: "B" bold_title: "Strong" diff --git a/config/locales/client.sq.yml b/config/locales/client.sq.yml index 089e4fce..9beb19df 100644 --- a/config/locales/client.sq.yml +++ b/config/locales/client.sq.yml @@ -486,8 +486,8 @@ sq: one: "%{count} Character" other: "%{count} Characters" wizard_composer: - show_preview: "Preview Post" - hide_preview: "Edit Post" + show_preview: "Preview" + hide_preview: "Edit" quote_post_title: "Quote whole post" bold_label: "B" bold_title: "Strong" diff --git a/config/locales/client.sr.yml b/config/locales/client.sr.yml index 5db98108..ea16bad9 100644 --- a/config/locales/client.sr.yml +++ b/config/locales/client.sr.yml @@ -489,8 +489,8 @@ sr-Cyrl: few: "%{count} Characters" other: "%{count} Characters" wizard_composer: - show_preview: "Preview Post" - hide_preview: "Edit Post" + show_preview: "Preview" + hide_preview: "Edit" quote_post_title: "Quote whole post" bold_label: "B" bold_title: "Strong" diff --git a/config/locales/client.sv.yml b/config/locales/client.sv.yml index e19dbb9c..253df9eb 100644 --- a/config/locales/client.sv.yml +++ b/config/locales/client.sv.yml @@ -486,8 +486,8 @@ sv: one: "%{count} Character" other: "%{count} Characters" wizard_composer: - show_preview: "Preview Post" - hide_preview: "Edit Post" + show_preview: "Preview" + hide_preview: "Edit" quote_post_title: "Quote whole post" bold_label: "B" bold_title: "Strong" diff --git a/config/locales/client.sw.yml b/config/locales/client.sw.yml index 5ebb0624..fcd71a16 100644 --- a/config/locales/client.sw.yml +++ b/config/locales/client.sw.yml @@ -486,8 +486,8 @@ sw: one: "%{count} Character" other: "%{count} Characters" wizard_composer: - show_preview: "Preview Post" - hide_preview: "Edit Post" + show_preview: "Preview" + hide_preview: "Edit" quote_post_title: "Quote whole post" bold_label: "B" bold_title: "Strong" diff --git a/config/locales/client.ta.yml b/config/locales/client.ta.yml index 4cfc9a90..4549f503 100644 --- a/config/locales/client.ta.yml +++ b/config/locales/client.ta.yml @@ -486,8 +486,8 @@ ta: one: "%{count} Character" other: "%{count} Characters" wizard_composer: - show_preview: "Preview Post" - hide_preview: "Edit Post" + show_preview: "Preview" + hide_preview: "Edit" quote_post_title: "Quote whole post" bold_label: "B" bold_title: "Strong" diff --git a/config/locales/client.te.yml b/config/locales/client.te.yml index a3b7c377..6c57db33 100644 --- a/config/locales/client.te.yml +++ b/config/locales/client.te.yml @@ -486,8 +486,8 @@ te: one: "%{count} Character" other: "%{count} Characters" wizard_composer: - show_preview: "Preview Post" - hide_preview: "Edit Post" + show_preview: "Preview" + hide_preview: "Edit" quote_post_title: "Quote whole post" bold_label: "B" bold_title: "Strong" diff --git a/config/locales/client.th.yml b/config/locales/client.th.yml index 68b139ee..77363ef5 100644 --- a/config/locales/client.th.yml +++ b/config/locales/client.th.yml @@ -483,8 +483,8 @@ th: x_characters: other: "%{count} Characters" wizard_composer: - show_preview: "Preview Post" - hide_preview: "Edit Post" + show_preview: "Preview" + hide_preview: "Edit" quote_post_title: "Quote whole post" bold_label: "B" bold_title: "Strong" diff --git a/config/locales/client.tl.yml b/config/locales/client.tl.yml index bc3a2557..d4ffd940 100644 --- a/config/locales/client.tl.yml +++ b/config/locales/client.tl.yml @@ -486,8 +486,8 @@ tl: one: "%{count} Character" other: "%{count} Characters" wizard_composer: - show_preview: "Preview Post" - hide_preview: "Edit Post" + show_preview: "Preview" + hide_preview: "Edit" quote_post_title: "Quote whole post" bold_label: "B" bold_title: "Strong" diff --git a/config/locales/client.tr.yml b/config/locales/client.tr.yml index b33e9e0b..8f598a24 100644 --- a/config/locales/client.tr.yml +++ b/config/locales/client.tr.yml @@ -486,8 +486,8 @@ tr: one: "%{count} Character" other: "%{count} Characters" wizard_composer: - show_preview: "Preview Post" - hide_preview: "Edit Post" + show_preview: "Preview" + hide_preview: "Edit" quote_post_title: "Quote whole post" bold_label: "B" bold_title: "Strong" diff --git a/config/locales/client.tt.yml b/config/locales/client.tt.yml index a48bac61..efb89a93 100644 --- a/config/locales/client.tt.yml +++ b/config/locales/client.tt.yml @@ -483,8 +483,8 @@ tt: x_characters: other: "%{count} Characters" wizard_composer: - show_preview: "Preview Post" - hide_preview: "Edit Post" + show_preview: "Preview" + hide_preview: "Edit" quote_post_title: "Quote whole post" bold_label: "B" bold_title: "Strong" diff --git a/config/locales/client.uk.yml b/config/locales/client.uk.yml index 7c1bf2b1..bf7b3b1f 100644 --- a/config/locales/client.uk.yml +++ b/config/locales/client.uk.yml @@ -492,8 +492,8 @@ uk: many: "%{count} Characters" other: "%{count} Characters" wizard_composer: - show_preview: "Preview Post" - hide_preview: "Edit Post" + show_preview: "Preview" + hide_preview: "Edit" quote_post_title: "Quote whole post" bold_label: "B" bold_title: "Strong" diff --git a/config/locales/client.ur.yml b/config/locales/client.ur.yml index 2469e86c..0b2a10ec 100644 --- a/config/locales/client.ur.yml +++ b/config/locales/client.ur.yml @@ -486,8 +486,8 @@ ur: one: "%{count} Character" other: "%{count} Characters" wizard_composer: - show_preview: "Preview Post" - hide_preview: "Edit Post" + show_preview: "Preview" + hide_preview: "Edit" quote_post_title: "Quote whole post" bold_label: "B" bold_title: "Strong" diff --git a/config/locales/client.vi.yml b/config/locales/client.vi.yml index 5dd7ce98..c1f76e2f 100644 --- a/config/locales/client.vi.yml +++ b/config/locales/client.vi.yml @@ -483,8 +483,8 @@ vi: x_characters: other: "%{count} Characters" wizard_composer: - show_preview: "Preview Post" - hide_preview: "Edit Post" + show_preview: "Preview" + hide_preview: "Edit" quote_post_title: "Quote whole post" bold_label: "B" bold_title: "Strong" diff --git a/config/locales/client.yi.yml b/config/locales/client.yi.yml index 94cd0b5d..5d923728 100644 --- a/config/locales/client.yi.yml +++ b/config/locales/client.yi.yml @@ -486,8 +486,8 @@ yi: one: "%{count} Character" other: "%{count} Characters" wizard_composer: - show_preview: "Preview Post" - hide_preview: "Edit Post" + show_preview: "Preview" + hide_preview: "Edit" quote_post_title: "Quote whole post" bold_label: "B" bold_title: "Strong" diff --git a/config/locales/client.zh.yml b/config/locales/client.zh.yml index a8950f7b..cb09de6b 100644 --- a/config/locales/client.zh.yml +++ b/config/locales/client.zh.yml @@ -483,8 +483,8 @@ zh-TW: x_characters: other: "%{count} Characters" wizard_composer: - show_preview: "Preview Post" - hide_preview: "Edit Post" + show_preview: "Preview" + hide_preview: "Edit" quote_post_title: "Quote whole post" bold_label: "B" bold_title: "Strong" diff --git a/config/locales/client.zu.yml b/config/locales/client.zu.yml index b147ac1f..4223f5bd 100644 --- a/config/locales/client.zu.yml +++ b/config/locales/client.zu.yml @@ -486,8 +486,8 @@ zu: one: "%{count} Character" other: "%{count} Characters" wizard_composer: - show_preview: "Preview Post" - hide_preview: "Edit Post" + show_preview: "Preview" + hide_preview: "Edit" quote_post_title: "Quote whole post" bold_label: "B" bold_title: "Strong" From d4da391a4d507aeee5c0ffdb0a57e0212b7e6d46 Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Mon, 1 Aug 2022 18:07:16 +0100 Subject: [PATCH 155/160] Various rebase fixes --- assets/javascripts/wizard-qunit.js | 17 ---------- .../admin/wizard/wizard-custom.scss | 14 -------- .../stylesheets/{admin => common}/admin.scss | 8 ++--- .../{admin/wizard => common/admin}/api.scss | 0 .../wizard => common/admin}/manager.scss | 0 .../wizard => common/admin}/mapper.scss | 0 .../wizard => common/admin}/variables.scss | 0 assets/stylesheets/common/wizard.scss | 14 ++++++++ .../wizard}/autocomplete.scss | 0 .../custom => common/wizard}/badges.scss | 0 .../wizard/custom => common/wizard}/base.scss | 0 .../custom => common/wizard}/buttons.scss | 0 .../custom => common/wizard}/composer.scss | 0 .../custom => common/wizard}/events.scss | 0 .../custom => common/wizard}/field.scss | 0 .../custom => common/wizard}/header.scss | 0 .../custom => common/wizard}/locations.scss | 0 .../wizard}/mentionables.scss | 0 .../custom => common/wizard}/mobile.scss | 0 .../wizard/custom => common/wizard}/step.scss | 0 .../custom => common/wizard}/validators.scss | 0 .../custom => common/wizard}/wizard.scss | 0 plugin.rb | 32 ++----------------- 23 files changed, 20 insertions(+), 65 deletions(-) delete mode 100644 assets/javascripts/wizard-qunit.js delete mode 100644 assets/stylesheets/admin/wizard/wizard-custom.scss rename assets/stylesheets/{admin => common}/admin.scss (99%) rename assets/stylesheets/{admin/wizard => common/admin}/api.scss (100%) rename assets/stylesheets/{admin/wizard => common/admin}/manager.scss (100%) rename assets/stylesheets/{admin/wizard => common/admin}/mapper.scss (100%) rename assets/stylesheets/{admin/wizard => common/admin}/variables.scss (100%) create mode 100644 assets/stylesheets/common/wizard.scss rename assets/stylesheets/{admin/wizard/custom => common/wizard}/autocomplete.scss (100%) rename assets/stylesheets/{admin/wizard/custom => common/wizard}/badges.scss (100%) rename assets/stylesheets/{admin/wizard/custom => common/wizard}/base.scss (100%) rename assets/stylesheets/{admin/wizard/custom => common/wizard}/buttons.scss (100%) rename assets/stylesheets/{admin/wizard/custom => common/wizard}/composer.scss (100%) rename assets/stylesheets/{admin/wizard/custom => common/wizard}/events.scss (100%) rename assets/stylesheets/{admin/wizard/custom => common/wizard}/field.scss (100%) rename assets/stylesheets/{admin/wizard/custom => common/wizard}/header.scss (100%) rename assets/stylesheets/{admin/wizard/custom => common/wizard}/locations.scss (100%) rename assets/stylesheets/{admin/wizard/custom => common/wizard}/mentionables.scss (100%) rename assets/stylesheets/{admin/wizard/custom => common/wizard}/mobile.scss (100%) rename assets/stylesheets/{admin/wizard/custom => common/wizard}/step.scss (100%) rename assets/stylesheets/{admin/wizard/custom => common/wizard}/validators.scss (100%) rename assets/stylesheets/{admin/wizard/custom => common/wizard}/wizard.scss (100%) diff --git a/assets/javascripts/wizard-qunit.js b/assets/javascripts/wizard-qunit.js deleted file mode 100644 index 8aaf23ab..00000000 --- a/assets/javascripts/wizard-qunit.js +++ /dev/null @@ -1,17 +0,0 @@ -//= require env -//= require jquery.debug -//= require ember.debug -//= require route-recognizer -//= require fake_xml_http_request -//= require pretender -//= require qunit -//= require ember-qunit -//= require test-shims -//= require ember-template-compiler - -//= require_tree ./wizard/tests/fixtures -//= require ./wizard/tests/pretender -//= require_tree ./wizard/tests/helpers -//= require_tree ./wizard/tests/acceptance - -//= require ./wizard/tests/bootstrap diff --git a/assets/stylesheets/admin/wizard/wizard-custom.scss b/assets/stylesheets/admin/wizard/wizard-custom.scss deleted file mode 100644 index bcf05477..00000000 --- a/assets/stylesheets/admin/wizard/wizard-custom.scss +++ /dev/null @@ -1,14 +0,0 @@ -@import "custom/base"; -@import "custom/wizard"; -@import "custom/header"; -@import "custom/step"; -@import "custom/badges"; -@import "custom/buttons"; -@import "custom/field"; -@import "custom/validators"; -@import "custom/mobile"; -@import "custom/autocomplete"; -@import "custom/composer"; -@import "custom/events"; -@import "custom/locations"; -@import "custom/mentionables"; diff --git a/assets/stylesheets/admin/admin.scss b/assets/stylesheets/common/admin.scss similarity index 99% rename from assets/stylesheets/admin/admin.scss rename to assets/stylesheets/common/admin.scss index d0106337..386fb372 100644 --- a/assets/stylesheets/admin/admin.scss +++ b/assets/stylesheets/common/admin.scss @@ -1,8 +1,8 @@ -@import "wizard/mapper"; -@import "wizard/manager"; -@import "wizard/api"; +@import "admin/mapper"; +@import "admin/manager"; +@import "admin/api"; @import "common/components/buttons"; -@import "wizard/variables"; +@import "admin/variables"; $expired: #339b18; $info: #038ae7; diff --git a/assets/stylesheets/admin/wizard/api.scss b/assets/stylesheets/common/admin/api.scss similarity index 100% rename from assets/stylesheets/admin/wizard/api.scss rename to assets/stylesheets/common/admin/api.scss diff --git a/assets/stylesheets/admin/wizard/manager.scss b/assets/stylesheets/common/admin/manager.scss similarity index 100% rename from assets/stylesheets/admin/wizard/manager.scss rename to assets/stylesheets/common/admin/manager.scss diff --git a/assets/stylesheets/admin/wizard/mapper.scss b/assets/stylesheets/common/admin/mapper.scss similarity index 100% rename from assets/stylesheets/admin/wizard/mapper.scss rename to assets/stylesheets/common/admin/mapper.scss diff --git a/assets/stylesheets/admin/wizard/variables.scss b/assets/stylesheets/common/admin/variables.scss similarity index 100% rename from assets/stylesheets/admin/wizard/variables.scss rename to assets/stylesheets/common/admin/variables.scss diff --git a/assets/stylesheets/common/wizard.scss b/assets/stylesheets/common/wizard.scss new file mode 100644 index 00000000..391c3a78 --- /dev/null +++ b/assets/stylesheets/common/wizard.scss @@ -0,0 +1,14 @@ +@import "wizard/base"; +@import "wizard/wizard"; +@import "wizard/header"; +@import "wizard/step"; +@import "wizard/badges"; +@import "wizard/buttons"; +@import "wizard/field"; +@import "wizard/validators"; +@import "wizard/mobile"; +@import "wizard/autocomplete"; +@import "wizard/composer"; +@import "wizard/events"; +@import "wizard/locations"; +@import "wizard/mentionables"; diff --git a/assets/stylesheets/admin/wizard/custom/autocomplete.scss b/assets/stylesheets/common/wizard/autocomplete.scss similarity index 100% rename from assets/stylesheets/admin/wizard/custom/autocomplete.scss rename to assets/stylesheets/common/wizard/autocomplete.scss diff --git a/assets/stylesheets/admin/wizard/custom/badges.scss b/assets/stylesheets/common/wizard/badges.scss similarity index 100% rename from assets/stylesheets/admin/wizard/custom/badges.scss rename to assets/stylesheets/common/wizard/badges.scss diff --git a/assets/stylesheets/admin/wizard/custom/base.scss b/assets/stylesheets/common/wizard/base.scss similarity index 100% rename from assets/stylesheets/admin/wizard/custom/base.scss rename to assets/stylesheets/common/wizard/base.scss diff --git a/assets/stylesheets/admin/wizard/custom/buttons.scss b/assets/stylesheets/common/wizard/buttons.scss similarity index 100% rename from assets/stylesheets/admin/wizard/custom/buttons.scss rename to assets/stylesheets/common/wizard/buttons.scss diff --git a/assets/stylesheets/admin/wizard/custom/composer.scss b/assets/stylesheets/common/wizard/composer.scss similarity index 100% rename from assets/stylesheets/admin/wizard/custom/composer.scss rename to assets/stylesheets/common/wizard/composer.scss diff --git a/assets/stylesheets/admin/wizard/custom/events.scss b/assets/stylesheets/common/wizard/events.scss similarity index 100% rename from assets/stylesheets/admin/wizard/custom/events.scss rename to assets/stylesheets/common/wizard/events.scss diff --git a/assets/stylesheets/admin/wizard/custom/field.scss b/assets/stylesheets/common/wizard/field.scss similarity index 100% rename from assets/stylesheets/admin/wizard/custom/field.scss rename to assets/stylesheets/common/wizard/field.scss diff --git a/assets/stylesheets/admin/wizard/custom/header.scss b/assets/stylesheets/common/wizard/header.scss similarity index 100% rename from assets/stylesheets/admin/wizard/custom/header.scss rename to assets/stylesheets/common/wizard/header.scss diff --git a/assets/stylesheets/admin/wizard/custom/locations.scss b/assets/stylesheets/common/wizard/locations.scss similarity index 100% rename from assets/stylesheets/admin/wizard/custom/locations.scss rename to assets/stylesheets/common/wizard/locations.scss diff --git a/assets/stylesheets/admin/wizard/custom/mentionables.scss b/assets/stylesheets/common/wizard/mentionables.scss similarity index 100% rename from assets/stylesheets/admin/wizard/custom/mentionables.scss rename to assets/stylesheets/common/wizard/mentionables.scss diff --git a/assets/stylesheets/admin/wizard/custom/mobile.scss b/assets/stylesheets/common/wizard/mobile.scss similarity index 100% rename from assets/stylesheets/admin/wizard/custom/mobile.scss rename to assets/stylesheets/common/wizard/mobile.scss diff --git a/assets/stylesheets/admin/wizard/custom/step.scss b/assets/stylesheets/common/wizard/step.scss similarity index 100% rename from assets/stylesheets/admin/wizard/custom/step.scss rename to assets/stylesheets/common/wizard/step.scss diff --git a/assets/stylesheets/admin/wizard/custom/validators.scss b/assets/stylesheets/common/wizard/validators.scss similarity index 100% rename from assets/stylesheets/admin/wizard/custom/validators.scss rename to assets/stylesheets/common/wizard/validators.scss diff --git a/assets/stylesheets/admin/wizard/custom/wizard.scss b/assets/stylesheets/common/wizard/wizard.scss similarity index 100% rename from assets/stylesheets/admin/wizard/custom/wizard.scss rename to assets/stylesheets/common/wizard/wizard.scss diff --git a/plugin.rb b/plugin.rb index a8bef80d..ab0fe48c 100644 --- a/plugin.rb +++ b/plugin.rb @@ -8,39 +8,11 @@ # subscription_url: https://coop.pavilion.tech gem 'liquid', '5.0.1', require: true -register_asset 'stylesheets/common/wizard-admin.scss' -register_asset 'stylesheets/common/wizard-mapper.scss' -register_asset 'stylesheets/common/wizard-custom.scss' +register_asset 'stylesheets/common/admin.scss' +register_asset 'stylesheets/common/wizard.scss' enabled_site_setting :custom_wizard_enabled -if Rails.env.production? - Rails.application.config.assets.precompile += %w{ - wizard-custom-guest.js - wizard-custom-start.js - wizard-custom.js - wizard-plugin.js.erb - wizard-raw-templates.js.erb - } -end - -config = Rails.application.config -plugin_asset_path = "#{Rails.root}/plugins/discourse-custom-wizard/assets" -config.assets.paths << "#{plugin_asset_path}/javascripts" -config.assets.paths << "#{plugin_asset_path}/stylesheets/wizard" - -if Rails.env.production? - config.assets.precompile += %w{ - ember_jquery.js - wizard-custom-guest.js - wizard-custom-start.js - wizard-custom.js - wizard-plugin.js.erb - wizard-raw-templates.js.erb - wizard-vendor.js - } -end - if respond_to?(:register_svg_icon) register_svg_icon "far-calendar" register_svg_icon "chevron-right" From 734cc44f6265791814eee4dff21442d405e8494e Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Tue, 9 Aug 2022 15:00:26 +0100 Subject: [PATCH 156/160] Update admin.scss --- assets/stylesheets/common/admin.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/stylesheets/common/admin.scss b/assets/stylesheets/common/admin.scss index 386fb372..6469c364 100644 --- a/assets/stylesheets/common/admin.scss +++ b/assets/stylesheets/common/admin.scss @@ -823,7 +823,7 @@ $error: #ef1700; vertical-align: middle; } -.btn.btn-pavilion-support { +.btn[href].btn-pavilion-support { background: var(--pavilion-primary); color: var(--pavilion-secondary); From c79dee3d1653da818f1835c2e361ac95f5bcbdb8 Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Fri, 23 Sep 2022 15:52:05 +0200 Subject: [PATCH 157/160] Add community subscription as an explicit type --- .rubocop.yml | 6 ++ .../wizard-subscription-badge.js.es6 | 2 +- .../wizard-subscription-container.js.es6 | 4 +- .../components/wizard-subscription-cta.js.es6 | 34 +++++++++++ .../wizard-subscription-selector.js.es6 | 47 +++++++-------- .../controllers/admin-wizards.js.es6 | 4 +- .../discourse/lib/wizard-subscription.js.es6 | 22 ------- .../discourse/mixins/subscription.js.es6 | 18 +++++- .../admin-wizards-submissions-show.js.es6 | 1 - .../discourse/templates/admin-wizards.hbs | 10 ++-- .../wizard-subscription-container.hbs | 2 +- .../components/wizard-subscription-cta.hbs | 1 + .../wizard-subscription-selector-header.hbs | 6 +- .../wizard-subscription-selector-row.hbs | 6 +- assets/stylesheets/common/admin.scss | 8 ++- .../stylesheets/common/admin/variables.scss | 2 + config/locales/client.en.yml | 18 ++++-- lib/custom_wizard/subscription.rb | 57 +++++++++++++------ .../custom_wizard/submission_spec.rb | 2 +- .../custom_wizard/subscription_spec.rb | 16 +++++- 20 files changed, 176 insertions(+), 90 deletions(-) create mode 100644 assets/javascripts/discourse/components/wizard-subscription-cta.js.es6 delete mode 100644 assets/javascripts/discourse/lib/wizard-subscription.js.es6 create mode 100644 assets/javascripts/discourse/templates/components/wizard-subscription-cta.hbs diff --git a/.rubocop.yml b/.rubocop.yml index d46296cf..69fcfc56 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,2 +1,8 @@ inherit_gem: rubocop-discourse: default.yml + +RSpec/ContextWording: + Enabled: false + +RSpec/DescribeClass: + Enabled: false diff --git a/assets/javascripts/discourse/components/wizard-subscription-badge.js.es6 b/assets/javascripts/discourse/components/wizard-subscription-badge.js.es6 index 87c985a4..301c618e 100644 --- a/assets/javascripts/discourse/components/wizard-subscription-badge.js.es6 +++ b/assets/javascripts/discourse/components/wizard-subscription-badge.js.es6 @@ -11,7 +11,7 @@ export default Component.extend(Subscription, { @discourseComputed("subscriptionType") i18nKey(type) { - return `admin.wizard.subscription_container.type.${type ? type : "none"}`; + return `admin.wizard.subscription.type.${type ? type : "none"}`; }, @discourseComputed("i18nKey") diff --git a/assets/javascripts/discourse/components/wizard-subscription-container.js.es6 b/assets/javascripts/discourse/components/wizard-subscription-container.js.es6 index ccaf2f48..5cc6b17c 100644 --- a/assets/javascripts/discourse/components/wizard-subscription-container.js.es6 +++ b/assets/javascripts/discourse/components/wizard-subscription-container.js.es6 @@ -12,14 +12,14 @@ export default Component.extend(Subscription, { @discourseComputed("subscribed") subscribedLabel(subscribed) { - return `admin.wizard.subscription_container.${ + return `admin.wizard.subscription.${ subscribed ? "subscribed" : "not_subscribed" }.label`; }, @discourseComputed("subscribed") subscribedTitle(subscribed) { - return `admin.wizard.subscription_container.${ + return `admin.wizard.subscription.${ subscribed ? "subscribed" : "not_subscribed" }.title`; }, diff --git a/assets/javascripts/discourse/components/wizard-subscription-cta.js.es6 b/assets/javascripts/discourse/components/wizard-subscription-cta.js.es6 new file mode 100644 index 00000000..4fb3002b --- /dev/null +++ b/assets/javascripts/discourse/components/wizard-subscription-cta.js.es6 @@ -0,0 +1,34 @@ +import Component from "@ember/component"; +import discourseComputed from "discourse-common/utils/decorators"; +import Subscription from "../mixins/subscription"; +import I18n from "I18n"; + +export default Component.extend(Subscription, { + tagName: "a", + classNameBindings: [":btn", ":btn-pavilion-support", "subscriptionType"], + attributeBindings: ["title"], + + @discourseComputed("subscribed") + i18nKey(subscribed) { + return `admin.wizard.subscription.cta.${subscribed ? "subscribed" : "none"}`; + }, + + @discourseComputed("subscribed") + icon(subscribed) { + return subscribed ? "far-life-ring" : "external-link-alt"; + }, + + @discourseComputed("i18nKey") + title(i18nKey) { + return I18n.t(`${i18nKey}.title`); + }, + + @discourseComputed("i18nKey") + label(i18nKey) { + return I18n.t(`${i18nKey}.label`); + }, + + click() { + window.open(this.subscriptionCtaLink, "_blank").focus(); + }, +}); diff --git a/assets/javascripts/discourse/components/wizard-subscription-selector.js.es6 b/assets/javascripts/discourse/components/wizard-subscription-selector.js.es6 index 8ef6896a..06f705b3 100644 --- a/assets/javascripts/discourse/components/wizard-subscription-selector.js.es6 +++ b/assets/javascripts/discourse/components/wizard-subscription-selector.js.es6 @@ -1,10 +1,6 @@ import SingleSelectComponent from "select-kit/components/single-select"; import Subscription from "../mixins/subscription"; import wizardSchema from "discourse/plugins/discourse-custom-wizard/discourse/lib/wizard-schema"; -import { - subscriptionTypeSufficient, - subscriptionTypes, -} from "discourse/plugins/discourse-custom-wizard/discourse/lib/wizard-subscription"; import discourseComputed from "discourse-common/utils/decorators"; import I18n from "I18n"; @@ -29,45 +25,50 @@ export default SingleSelectComponent.extend(Subscription, { caretDownIcon: "caret-down", }, - requiredSubscriptionType(feature, attribute, value) { + allowedSubscriptionTypes(feature, attribute, value) { let attributes = this.subscriptionAttributes[feature]; if (!attributes || !attributes[attribute]) { - return null; + return ['none']; } - - let requiredType = null; - Object.keys(attributes[attribute]).some((subscriptionType) => { + let allowedTypes = []; + Object.keys(attributes[attribute]).forEach((subscriptionType) => { let values = attributes[attribute][subscriptionType]; if (values[0] === "*" || values.includes(value)) { - if (subscriptionTypes.includes(subscriptionType)) { - requiredType = subscriptionType; - } - return true; + allowedTypes.push(subscriptionType); } - return false; }); - - return requiredType; + return allowedTypes; }, @discourseComputed("feature", "attribute") content(feature, attribute) { return wizardSchema[feature][attribute] .map((value) => { - let requiredSubscriptionType = this.requiredSubscriptionType( + let allowedSubscriptionTypes = this.allowedSubscriptionTypes( feature, attribute, value ); - return { + + let subscriptionRequired = allowedSubscriptionTypes.length && + !allowedSubscriptionTypes.includes('none'); + + let attrs = { id: value, name: I18n.t(nameKey(feature, attribute, value)), - subscriptionType: requiredSubscriptionType, - disabled: !subscriptionTypeSufficient( - this.subscriptionType, - requiredSubscriptionType - ), + subscriptionRequired }; + + if (subscriptionRequired) { + let subscribed = allowedSubscriptionTypes.includes(this.subscriptionType); + let selectorKey = subscribed ? "subscribed" : "not_subscribed"; + let selectorLabel = `admin.wizard.subscription.${selectorKey}.selector`; + + attrs.disabled = !subscribed; + attrs.selectorLabel = selectorLabel; + } + + return attrs; }) .sort(function (a, b) { if (a.subscriptionType && !b.subscriptionType) { diff --git a/assets/javascripts/discourse/controllers/admin-wizards.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards.js.es6 index f99c06cc..15c6ea85 100644 --- a/assets/javascripts/discourse/controllers/admin-wizards.js.es6 +++ b/assets/javascripts/discourse/controllers/admin-wizards.js.es6 @@ -1,7 +1,9 @@ import Controller from "@ember/controller"; -import { equal } from "@ember/object/computed"; +import { equal, or } from "@ember/object/computed"; export default Controller.extend({ businessSubscription: equal("subscriptionType", "business"), + communitySubscription: equal("subscriptionType", "community"), standardSubscription: equal("subscriptionType", "standard"), + showApi: or('businessSubscription', 'communitySubscription') }); diff --git a/assets/javascripts/discourse/lib/wizard-subscription.js.es6 b/assets/javascripts/discourse/lib/wizard-subscription.js.es6 deleted file mode 100644 index adecf063..00000000 --- a/assets/javascripts/discourse/lib/wizard-subscription.js.es6 +++ /dev/null @@ -1,22 +0,0 @@ -const subscriptionTypes = ["standard", "business"]; - -function subscriptionTypeSufficient(subscriptionType, requiredType) { - if (requiredType && !subscriptionType) { - return false; - } - if (requiredType === "none" || requiredType === null) { - return true; - } - if ( - requiredType === "standard" && - subscriptionTypes.includes(subscriptionType) - ) { - return true; - } - if (requiredType === "business" && subscriptionType === "business") { - return true; - } - return false; -} - -export { subscriptionTypeSufficient, subscriptionTypes }; diff --git a/assets/javascripts/discourse/mixins/subscription.js.es6 b/assets/javascripts/discourse/mixins/subscription.js.es6 index 574f8585..072b57e9 100644 --- a/assets/javascripts/discourse/mixins/subscription.js.es6 +++ b/assets/javascripts/discourse/mixins/subscription.js.es6 @@ -3,8 +3,12 @@ import { getOwner } from "discourse-common/lib/get-owner"; import { readOnly } from "@ember/object/computed"; import discourseComputed from "discourse-common/utils/decorators"; +const PRODUCT_PAGE = "https://custom-wizard.pavilion.tech"; +const SUPPORT_MESSAGE = "https://coop.pavilion.tech/new-message?username=support&title=Custom%20Wizard%20Support"; +const MANAGER_CATEGORY = "https://discourse.pluginmanager.org/c/discourse-custom-wizard"; + export default Mixin.create({ - subscriptionLandingUrl: "https://custom-wizard.pavilion.tech", + subscriptionLandingUrl: PRODUCT_PAGE, subscriptionClientUrl: "/admin/plugins/subscription-client", @discourseComputed @@ -15,6 +19,7 @@ export default Mixin.create({ subscribed: readOnly("adminWizards.subscribed"), subscriptionType: readOnly("adminWizards.subscriptionType"), businessSubscription: readOnly("adminWizards.businessSubscription"), + communitySubscription: readOnly("adminWizards.communitySubscription"), standardSubscription: readOnly("adminWizards.standardSubscription"), subscriptionAttributes: readOnly("adminWizards.subscriptionAttributes"), subscriptionClientInstalled: readOnly( @@ -27,4 +32,15 @@ export default Mixin.create({ ? this.subscriptionClientUrl : this.subscriptionLandingUrl; }, + + @discourseComputed("subscriptionType") + subscriptionCtaLink(subscriptionType) { + switch (subscriptionType) { + case "none": return PRODUCT_PAGE; + case "standard": return SUPPORT_MESSAGE; + case "business": return SUPPORT_MESSAGE; + case "community": return MANAGER_CATEGORY; + default: return PRODUCT_PAGE; + } + }, }); diff --git a/assets/javascripts/discourse/routes/admin-wizards-submissions-show.js.es6 b/assets/javascripts/discourse/routes/admin-wizards-submissions-show.js.es6 index 17b03e78..5a9d6046 100644 --- a/assets/javascripts/discourse/routes/admin-wizards-submissions-show.js.es6 +++ b/assets/javascripts/discourse/routes/admin-wizards-submissions-show.js.es6 @@ -2,7 +2,6 @@ import { A } from "@ember/array"; import EmberObject from "@ember/object"; import CustomWizardAdmin from "../models/custom-wizard-admin"; import DiscourseRoute from "discourse/routes/discourse"; -import CustomWizard from "../models/custom-wizard"; export default DiscourseRoute.extend({ model(params) { diff --git a/assets/javascripts/discourse/templates/admin-wizards.hbs b/assets/javascripts/discourse/templates/admin-wizards.hbs index 236444cd..cea77942 100644 --- a/assets/javascripts/discourse/templates/admin-wizards.hbs +++ b/assets/javascripts/discourse/templates/admin-wizards.hbs @@ -2,17 +2,15 @@ {{nav-item route="adminWizardsWizard" label="admin.wizard.nav_label"}} {{nav-item route="adminWizardsCustomFields" label="admin.wizard.custom_field.nav_label"}} {{nav-item route="adminWizardsSubmissions" label="admin.wizard.submissions.nav_label"}} - {{#if businessSubscription}} + {{#if showApi}} {{nav-item route="adminWizardsApi" label="admin.wizard.api.nav_label"}} {{/if}} {{nav-item route="adminWizardsLogs" label="admin.wizard.log.nav_label"}} {{nav-item route="adminWizardsManager" label="admin.wizard.manager.nav_label"}} -
- - - Custom Wizard Subscriptions Are Coming! - +
+ {{wizard-subscription-badge}} + {{wizard-subscription-cta}}
{{/admin-nav}} diff --git a/assets/javascripts/discourse/templates/components/wizard-subscription-container.hbs b/assets/javascripts/discourse/templates/components/wizard-subscription-container.hbs index 78be031f..01d436f5 100644 --- a/assets/javascripts/discourse/templates/components/wizard-subscription-container.hbs +++ b/assets/javascripts/discourse/templates/components/wizard-subscription-container.hbs @@ -1,5 +1,5 @@
-

{{i18n "admin.wizard.subscription_container.title"}}

+

{{i18n "admin.wizard.subscription.title"}}

{{d-icon subscribedIcon}} diff --git a/assets/javascripts/discourse/templates/components/wizard-subscription-cta.hbs b/assets/javascripts/discourse/templates/components/wizard-subscription-cta.hbs new file mode 100644 index 00000000..00569756 --- /dev/null +++ b/assets/javascripts/discourse/templates/components/wizard-subscription-cta.hbs @@ -0,0 +1 @@ +{{d-icon icon}}{{label}} diff --git a/assets/javascripts/discourse/templates/components/wizard-subscription-selector/wizard-subscription-selector-header.hbs b/assets/javascripts/discourse/templates/components/wizard-subscription-selector/wizard-subscription-selector-header.hbs index 708b0887..d91e90f1 100644 --- a/assets/javascripts/discourse/templates/components/wizard-subscription-selector/wizard-subscription-selector-header.hbs +++ b/assets/javascripts/discourse/templates/components/wizard-subscription-selector/wizard-subscription-selector-header.hbs @@ -7,10 +7,8 @@ shouldDisplayClearableButton=shouldDisplayClearableButton }} - {{#if selectedContent.subscriptionType}} - - {{selectedContent.subscriptionType}} - + {{#if subscriptionRequired}} + {{i18n selectorLabel}} {{/if}} {{d-icon caretIcon class="caret-icon"}} diff --git a/assets/javascripts/discourse/templates/components/wizard-subscription-selector/wizard-subscription-selector-row.hbs b/assets/javascripts/discourse/templates/components/wizard-subscription-selector/wizard-subscription-selector-row.hbs index 1db906e6..de24fd75 100644 --- a/assets/javascripts/discourse/templates/components/wizard-subscription-selector/wizard-subscription-selector-row.hbs +++ b/assets/javascripts/discourse/templates/components/wizard-subscription-selector/wizard-subscription-selector-row.hbs @@ -9,9 +9,7 @@
{{html-safe label}} - {{#if item.subscriptionType}} - - {{item.subscriptionType}} - + {{#if item.subscriptionRequired}} + {{i18n item.selectorLabel}} {{/if}}
diff --git a/assets/stylesheets/common/admin.scss b/assets/stylesheets/common/admin.scss index 43fe9b5c..9c23d498 100644 --- a/assets/stylesheets/common/admin.scss +++ b/assets/stylesheets/common/admin.scss @@ -841,7 +841,7 @@ $error: #ef1700; vertical-align: middle; } -.btn[href].btn-pavilion-support { +.btn.btn-pavilion-support { background: var(--pavilion-primary); color: var(--pavilion-secondary); @@ -920,6 +920,12 @@ $error: #ef1700; color: $secondary; } + &.community { + background-color: $subscription_community; + border: 1.5px solid $pavilion_primary; + color: $pavilion_primary; + } + .d-icon { margin-right: 0.75em; } diff --git a/assets/stylesheets/common/admin/variables.scss b/assets/stylesheets/common/admin/variables.scss index 9bb93798..33a67c2d 100644 --- a/assets/stylesheets/common/admin/variables.scss +++ b/assets/stylesheets/common/admin/variables.scss @@ -4,6 +4,7 @@ $pavilion_warning: rgb(243, 163, 61); $subscription_standard: $pavilion_primary; $subscription_business: #333; +$subscription_community: #fff; :root { --pavilion-primary: #{$pavilion_primary}; @@ -11,4 +12,5 @@ $subscription_business: #333; --pavilion-warning: #{$pavilion_warning}; --subscription-standard: #{$subscription_standard}; --subscription-business: #{$subscription_business}; + --subscription-community: #{$subscription_community}; } diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 82f7da70..9b6efeae 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -129,9 +129,6 @@ en: edit_columns: "Edit Columns" expand_text: "Read More" collapse_text: "Show Less" - support_button: - title: "Request Support" - label: "Support" category_settings: custom_wizard: title: "Custom Wizard" @@ -535,14 +532,16 @@ en: destroy: Destroy destroyed: destroyed - subscription_container: + subscription: title: Subscriber Features subscribed: label: Subscribed title: You're subscribed and can use these features + selector: subscribed not_subscribed: label: Not Subscribed title: Subscribe to use these features + selector: not subscribed type: none: label: Not Subscribed @@ -553,6 +552,17 @@ en: standard: label: Standard title: There is a Custom Wizard Standard subscription active on this forum. + community: + label: Community + title: There is a Custom Wizard Community subscription active on this forum. + cta: + none: + label: Get a Subscription + title: Get a subscription for this forum. + subscribed: + label: Support + title: Get support for your subscription. + wizard_js: group: diff --git a/lib/custom_wizard/subscription.rb b/lib/custom_wizard/subscription.rb index 114f6743..06e8bd76 100644 --- a/lib/custom_wizard/subscription.rb +++ b/lib/custom_wizard/subscription.rb @@ -1,7 +1,8 @@ # frozen_string_literal: true class CustomWizard::Subscription - STANDARD_PRODUCT_ID = 'prod_LNAGVAaIqDsHmB' - BUSINESS_PRODUCT_ID = 'prod_LNABQ50maBQ1pY' + STANDARD_PRODUCT_ID = 'prod_MH11woVoZU5AWb' + BUSINESS_PRODUCT_ID = 'prod_MH0wT627okh3Ef' + COMMUNITY_PRODUCT_ID = 'prod_MU7l9EjxhaukZ7' def self.attributes { @@ -9,75 +10,88 @@ class CustomWizard::Subscription required: { none: [], standard: ['*'], - business: ['*'] + business: ['*'], + community: ['*'] }, permitted: { none: [], standard: ['*'], - business: ['*'] + business: ['*'], + community: ['*'] } }, step: { condition: { none: [], standard: ['*'], - business: ['*'] + business: ['*'], + community: ['*'] }, index: { none: [], standard: ['*'], - business: ['*'] + business: ['*'], + community: ['*'] }, required_data: { none: [], standard: ['*'], - business: ['*'] + business: ['*'], + community: ['*'] }, permitted_params: { none: [], standard: ['*'], - business: ['*'] + business: ['*'], + community: ['*'] } }, field: { condition: { none: [], standard: ['*'], - business: ['*'] + business: ['*'], + community: ['*'] }, index: { none: [], standard: ['*'], - business: ['*'] + business: ['*'], + community: ['*'] }, type: { none: ['text', 'textarea', 'text_only', 'date', 'time', 'date_time', 'number', 'checkbox', 'dropdown', 'upload'], standard: ['*'], - business: ['*'] + business: ['*'], + community: ['*'] }, realtime_validations: { none: [], standard: ['*'], - business: ['*'] + business: ['*'], + community: ['*'] } }, action: { type: { none: ['create_topic', 'update_profile', 'open_composer', 'route_to'], standard: ['create_topic', 'update_profile', 'open_composer', 'route_to', 'send_message', 'watch_categories', 'add_to_group'], - business: ['*'] + business: ['*'], + community: ['*'] } }, custom_field: { klass: { none: ['topic', 'post'], standard: ['topic', 'post'], - business: ['*'] + business: ['*'], + community: ['*'] }, type: { none: ['string', 'boolean', 'integer'], standard: ['string', 'boolean', 'integer'], - business: ['*'] + business: ['*'], + community: ['*'] } } } @@ -109,10 +123,11 @@ class CustomWizard::Subscription return :none unless subscribed? return :standard if standard? return :business if business? + return :community if community? end def subscribed? - standard? || business? + standard? || business? || community? end def standard? @@ -123,6 +138,10 @@ class CustomWizard::Subscription @subscription.product_id === BUSINESS_PRODUCT_ID end + def community? + @subscription.product_id === COMMUNITY_PRODUCT_ID + end + def client_installed? defined?(SubscriptionClient) == 'constant' && SubscriptionClient.class == Module end @@ -132,7 +151,7 @@ class CustomWizard::Subscription if client_installed? subscription = SubscriptionClientSubscription.active - .where(product_id: [STANDARD_PRODUCT_ID, BUSINESS_PRODUCT_ID]) + .where(product_id: [STANDARD_PRODUCT_ID, BUSINESS_PRODUCT_ID, COMMUNITY_PRODUCT_ID]) .order("product_id = '#{BUSINESS_PRODUCT_ID}' DESC") .first end @@ -152,6 +171,10 @@ class CustomWizard::Subscription new.business? end + def self.community? + new.community? + end + def self.standard? new.standard? end diff --git a/spec/components/custom_wizard/submission_spec.rb b/spec/components/custom_wizard/submission_spec.rb index 97732c7a..48459a08 100644 --- a/spec/components/custom_wizard/submission_spec.rb +++ b/spec/components/custom_wizard/submission_spec.rb @@ -17,7 +17,7 @@ describe CustomWizard::Submission do ).to eq("I am user submission") end - context "#list" do + describe "#list" do before do template_json_2 = template_json.dup template_json_2["id"] = "super_mega_fun_wizard_2" diff --git a/spec/components/custom_wizard/subscription_spec.rb b/spec/components/custom_wizard/subscription_spec.rb index 984eff9f..5f06397b 100644 --- a/spec/components/custom_wizard/subscription_spec.rb +++ b/spec/components/custom_wizard/subscription_spec.rb @@ -96,7 +96,21 @@ describe CustomWizard::Subscription do expect(described_class.type).to eq(:business) end - it "business are included" do + it "business features are included" do + expect(described_class.includes?(:action, :type, 'create_category')).to eq(true) + end + end + + context "with community subscription" do + before do + SubscriptionClientSubscription.stubs(:product_id).returns(CustomWizard::Subscription::COMMUNITY_PRODUCT_ID) + end + + it "detects community type" do + expect(described_class.type).to eq(:community) + end + + it "community features are included" do expect(described_class.includes?(:action, :type, 'create_category')).to eq(true) end end From a3db405d937249cd15a3ef3f0ea17995de27e219 Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Fri, 23 Sep 2022 15:54:43 +0200 Subject: [PATCH 158/160] Apply prettier --- .../components/wizard-subscription-cta.js.es6 | 4 +++- .../wizard-subscription-selector.js.es6 | 13 +++++++----- .../controllers/admin-wizards.js.es6 | 2 +- .../discourse/mixins/subscription.js.es6 | 21 ++++++++++++------- assets/stylesheets/common/wizard/field.scss | 2 +- 5 files changed, 27 insertions(+), 15 deletions(-) diff --git a/assets/javascripts/discourse/components/wizard-subscription-cta.js.es6 b/assets/javascripts/discourse/components/wizard-subscription-cta.js.es6 index 4fb3002b..f483fbe8 100644 --- a/assets/javascripts/discourse/components/wizard-subscription-cta.js.es6 +++ b/assets/javascripts/discourse/components/wizard-subscription-cta.js.es6 @@ -10,7 +10,9 @@ export default Component.extend(Subscription, { @discourseComputed("subscribed") i18nKey(subscribed) { - return `admin.wizard.subscription.cta.${subscribed ? "subscribed" : "none"}`; + return `admin.wizard.subscription.cta.${ + subscribed ? "subscribed" : "none" + }`; }, @discourseComputed("subscribed") diff --git a/assets/javascripts/discourse/components/wizard-subscription-selector.js.es6 b/assets/javascripts/discourse/components/wizard-subscription-selector.js.es6 index 06f705b3..53f7d19c 100644 --- a/assets/javascripts/discourse/components/wizard-subscription-selector.js.es6 +++ b/assets/javascripts/discourse/components/wizard-subscription-selector.js.es6 @@ -28,7 +28,7 @@ export default SingleSelectComponent.extend(Subscription, { allowedSubscriptionTypes(feature, attribute, value) { let attributes = this.subscriptionAttributes[feature]; if (!attributes || !attributes[attribute]) { - return ['none']; + return ["none"]; } let allowedTypes = []; Object.keys(attributes[attribute]).forEach((subscriptionType) => { @@ -50,17 +50,20 @@ export default SingleSelectComponent.extend(Subscription, { value ); - let subscriptionRequired = allowedSubscriptionTypes.length && - !allowedSubscriptionTypes.includes('none'); + let subscriptionRequired = + allowedSubscriptionTypes.length && + !allowedSubscriptionTypes.includes("none"); let attrs = { id: value, name: I18n.t(nameKey(feature, attribute, value)), - subscriptionRequired + subscriptionRequired, }; if (subscriptionRequired) { - let subscribed = allowedSubscriptionTypes.includes(this.subscriptionType); + let subscribed = allowedSubscriptionTypes.includes( + this.subscriptionType + ); let selectorKey = subscribed ? "subscribed" : "not_subscribed"; let selectorLabel = `admin.wizard.subscription.${selectorKey}.selector`; diff --git a/assets/javascripts/discourse/controllers/admin-wizards.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards.js.es6 index 15c6ea85..518893b6 100644 --- a/assets/javascripts/discourse/controllers/admin-wizards.js.es6 +++ b/assets/javascripts/discourse/controllers/admin-wizards.js.es6 @@ -5,5 +5,5 @@ export default Controller.extend({ businessSubscription: equal("subscriptionType", "business"), communitySubscription: equal("subscriptionType", "community"), standardSubscription: equal("subscriptionType", "standard"), - showApi: or('businessSubscription', 'communitySubscription') + showApi: or("businessSubscription", "communitySubscription"), }); diff --git a/assets/javascripts/discourse/mixins/subscription.js.es6 b/assets/javascripts/discourse/mixins/subscription.js.es6 index 072b57e9..34276c3f 100644 --- a/assets/javascripts/discourse/mixins/subscription.js.es6 +++ b/assets/javascripts/discourse/mixins/subscription.js.es6 @@ -4,8 +4,10 @@ import { readOnly } from "@ember/object/computed"; import discourseComputed from "discourse-common/utils/decorators"; const PRODUCT_PAGE = "https://custom-wizard.pavilion.tech"; -const SUPPORT_MESSAGE = "https://coop.pavilion.tech/new-message?username=support&title=Custom%20Wizard%20Support"; -const MANAGER_CATEGORY = "https://discourse.pluginmanager.org/c/discourse-custom-wizard"; +const SUPPORT_MESSAGE = + "https://coop.pavilion.tech/new-message?username=support&title=Custom%20Wizard%20Support"; +const MANAGER_CATEGORY = + "https://discourse.pluginmanager.org/c/discourse-custom-wizard"; export default Mixin.create({ subscriptionLandingUrl: PRODUCT_PAGE, @@ -36,11 +38,16 @@ export default Mixin.create({ @discourseComputed("subscriptionType") subscriptionCtaLink(subscriptionType) { switch (subscriptionType) { - case "none": return PRODUCT_PAGE; - case "standard": return SUPPORT_MESSAGE; - case "business": return SUPPORT_MESSAGE; - case "community": return MANAGER_CATEGORY; - default: return PRODUCT_PAGE; + case "none": + return PRODUCT_PAGE; + case "standard": + return SUPPORT_MESSAGE; + case "business": + return SUPPORT_MESSAGE; + case "community": + return MANAGER_CATEGORY; + default: + return PRODUCT_PAGE; } }, }); diff --git a/assets/stylesheets/common/wizard/field.scss b/assets/stylesheets/common/wizard/field.scss index a127b6a5..f710632b 100644 --- a/assets/stylesheets/common/wizard/field.scss +++ b/assets/stylesheets/common/wizard/field.scss @@ -69,7 +69,7 @@ body.custom-wizard { } & > .field-label + .field-description { - margin-left: .5em; + margin-left: 0.5em; } } From 8f7fae0e7c82dd4fec0c0ca21f50efc935ca7a6f Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Fri, 23 Sep 2022 17:27:11 +0200 Subject: [PATCH 159/160] Remove beta tag --- plugin.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin.rb b/plugin.rb index e7d7c726..d6d4d8ea 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.0-beta.1 +# version: 2.0.0 # authors: Angus McLeod, Faizaan Gagan, Robert Barrow, Keegan George # url: https://github.com/paviliondev/discourse-custom-wizard # contact_emails: development@pavilion.tech From 3bacb09f829c28ac3f31592fd875e3724de976cb Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Fri, 23 Sep 2022 17:30:55 +0200 Subject: [PATCH 160/160] Acknowledge Kailtlin as an author --- plugin.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin.rb b/plugin.rb index d6d4d8ea..44d2c152 100644 --- a/plugin.rb +++ b/plugin.rb @@ -2,7 +2,7 @@ # name: discourse-custom-wizard # about: Forms for Discourse. Better onboarding, structured posting, data enrichment, automated actions and much more. # version: 2.0.0 -# authors: Angus McLeod, Faizaan Gagan, Robert Barrow, Keegan George +# authors: Angus McLeod, Faizaan Gagan, Robert Barrow, Keegan George, Kaitlin Maddever # url: https://github.com/paviliondev/discourse-custom-wizard # contact_emails: development@pavilion.tech # subscription_url: https://coop.pavilion.tech