From a27c222dc66bc16e2efb996ef2d8b39ced3b8b3e Mon Sep 17 00:00:00 2001 From: angusmcleod Date: Tue, 10 Aug 2021 14:45:23 +0800 Subject: [PATCH 01/15] 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 02/15] 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 03/15] 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 04/15] 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 05/15] 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 06/15] 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 6b1e7568c154e545a4bdaabbef0259ddb90ae5ba Mon Sep 17 00:00:00 2001 From: angusmcleod Date: Wed, 1 Sep 2021 10:19:00 +0800 Subject: [PATCH 07/15] 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 6ef333a6573df53b67c50951f383f32aeb19b4c8 Mon Sep 17 00:00:00 2001 From: angusmcleod Date: Fri, 3 Sep 2021 16:46:32 +0800 Subject: [PATCH 08/15] 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}} 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 09/15] 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 23c4b45195c4efebfaea454a15081ae405f3ed64 Mon Sep 17 00:00:00 2001 From: angusmcleod Date: Tue, 7 Sep 2021 20:06:13 +0800 Subject: [PATCH 10/15] 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 11/15] 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 12/15] 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 13/15] 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 14/15] 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 15/15] 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"