diff --git a/app/controllers/custom_wizard/admin/admin.rb b/app/controllers/custom_wizard/admin/admin.rb index e011e094..867be56c 100644 --- a/app/controllers/custom_wizard/admin/admin.rb +++ b/app/controllers/custom_wizard/admin/admin.rb @@ -3,8 +3,12 @@ class CustomWizard::AdminController < ::Admin::AdminController before_action :ensure_admin def index + subcription = CustomWizard::Subscription.new render_json_dump( - api_section: ["business"].include?(CustomWizard::Subscription.type) + subscribed: subcription.subscribed?, + subscription_type: subcription.type, + subscription_attributes: CustomWizard::Subscription.attributes, + subscription_client_installed: subcription.client_installed? ) end diff --git a/app/controllers/custom_wizard/admin/custom_fields.rb b/app/controllers/custom_wizard/admin/custom_fields.rb index 1cd20f5d..111e9faf 100644 --- a/app/controllers/custom_wizard/admin/custom_fields.rb +++ b/app/controllers/custom_wizard/admin/custom_fields.rb @@ -2,9 +2,7 @@ class CustomWizard::AdminCustomFieldsController < CustomWizard::AdminController def index render_json_dump( - custom_fields: custom_field_list, - subscribed: CustomWizard::Subscription.subscribed?, - subscription: CustomWizard::Subscription.type + custom_fields: custom_field_list ) end diff --git a/app/controllers/custom_wizard/admin/wizard.rb b/app/controllers/custom_wizard/admin/wizard.rb index ad63209a..0a59e02b 100644 --- a/app/controllers/custom_wizard/admin/wizard.rb +++ b/app/controllers/custom_wizard/admin/wizard.rb @@ -10,9 +10,7 @@ class CustomWizard::AdminWizardController < CustomWizard::AdminController ), field_types: CustomWizard::Field.types, realtime_validations: CustomWizard::RealtimeValidation.types, - custom_fields: custom_field_list, - subscribed: CustomWizard::Subscription.subscribed?, - subscription: CustomWizard::Subscription.type + custom_fields: custom_field_list ) end diff --git a/assets/javascripts/discourse/components/custom-field-input.js.es6 b/assets/javascripts/discourse/components/custom-field-input.js.es6 index 7ee70716..5d2d6c3b 100644 --- a/assets/javascripts/discourse/components/custom-field-input.js.es6 +++ b/assets/javascripts/discourse/components/custom-field-input.js.es6 @@ -3,29 +3,6 @@ import discourseComputed, { observes } from "discourse-common/utils/decorators"; import { alias, equal, or } from "@ember/object/computed"; import I18n from "I18n"; -import wizardSchema, { - requiringAdditionalSubscription, - subscriptionLevel, -} from "discourse/plugins/discourse-custom-wizard/discourse/lib/wizard-schema"; - -const generateContent = function (kategory, subscription) { - let unsubscribedCustomFields = requiringAdditionalSubscription( - subscription, - "custom_fields", - kategory - ); - return wizardSchema.custom_field[kategory].reduce((result, item) => { - let disabled = unsubscribedCustomFields.includes(item); - result.push({ - id: item, - name: I18n.t(`admin.wizard.custom_field.${kategory}.${item}`), - subscription: subscriptionLevel(item, "custom_fields", kategory), - disabled, - }); - return result; - }, []); -}; - export default Component.extend({ tagName: "tr", topicSerializers: ["topic_view", "topic_list_item"], @@ -58,16 +35,6 @@ export default Component.extend({ } }, - @discourseComputed("subscription") - customFieldTypes(subscription) { - return generateContent("type", subscription); - }, - - @discourseComputed("subscription") - customFieldKlasses(subscription) { - return generateContent("klass", subscription); - }, - @observes("field.klass") clearSerializersWhenClassChanges() { this.set("field.serializers", null); diff --git a/assets/javascripts/discourse/components/wizard-custom-action.js.es6 b/assets/javascripts/discourse/components/wizard-custom-action.js.es6 index 6cf22942..a0a9cc72 100644 --- a/assets/javascripts/discourse/components/wizard-custom-action.js.es6 +++ b/assets/javascripts/discourse/components/wizard-custom-action.js.es6 @@ -1,8 +1,6 @@ import { default as discourseComputed } from "discourse-common/utils/decorators"; -import wizardSchema, { - requiringAdditionalSubscription, - subscriptionLevel, -} from "discourse/plugins/discourse-custom-wizard/discourse/lib/wizard-schema"; +import wizardSchema from "discourse/plugins/discourse-custom-wizard/discourse/lib/wizard-schema"; +import { subscriptionSelectKitContent } from "discourse/plugins/discourse-custom-wizard/discourse/lib/wizard-subscription"; import { empty, equal, or } from "@ember/object/computed"; import { notificationLevels, selectKitContent } from "../lib/wizard"; import { computed } from "@ember/object"; @@ -97,22 +95,8 @@ export default Component.extend(UndoChanges, { return apis.find((a) => a.name === api).endpoints; }, - @discourseComputed("subscription") - actionTypes(subscription) { - let unsubscribedActions = requiringAdditionalSubscription( - subscription, - "actions", - "" - ); - return Object.keys(wizardSchema.action.types).reduce((result, type) => { - let disabled = unsubscribedActions.includes(type); - result.push({ - id: type, - name: I18n.t(`admin.wizard.action.${type}.label`), - subscription: subscriptionLevel(type, "actions", ""), - disabled, - }); - return result; - }, []); + @discourseComputed + actionTypes() { + return subscriptionSelectKitContent("action", "types"); }, }); diff --git a/assets/javascripts/discourse/components/wizard-subscription-badge.js.es6 b/assets/javascripts/discourse/components/wizard-subscription-badge.js.es6 new file mode 100644 index 00000000..7a63f4d3 --- /dev/null +++ b/assets/javascripts/discourse/components/wizard-subscription-badge.js.es6 @@ -0,0 +1,30 @@ +import Component from "@ember/component"; +import discourseComputed from "discourse-common/utils/decorators"; +import Subscription from "../mixins/subscription"; +import DiscourseURL from "discourse/lib/url"; +import I18n from "I18n"; + +export default Component.extend(Subscription, { + tagName: 'a', + classNameBindings: [":wizard-subscription-badge", "subscriptionType"], + attributeBindings: ["title"], + + @discourseComputed("subscriptionType") + i18nKey(type) { + return `admin.wizard.subscription_container.type.${type ? type : "none"}`; + }, + + @discourseComputed("i18nKey") + title(i18nKey) { + return I18n.t(`${i18nKey}.title`); + }, + + @discourseComputed("i18nKey") + label(i18nKey) { + return I18n.t(`${i18nKey}.label`); + }, + + click() { + DiscourseURL.routeTo(this.subscriptionLink); + } +}); diff --git a/assets/javascripts/discourse/components/wizard-subscription-container.js.es6 b/assets/javascripts/discourse/components/wizard-subscription-container.js.es6 new file mode 100644 index 00000000..3569276c --- /dev/null +++ b/assets/javascripts/discourse/components/wizard-subscription-container.js.es6 @@ -0,0 +1,26 @@ +import Component from "@ember/component"; +import discourseComputed from "discourse-common/utils/decorators"; +import Subscription from "../mixins/subscription"; + +export default Component.extend(Subscription, { + classNameBindings: [":wizard-subscription-container", "subscribed"], + + @discourseComputed("subscribed") + subscribedIcon(subscribed) { + return subscribed ? "check" : "dash"; + }, + + @discourseComputed("subscribed") + subscribedLabel(subscribed) { + return `admin.wizard.subscription_container.${ + subscribed ? "subscribed" : "not_subscribed" + }.label`; + }, + + @discourseComputed("subscribed") + subscribedTitle(subscribed) { + return `admin.wizard.subscription_container.${ + subscribed ? "subscribed" : "not_subscribed" + }.title`; + } +}); diff --git a/assets/javascripts/discourse/components/wizard-subscription-selector.js.es6 b/assets/javascripts/discourse/components/wizard-subscription-selector.js.es6 index ea1aa5d9..59714578 100644 --- a/assets/javascripts/discourse/components/wizard-subscription-selector.js.es6 +++ b/assets/javascripts/discourse/components/wizard-subscription-selector.js.es6 @@ -1,6 +1,21 @@ import SingleSelectComponent from "select-kit/components/single-select"; +import Subscription from "../mixins/subscription"; +import wizardSchema from "discourse/plugins/discourse-custom-wizard/discourse/lib/wizard-schema"; +import { + subscriptionTypeSufficient, + subscriptionTypes +} from "discourse/plugins/discourse-custom-wizard/discourse/lib/wizard-subscription"; +import discourseComputed from "discourse-common/utils/decorators"; -export default SingleSelectComponent.extend({ +const nameKey = function(feature, attribute, value) { + if (feature === 'action') { + return `admin.wizard.action.${value}.label`; + } else { + return `admin.wizard.${feature}.${attribute}.${value}`; + } +} + +export default SingleSelectComponent.extend(Subscription, { classNames: ["combo-box", "wizard-subscription-selector"], selectKitOptions: { @@ -13,6 +28,38 @@ export default SingleSelectComponent.extend({ caretDownIcon: "caret-down", }, + requiredSubscriptionType(feature, attribute, value) { + let attributes = this.subscriptionAttributes[feature]; + if (!attributes || !attributes[attribute]) return null; + + let requiredType = null; + Object.keys(attributes[attribute]).some(subscriptionType => { + let values = attributes[attribute][subscriptionType]; + if (values[0] === "*" || values.includes(value)) { + if (subscriptionTypes.includes(subscriptionType)) { + requiredType = subscriptionType; + } + return true; + } + return false; + }); + + return requiredType; + }, + + @discourseComputed('feature', 'attribute') + content(feature, attribute) { + return wizardSchema[feature][attribute].map((value) => { + let requiredSubscriptionType = this.requiredSubscriptionType(feature, attribute, value); + return { + id: value, + name: I18n.t(nameKey(feature, attribute, value)), + subscriptionType: requiredSubscriptionType, + disabled: !subscriptionTypeSufficient(this.subscriptionType, requiredSubscriptionType) + }; + }); + }, + modifyComponentForRow() { return "wizard-subscription-selector/wizard-subscription-selector-row"; }, diff --git a/assets/javascripts/discourse/connectors/category-custom-settings/custom-wizard-category-settings.hbs b/assets/javascripts/discourse/connectors/category-custom-settings/custom-wizard-category-settings.hbs index 4b5d673d..5ce96f2f 100644 --- a/assets/javascripts/discourse/connectors/category-custom-settings/custom-wizard-category-settings.hbs +++ b/assets/javascripts/discourse/connectors/category-custom-settings/custom-wizard-category-settings.hbs @@ -6,11 +6,11 @@
{{combo-box - value=wizardListVal - content=wizardList - onChange=(action "changeWizard") - options=(hash - none="admin.wizard.select" - )}} + value=wizardListVal + content=wizardList + onChange=(action "changeWizard") + options=(hash + none="admin.wizard.select" + )}}
diff --git a/assets/javascripts/discourse/controllers/admin-wizards-wizard-show.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards-wizard-show.js.es6 index e6b0ad04..2ecc42da 100644 --- a/assets/javascripts/discourse/controllers/admin-wizards-wizard-show.js.es6 +++ b/assets/javascripts/discourse/controllers/admin-wizards-wizard-show.js.es6 @@ -14,6 +14,7 @@ import I18n from "I18n"; export default Controller.extend({ hasName: notEmpty("wizard.name"), + @observes("currentStep") resetCurrentObjects() { const currentStep = this.currentStep; diff --git a/assets/javascripts/discourse/controllers/admin-wizards.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards.js.es6 index 33841460..b69b878a 100644 --- a/assets/javascripts/discourse/controllers/admin-wizards.js.es6 +++ b/assets/javascripts/discourse/controllers/admin-wizards.js.es6 @@ -1,26 +1,7 @@ -import Controller, { inject as controller } from "@ember/controller"; -import { isPresent } from "@ember/utils"; -import { A } from "@ember/array"; +import Controller from "@ember/controller"; +import { equal } from "@ember/object/computed"; export default Controller.extend({ - adminWizardsNotices: controller(), - - unsubscribe() { - this.messageBus.unsubscribe("/custom-wizard/notices"); - }, - - subscribe() { - this.unsubscribe(); - this.messageBus.subscribe("/custom-wizard/notices", (data) => { - if (isPresent(data.active_notice_count)) { - this.set("activeNoticeCount", data.active_notice_count); - this.adminWizardsNotices.setProperties({ - notices: A(), - page: 0, - canLoadMore: true, - }); - this.adminWizardsNotices.loadMoreNotices(); - } - }); - }, + businessSubscription: equal('subscriptionType', 'business'), + standardSubscription: equal('subscriptionType', 'standard') }); diff --git a/assets/javascripts/discourse/custom-wizard-admin-route-map.js.es6 b/assets/javascripts/discourse/custom-wizard-admin-route-map.js.es6 index c3c95e48..272e276e 100644 --- a/assets/javascripts/discourse/custom-wizard-admin-route-map.js.es6 +++ b/assets/javascripts/discourse/custom-wizard-admin-route-map.js.es6 @@ -58,16 +58,6 @@ export default { path: "/manager", resetNamespace: true, }); - - this.route("adminWizardsSubscription", { - path: "/subscription", - resetNamespace: true, - }); - - this.route("adminWizardsNotices", { - path: "/notices", - resetNamespace: true, - }); } ); }, diff --git a/assets/javascripts/discourse/lib/wizard-schema.js.es6 b/assets/javascripts/discourse/lib/wizard-schema.js.es6 index 26c21bd2..ca5c30ee 100644 --- a/assets/javascripts/discourse/lib/wizard-schema.js.es6 +++ b/assets/javascripts/discourse/lib/wizard-schema.js.es6 @@ -203,101 +203,17 @@ const custom_field = { type: ["string", "boolean", "integer", "json"], }; -const subscription_levels = { - standard: { - actions: ["send_message", "add_to_group", "watch_categories"], - custom_fields: { - klass: [], - type: ["json"], - }, - }, - - business: { - actions: ["create_category", "create_group", "send_to_api"], - custom_fields: { - klass: ["group", "category"], - type: [], - }, - }, -}; +field.type = Object.keys(field.types); +action.type = Object.keys(action.types); const wizardSchema = { wizard, step, field, custom_field, - action, - subscription_levels, + action }; -export function requiringAdditionalSubscription( - currentSubscription, - category, - subCategory -) { - switch (category) { - case "actions": - switch (currentSubscription) { - case "business": - return []; - case "standard": - return subscription_levels["business"][category]; - default: - return subscription_levels["standard"][category].concat( - subscription_levels["business"][category] - ); - } - case "custom_fields": - switch (currentSubscription) { - case "business": - return []; - case "standard": - return subscription_levels["business"][category][subCategory]; - default: - return subscription_levels["standard"][category][subCategory].concat( - subscription_levels["business"][category][subCategory] - ); - } - default: - return []; - } -} - -export function subscriptionLevel(type, category, subCategory) { - switch (category) { - case "actions": - if (subscription_levels["business"].actions.includes(type)) { - return "business"; - } else { - if (subscription_levels["standard"].actions.includes(type)) { - return "standard"; - } else { - return ""; - } - } - case "custom_fields": - if ( - subscription_levels["business"].custom_fields[subCategory].includes( - type - ) - ) { - return "business"; - } else { - if ( - subscription_levels["standard"].custom_fields[subCategory].includes( - type - ) - ) { - return "standard"; - } else { - return ""; - } - } - default: - return ""; - } -} - export function buildFieldTypes(types) { wizardSchema.field.types = types; } diff --git a/assets/javascripts/discourse/lib/wizard-subscription.js.es6 b/assets/javascripts/discourse/lib/wizard-subscription.js.es6 new file mode 100644 index 00000000..deff32ca --- /dev/null +++ b/assets/javascripts/discourse/lib/wizard-subscription.js.es6 @@ -0,0 +1,17 @@ +const subscriptionTypes = [ + 'standard', + 'business' +] + +function subscriptionTypeSufficient(subscriptionType, requiredType) { + if (requiredType && !subscriptionType) return false; + if (requiredType === 'none' || requiredType === null) return true; + if (requiredType === 'standard' && subscriptionTypes.includes(subscriptionType)) return true; + if (requiredType === 'business' && subscriptionType === 'business') return true; + return false; +} + +export { + subscriptionTypeSufficient, + subscriptionTypes +} diff --git a/assets/javascripts/discourse/mixins/subscription.js.es6 b/assets/javascripts/discourse/mixins/subscription.js.es6 new file mode 100644 index 00000000..00f77bb2 --- /dev/null +++ b/assets/javascripts/discourse/mixins/subscription.js.es6 @@ -0,0 +1,26 @@ +import Mixin from "@ember/object/mixin"; +import { getOwner } from "discourse-common/lib/get-owner"; +import { readOnly } from "@ember/object/computed"; +import discourseComputed from "discourse-common/utils/decorators"; + +export default Mixin.create({ + subscriptionLandingUrl: "https://custom-wizard.pavilion.tech", + subscriptionClientUrl: "/admin/plugins/subscription-client", + + @discourseComputed + adminWizards() { + return getOwner(this).lookup('controller:admin-wizards'); + }, + + subscribed: readOnly('adminWizards.subscribed'), + subscriptionType: readOnly('adminWizards.subscriptionType'), + businessSubscription: readOnly('adminWizards.businessSubscription'), + standardSubscription: readOnly('adminWizards.standardSubscription'), + subscriptionAttributes: readOnly('adminWizards.subscriptionAttributes'), + subscriptionClientInstalled: readOnly('adminWizards.subscriptionClientInstalled'), + + @discourseComputed("subscriptionClientInstalled") + subscriptionLink(subscriptionClientInstalled) { + return subscriptionClientInstalled ? this.subscriptionClientUrl : this.subscriptionLandingUrl; + } +}); diff --git a/assets/javascripts/discourse/routes/admin-wizards-custom-fields.js.es6 b/assets/javascripts/discourse/routes/admin-wizards-custom-fields.js.es6 index 4992f92d..4d9de2c4 100644 --- a/assets/javascripts/discourse/routes/admin-wizards-custom-fields.js.es6 +++ b/assets/javascripts/discourse/routes/admin-wizards-custom-fields.js.es6 @@ -9,13 +9,9 @@ export default DiscourseRoute.extend({ setupController(controller, model) { const customFields = A(model.custom_fields || []); - const subscribed = model.subscribed; - const subscription = model.subscription; controller.setProperties({ - customFields, - subscribed, - subscription, + customFields }); }, }); diff --git a/assets/javascripts/discourse/routes/admin-wizards-wizard-show.js.es6 b/assets/javascripts/discourse/routes/admin-wizards-wizard-show.js.es6 index d6263471..63ff564e 100644 --- a/assets/javascripts/discourse/routes/admin-wizards-wizard-show.js.es6 +++ b/assets/javascripts/discourse/routes/admin-wizards-wizard-show.js.es6 @@ -38,9 +38,7 @@ export default DiscourseRoute.extend({ wizard, currentStep: wizard.steps[0], currentAction: wizard.actions[0], - creating: model.create, - subscribed: parentModel.subscribed, - subscription: parentModel.subscription, + creating: model.create }; controller.setProperties(props); diff --git a/assets/javascripts/discourse/routes/admin-wizards.js.es6 b/assets/javascripts/discourse/routes/admin-wizards.js.es6 index 5c39c0d6..0286926d 100644 --- a/assets/javascripts/discourse/routes/admin-wizards.js.es6 +++ b/assets/javascripts/discourse/routes/admin-wizards.js.es6 @@ -7,21 +7,17 @@ export default DiscourseRoute.extend({ }, setupController(controller, model) { - controller.set("api_section", model.api_section); - - if (model.active_notice_count) { - controller.set("activeNoticeCount", model.active_notice_count); - } - if (model.featured_notices) { - controller.set("featuredNotices", model.featured_notices); - } - - controller.subscribe(); + controller.setProperties({ + subscribed: model.subscribed, + subscriptionType: model.subscription_type, + subscriptionAttributes: model.subscription_attributes, + subscriptionClientInstalled: model.subscription_client_installed + }); }, afterModel(model, transition) { if (transition.targetName === "adminWizards.index") { this.transitionTo("adminWizardsWizard"); } - }, + } }); diff --git a/assets/javascripts/discourse/templates/admin-wizards-custom-fields.hbs b/assets/javascripts/discourse/templates/admin-wizards-custom-fields.hbs index dceed458..10501498 100644 --- a/assets/javascripts/discourse/templates/admin-wizards-custom-fields.hbs +++ b/assets/javascripts/discourse/templates/admin-wizards-custom-fields.hbs @@ -32,9 +32,7 @@ {{custom-field-input field=field removeField=(action "removeField") - saveField=(action "saveField") - subscribed=subscribed - subscription=subscription}} + saveField=(action "saveField")}} {{/each}} diff --git a/assets/javascripts/discourse/templates/admin-wizards-wizard-show.hbs b/assets/javascripts/discourse/templates/admin-wizards-wizard-show.hbs index abf06ba9..5d1c2166 100644 --- a/assets/javascripts/discourse/templates/admin-wizards-wizard-show.hbs +++ b/assets/javascripts/discourse/templates/admin-wizards-wizard-show.hbs @@ -126,7 +126,7 @@ - {{#subscription-container subscribed=subscribed}} + {{#wizard-subscription-container}}
@@ -146,7 +146,7 @@ {{i18n "admin.wizard.restart_on_revisit_label"}}
- {{/subscription-container}} + {{/wizard-subscription-container}} {{wizard-links @@ -177,9 +177,7 @@ wizard=wizard apis=apis removeAction="removeAction" - wizardFields=wizardFields - subscribed=subscribed - subscription=subscription}} + wizardFields=wizardFields}} {{/each}}
diff --git a/assets/javascripts/discourse/templates/admin-wizards.hbs b/assets/javascripts/discourse/templates/admin-wizards.hbs index a50748cc..26ba3e06 100644 --- a/assets/javascripts/discourse/templates/admin-wizards.hbs +++ b/assets/javascripts/discourse/templates/admin-wizards.hbs @@ -2,13 +2,14 @@ {{nav-item route="adminWizardsWizard" label="admin.wizard.nav_label"}} {{nav-item route="adminWizardsCustomFields" label="admin.wizard.custom_field.nav_label"}} {{nav-item route="adminWizardsSubmissions" label="admin.wizard.submissions.nav_label"}} - {{#if api_section}} + {{#if businessSubscription}} {{nav-item route="adminWizardsApi" label="admin.wizard.api.nav_label"}} {{/if}} {{nav-item route="adminWizardsLogs" label="admin.wizard.log.nav_label"}} {{nav-item route="adminWizardsManager" label="admin.wizard.manager.nav_label"}}
+ {{wizard-subscription-badge}} {{d-icon "far-life-ring"}}{{i18n "admin.wizard.support_button.label"}} diff --git a/assets/javascripts/discourse/templates/components/custom-field-input.hbs b/assets/javascripts/discourse/templates/components/custom-field-input.hbs index b4ddcc81..335a9b9d 100644 --- a/assets/javascripts/discourse/templates/components/custom-field-input.hbs +++ b/assets/javascripts/discourse/templates/components/custom-field-input.hbs @@ -2,16 +2,22 @@ {{wizard-subscription-selector value=field.klass - content=customFieldKlasses - none="admin.wizard.custom_field.klass.select" - onChange=(action (mut field.klass))}} + feature="custom_field" + attribute="klass" + onChange=(action (mut field.klass)) + options=(hash + none="admin.wizard.custom_field.klass.select" + )}} {{wizard-subscription-selector value=field.type - content=customFieldTypes - none="admin.wizard.custom_field.type.select" - onChange=(action (mut field.type))}} + feature="custom_field" + attribute="type" + onChange=(action (mut field.type)) + options=(hash + none="admin.wizard.custom_field.type.select" + )}} {{input @@ -22,8 +28,10 @@ {{multi-select value=field.serializers content=serializerContent - none="admin.wizard.custom_field.serializers.select" - onChange=(action (mut field.serializers))}} + onChange=(action (mut field.serializers)) + options=(hash + none="admin.wizard.custom_field.serializers.select" + )}} {{#if loading}} diff --git a/assets/javascripts/discourse/templates/components/wizard-custom-action.hbs b/assets/javascripts/discourse/templates/components/wizard-custom-action.hbs index cb4cf28d..818c525d 100644 --- a/assets/javascripts/discourse/templates/components/wizard-custom-action.hbs +++ b/assets/javascripts/discourse/templates/components/wizard-custom-action.hbs @@ -14,7 +14,8 @@
{{wizard-subscription-selector value=action.type - content=actionTypes + feature="action" + attribute="type" onChange=(action "changeType") options=(hash none="admin.wizard.select_type" diff --git a/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs b/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs index b4f1ac3a..00c20153 100644 --- a/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs +++ b/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs @@ -222,7 +222,7 @@
{{/if}} -{{#subscription-container subscribed=subscribed}} +{{#wizard-subscription-container}}
@@ -237,7 +237,7 @@
- > +
@@ -248,10 +248,9 @@
{{#if isCategory}} -
+
- {{i18n "admin.wizard.pro.label"}}
@@ -269,4 +268,4 @@ {{#if validations}} {{wizard-realtime-validations field=field validations=validations}} {{/if}} -{{/subscription-container}} +{{/wizard-subscription-container}} diff --git a/assets/javascripts/discourse/templates/components/wizard-custom-step.hbs b/assets/javascripts/discourse/templates/components/wizard-custom-step.hbs index 0d1294f3..61812d04 100644 --- a/assets/javascripts/discourse/templates/components/wizard-custom-step.hbs +++ b/assets/javascripts/discourse/templates/components/wizard-custom-step.hbs @@ -34,7 +34,7 @@
-{{#subscription-container subscribed=subscribed}} +{{#wizard-subscription-container}}
@@ -56,11 +56,11 @@
-
+
- {{i18n "admin.wizard.pro.label"}}
+
{{wizard-mapper inputs=step.required_data @@ -99,7 +99,7 @@ )}}
-{{/subscription-container}} +{{/wizard-subscription-container}} {{wizard-links itemType="field" diff --git a/assets/javascripts/discourse/templates/components/wizard-subscription-badge.hbs b/assets/javascripts/discourse/templates/components/wizard-subscription-badge.hbs new file mode 100644 index 00000000..b2ce05bc5 --- /dev/null +++ b/assets/javascripts/discourse/templates/components/wizard-subscription-badge.hbs @@ -0,0 +1,6 @@ + + + +{{label}} diff --git a/assets/javascripts/discourse/templates/components/subscription-container.hbs b/assets/javascripts/discourse/templates/components/wizard-subscription-container.hbs similarity index 54% rename from assets/javascripts/discourse/templates/components/subscription-container.hbs rename to assets/javascripts/discourse/templates/components/wizard-subscription-container.hbs index 8b012671..78be031f 100644 --- a/assets/javascripts/discourse/templates/components/subscription-container.hbs +++ b/assets/javascripts/discourse/templates/components/wizard-subscription-container.hbs @@ -1,7 +1,7 @@
-

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

+

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

- + {{d-icon subscribedIcon}} {{i18n subscribedLabel}} diff --git a/assets/javascripts/discourse/templates/components/wizard-subscription-selector/wizard-subscription-selector-header.hbs b/assets/javascripts/discourse/templates/components/wizard-subscription-selector/wizard-subscription-selector-header.hbs index a7e3d2e6..708b0887 100644 --- a/assets/javascripts/discourse/templates/components/wizard-subscription-selector/wizard-subscription-selector-header.hbs +++ b/assets/javascripts/discourse/templates/components/wizard-subscription-selector/wizard-subscription-selector-header.hbs @@ -7,8 +7,10 @@ shouldDisplayClearableButton=shouldDisplayClearableButton }} - {{#if selectedContent.subscription}} - {{i18n "admin.wizard.subscription.label"}} + {{#if selectedContent.subscriptionType}} + + {{selectedContent.subscriptionType}} + {{/if}} {{d-icon caretIcon class="caret-icon"}} diff --git a/assets/javascripts/discourse/templates/components/wizard-subscription-selector/wizard-subscription-selector-row.hbs b/assets/javascripts/discourse/templates/components/wizard-subscription-selector/wizard-subscription-selector-row.hbs index ecd77cb1..1db906e6 100644 --- a/assets/javascripts/discourse/templates/components/wizard-subscription-selector/wizard-subscription-selector-row.hbs +++ b/assets/javascripts/discourse/templates/components/wizard-subscription-selector/wizard-subscription-selector-row.hbs @@ -9,9 +9,9 @@
{{html-safe label}} - {{#if item.subscription}} + {{#if item.subscriptionType}} - {{item.subscription}} + {{item.subscriptionType}} {{/if}}
diff --git a/assets/stylesheets/admin/admin.scss b/assets/stylesheets/admin/admin.scss index c026263a..b8458163 100644 --- a/assets/stylesheets/admin/admin.scss +++ b/assets/stylesheets/admin/admin.scss @@ -25,6 +25,14 @@ $error: #ef1700; } } +.admin-wizards .admin-actions { + display: flex; + + .btn-pavilion-support { + margin-left: 10px; + } +} + .wizard-message { background-color: var(--primary-low); width: 100%; @@ -156,6 +164,16 @@ $error: #ef1700; @extend .wizard-settings-group; } +.admin-wizard-container.settings { + .wizard-settings { + .wizard-subscription-container { + [class~="setting"] { + margin-bottom: 0; + } + } + } +} + .wizard-custom-field { background: transparent; background-color: var(--primary-very-low); @@ -802,84 +820,6 @@ $error: #ef1700; vertical-align: middle; } -.admin-wizards-subscription { - .admin-wizard-controls { - h3, - label { - margin: 0; - } - - label { - padding: 0.4em 0.5em; - margin-left: 0.75em; - background-color: var(--success); - color: var(--secondary); - } - - .buttons { - display: flex; - align-items: center; - - .loading-container { - margin-right: 1em; - } - } - } - - .custom-wizard-subscription { - .title-container { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 0.5em; - - h3 { - margin: 0; - } - - .buttons > span { - margin-right: 0.5em; - } - } - - .detail-container { - display: flex; - align-items: center; - padding: 1em; - background-color: var(--primary-very-low); - - .subscription-state { - padding: 0.25em 0.5em; - margin-right: 0.75em; - - &.active { - background-color: var(--success); - color: var(--secondary); - } - } - } - } -} - -.wizard-subscription-selector.select-kit.single-select { - .select-kit-row { - .texts { - display: flex; - align-items: center; - } - &.disabled { - background: var(--primary-low); - } - } - - .subscription-label { - margin-left: 0.75em; - padding-top: 0.25em; - color: var(--tertiary); - font-size: 0.75em; - } -} - .btn.btn-pavilion-support { background: var(--pavilion-primary); color: var(--pavilion-secondary); @@ -899,7 +839,7 @@ $error: #ef1700; } } -.subscription-container { +.wizard-subscription-container { width: 100%; padding: 1em; background-color: rgba($pavilion_primary, 0.1); @@ -907,158 +847,79 @@ $error: #ef1700; .subscription-header { display: flex; justify-content: space-between; - margin-bottom: 1em; + margin-bottom: .25em; h3 { margin: 0; } a { - color: var(--pavilion-primary); + color: var(--primary); } } &:not(.subscribed) .subscription-settings { filter: blur(1px); + pointer-events: none; } } -.admin-wizards-notices { - .wizard-table { - overflow: unset; - } -} - -.wizard-notice { - padding: 0.75em; - margin-bottom: 1em; - border: 1px solid var(--primary-low); - - &.dismissed { - display: none; - } - - &.expired .notice-badge:not(.notice-expired-at), - &.expired a, - &.expired p { - color: var(--primary-medium) !important; - } - - .notice-badge { - padding: 0 0.5em; - } - - .notice-header { - display: flex; - - .notice-title { - padding: 0; - } - - .notice-header-right { - margin-left: auto; - display: flex; - align-items: center; - - .notice-badge { - margin-left: 0.5em; - } - } - } - - .dismiss-notice-container, - .hide-notice-container { - width: 40px; - display: flex; - justify-content: center; - align-items: center; - } - - .dismiss-notice, - .hide-notice { - display: flex; - align-items: center; - - .d-icon { - margin-right: 0; - color: var(--primary); - } - } -} - -.notice-badge { +.wizard-subscription-badge { display: inline-flex; align-items: center; - min-height: 25px; + max-height: 34px; box-sizing: border-box; - color: var(--primary) !important; -} - -.admin-actions { - display: flex; - align-items: center; -} - -.wizard-notices-link { position: relative; - margin-right: 10px; + cursor: pointer; + padding: .5em .65em; + background-color: rgba($primary-medium, 0.05); + border: 1.5px solid rgba($primary-medium, 0.5); + color: $primary-medium; - div > a { - @include btn; - color: var(--secondary) !important; - background-color: var(--primary-medium); + &:hover { + color: $primary-medium; + } - &.active { - background-color: var(--tertiary) !important; - color: var(--secondary) !important; + svg { + width: 15px; + height: 15px; + margin-right: 0.45em; + margin-bottom: 0.15em; + } + + &.standard { + background-color: rgba($subscription_standard, 0.05); + border: 1.5px solid rgba($subscription_standard, 0.5); + color: $subscription_standard; + } + + &.business { + background-color: $subscription_business; + border: 1.5px solid $subscription_business; + color: $secondary; + } + + .d-icon { + margin-right: 0.75em; + } +} + +.wizard-subscription-selector.select-kit.single-select { + .select-kit-row { + .texts { + display: flex; + align-items: center; + } + &.disabled { + background: var(--primary-low); + cursor: unset; } } -} -.active-notice-count { - background-color: $danger; - color: $secondary; - border-radius: 50%; - width: 18px; - height: 18px; - line-height: 18px; - text-align: center; - position: absolute; - top: -8px; - right: -8px; - font-size: 0.7em; -} - -a.show-notice-message { - padding: 0.25em 0.5em; - color: var(--primary); -} - -.wizard-notice, -.wizard-notice-row:not(.expired):not(.dismissed) { - &.info { - background-color: rgba($info, 0.1); - border: 1px solid rgba($info, 0.5); - } - &.warning, - &.connection-error { - background-color: rgba($warning, 0.1); - border: 1px solid rgba($warning, 0.5); - } -} - -.notice-message { - position: relative; - - .cooked-notice-message { - background-color: var(--secondary); - padding: 1em; - z-index: 1; - box-shadow: shadow("dropdown"); - border-top: 1px solid var(--primary-low); - - p { - margin: 0; - } + .subscription-label { + margin-left: 0.75em; + padding-top: 0.25em; + color: var(--pavilion-primary); + font-size: 0.75em; } } diff --git a/assets/stylesheets/admin/wizard/variables.scss b/assets/stylesheets/admin/wizard/variables.scss index 5912f961..9bb93798 100644 --- a/assets/stylesheets/admin/wizard/variables.scss +++ b/assets/stylesheets/admin/wizard/variables.scss @@ -2,8 +2,13 @@ $pavilion_primary: #3c1c8c; $pavilion_secondary: #ffffff; $pavilion_warning: rgb(243, 163, 61); +$subscription_standard: $pavilion_primary; +$subscription_business: #333; + :root { --pavilion-primary: #{$pavilion_primary}; --pavilion-secondary: #{$pavilion_secondary}; --pavilion-warning: #{$pavilion_warning}; + --subscription-standard: #{$subscription_standard}; + --subscription-business: #{$subscription_business}; } diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 34c7003b..45efabb8 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -47,7 +47,7 @@ en: value: "Value" profile: "profile" type: "Type" - none: "Make a selection" + none: "Make a selection" submission_key: 'submission key' param_key: 'param' group: "Group" @@ -126,9 +126,9 @@ en: hide: "Hide" preview: "{{action}} Preview" popover: "{{action}} Fields" - + input: - conditional: + conditional: name: 'if' output: 'then' assignment: @@ -137,7 +137,7 @@ en: name: 'map' validation: name: 'ensure' - + selector: label: text: "text" @@ -175,7 +175,7 @@ en: dependent: "{{property}} is dependent on {{dependent}}" conflict: "{{type}} with {{property}} '{{value}}' already exists" after_time: "After time invalid" - + step: header: "Steps" title: "Title" @@ -189,7 +189,7 @@ en: force_final: label: "Conditional Final Step" description: "Display this step as the final step if conditions on later steps have not passed when the user reaches this step." - + field: header: "Fields" label: "Label" @@ -212,7 +212,7 @@ en: prefill: "Prefill" content: "Content" tag_groups: "Tag Groups" - date_time_format: + date_time_format: label: "Format" instructions: "Moment.js format" validations: @@ -229,7 +229,7 @@ en: weeks: "Weeks" months: "Months" years: "Years" - + type: text: "Text" textarea: Textarea @@ -248,7 +248,7 @@ en: date: Date time: Time date_time: Date & Time - + connector: and: "and" or: "or" @@ -262,7 +262,7 @@ en: regex: '=~' association: '→' is: 'is' - + action: header: "Actions" include: "Include Fields" @@ -270,8 +270,8 @@ en: post: "Post" topic_attr: "Topic Attribute" interpolate_fields: "Insert wizard fields using the field_id in w{}. Insert user fields using field key in u{}." - - run_after: + + run_after: label: "Run After" wizard_completion: "Wizard Completion" custom_fields: @@ -353,11 +353,11 @@ en: messageable_level: Messageable Level visibility_level: Visibility Level members_visibility_level: Members Visibility Level - + custom_field: nav_label: "Custom Fields" add: "Add" - external: + external: label: "from another plugin" title: "This custom field has been added by another plugin. You can use it in your wizards but you can't edit the field here." name: @@ -386,7 +386,7 @@ en: basic_category: "Category" basic_group: "Group" post: "Post" - + submissions: nav_label: "Submissions" title: "{{name}} Submissions" @@ -468,59 +468,22 @@ en: subscription_container: title: Subscriber Features - subscribed: + subscribed: label: Subscribed title: You're subscribed and can use these features not_subscribed: label: Not Subscribed title: Subscribe to use these features - - subscription: - nav_label: Subscription - label: In subscription - additional_label: "Add subscription" - title: Custom Wizard Subscription - authorize: Authorize - authorized: Authorized - unauthorize: cancel - not_subscribed: You're not currently subscribed - subscription: - title: - standard: Standard Subscription - business: Business Subscription - status: - active: Active - inactive: Inactive - update: Update - last_updated: Last updated - - notice: - plugin: Custom Wizard Plugin - message: Message - time: Time - status: Status - title: Title - dismiss: - label: Dismiss - title: Dismiss notice - dismiss_all: - label: Dismiss All - title: Dismiss all informational Custom Wizard notices - confirm: Are you sure you want to dismiss all informational Custom Wizard notices? - active: active - created_at: issued - updated_at: updated - expired_at: expired - dismissed_at: dismissed type: - label: Type - info: Information - warning: Warning - connection_error: Connection Error - - notices: - nav_label: Notices - title: Plugin Notices + none: + label: Not Subscribed + title: There is no Custom Wizard subscription active on this forum. + business: + label: Business + title: There is a Custom Wizard Business subscription active on this forum. + standard: + label: Standard + title: There is a Custom Wizard Standard subscription active on this forum. wizard_js: group: @@ -636,7 +599,7 @@ en: yourself_confirm: title: "Did you forget to add recipients?" body: "Right now this message is only being sent to yourself!" - + realtime_validations: similar_topics: insufficient_characters: "Type a minimum 5 characters to start looking for similar topics" diff --git a/config/settings.yml b/config/settings.yml index c4337d92..514462d5 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -1,5 +1,5 @@ plugins: - custom_wizard_enabled: + custom_wizard_enabled: default: true client: true wizard_redirect_exclude_paths: diff --git a/lib/custom_wizard/custom_field.rb b/lib/custom_wizard/custom_field.rb index ed0c9594..f26c11fa 100644 --- a/lib/custom_wizard/custom_field.rb +++ b/lib/custom_wizard/custom_field.rb @@ -84,7 +84,7 @@ class ::CustomWizard::CustomField next end - if attr == 'klass' && @subscription.requires_additional_subscription("custom_fields", "klass").include?(value) + if attr == 'klass' && @subscription.includes?(:custom_field, :klass, value) add_error(I18n.t("wizard.custom_field.error.subscription_type", type: value)) end @@ -99,7 +99,7 @@ class ::CustomWizard::CustomField add_error(I18n.t("#{i18n_key}.unsupported_type", type: value)) end - if attr == 'type' && @subscription.requires_additional_subscription("custom_fields", "type").include?(value) + if attr == 'type' && @subscription.includes?(:custom_field, :type, value) add_error(I18n.t("wizard.custom_field.error.subscription_type", type: value)) end diff --git a/lib/custom_wizard/mapper.rb b/lib/custom_wizard/mapper.rb index c513307b..aa444de1 100644 --- a/lib/custom_wizard/mapper.rb +++ b/lib/custom_wizard/mapper.rb @@ -47,7 +47,6 @@ class CustomWizard::Mapper @data = params[:data] || {} @user = params[:user] @opts = params[:opts] || {} - @subscription = CustomWizard::Subscription.new end def perform @@ -252,7 +251,7 @@ class CustomWizard::Mapper end end - if opts[:template] && @subscription.subscribed? + if opts[:template] && CustomWizard::Subscription.subscribed? template = Liquid::Template.parse(string) string = template.render(data) end diff --git a/lib/custom_wizard/subscription.rb b/lib/custom_wizard/subscription.rb index 39febc14..b59791b3 100644 --- a/lib/custom_wizard/subscription.rb +++ b/lib/custom_wizard/subscription.rb @@ -1,8 +1,170 @@ class CustomWizard::Subscription + STANDARD_PRODUCT_ID = 'prod_LNAGVAaIqDsHmB' + BUSINESS_PRODUCT_ID = 'prod_LNABQ50maBQ1pY' + + def self.attributes + { + wizard: { + required: { + none: [], + standard: ['*'], + business: ['*'] + }, + permitted: { + none: [], + standard: ['*'], + business: ['*'] + } + }, + step: { + condition: { + none: [], + standard: ['*'], + business: ['*'] + }, + index: { + none: [], + standard: ['*'], + business: ['*'] + }, + required_data: { + none: [], + standard: ['*'], + business: ['*'] + }, + permitted_params: { + none: [], + standard: ['*'], + business: ['*'] + } + }, + field: { + condition: { + none: [], + standard: ['*'], + business: ['*'] + }, + index: { + none: [], + standard: ['*'], + business: ['*'] + }, + type: { + none: ['label', 'description', 'image', 'required', 'placeholder', 'file'], + standard: ['*'], + business: ['*'] + }, + prefill: { + standard: ['*'], + business: ['*'] + }, + content: { + none: [], + standard: ['*'], + business: ['*'] + }, + realtime_validations: { + none: [], + standard: ['*'], + business: ['*'] + } + }, + action: { + type: { + none: [], + standard: ['send_message', 'watch_categories', 'add_to_group'], + business: ['*'] + } + }, + custom_field: { + klass: { + none: ['topic', 'post'], + standard: ['topic', 'post'], + business: ['*'] + }, + type: { + none: ['string', 'boolean', 'integer'], + standard: ['string', 'boolean', 'integer'], + business: ['*'] + } + } + } + end + + def initialize + @subscription = find_subscription + end + + def includes?(feature, attribute, value) + attributes = self.class.attributes[feature] + + ## Attribute is not part of a subscription + return true unless attributes.present? && attributes.key?(attribute) + + values = attributes[attribute][type] + + ## Subscription type does not support the attribute. + return false if values.blank? + + ## Subscription type supports all values of the attribute. + return true if values === "*" + + ## Subscription type supports some values of the attributes. + values.include?(value) + end + + def type + return :none unless subscribed? + return :standard if standard? + return :business if business? + end + + def subscribed? + standard? || business? + end + + def standard? + @subscription.product_id === STANDARD_PRODUCT_ID + end + + def business? + @subscription.product_id === BUSINESS_PRODUCT_ID + end + + def client_installed? + defined?(SubscriptionClient) == 'constant' && SubscriptionClient.class == Module + end + + def find_subscription + subscription = nil + + if client_installed? + subscription = SubscriptionClientSubscription.active + .where(product_id: [STANDARD_PRODUCT_ID, BUSINESS_PRODUCT_ID]) + .order("product_id = '#{BUSINESS_PRODUCT_ID}' DESC") + .first + end + + unless subscription + subscription = OpenStruct.new(product_id: nil) + end + + subscription + end + def self.subscribed? + new.subscribed? + end + + def self.business? + new.business? + end + + def self.standard? + new.standard? end def self.type - + new.type end end diff --git a/lib/custom_wizard/validators/template.rb b/lib/custom_wizard/validators/template.rb index 9ae37170..0ffc2608 100644 --- a/lib/custom_wizard/validators/template.rb +++ b/lib/custom_wizard/validators/template.rb @@ -56,29 +56,10 @@ class CustomWizard::TemplateValidator def self.subscription { - wizard: { - save_submissions: 'false', - restart_on_revisit: 'true', - }, - step: { - condition: 'present', - index: 'conditional', - required_data: 'present', - permitted_params: 'present' - }, - field: { - condition: 'present', - index: 'conditional' - }, - action: { - type: %w[ - send_message - add_to_group - create_category - create_group - send_to_api - ] - } + wizard: ['save_submissions', 'restart_on_revisit'], + step: ['condition', 'index', 'required_data', 'permitted_params'], + field: ['condition', 'index'], + action: ['type'] } end @@ -93,16 +74,8 @@ class CustomWizard::TemplateValidator end def validate_subscription(object, type) - self.class.subscription[type].each do |property, subscription_type| - val = object[property.to_s] - is_subscription = (val != nil) && ( - subscription_type === 'present' && val.present? || - (['true', 'false'].include?(subscription_type) && cast_bool(val) == cast_bool(subscription_type)) || - (subscription_type === 'conditional' && val.is_a?(Hash)) || - (subscription_type.is_a?(Array) && subscription_type.include?(val)) - ) - - if is_subscription && !@subscription.subscribed? + self.class.subscription[type].each do |property| + if !@subscription.includes?(type, property, object[property]) errors.add :base, I18n.t("wizard.validation.subscription", type: type.to_s, property: property) end end @@ -148,10 +121,6 @@ class CustomWizard::TemplateValidator end end - def cast_bool(val) - ActiveRecord::Type::Boolean.new.cast(val) - end - def validate_liquid_template(object, type) %w[ description diff --git a/plugin.rb b/plugin.rb index 03862103..ebd5c6d5 100644 --- a/plugin.rb +++ b/plugin.rb @@ -5,17 +5,13 @@ # authors: Angus McLeod, Faizaan Gagan, Robert Barrow, Keegan George # contact_emails: support@thepavilion.io # url: https://github.com/paviliondev/discourse-custom-wizard +# subscription_url: https://coop.pavilion.tech gem 'liquid', '5.0.1', require: true register_asset 'stylesheets/admin/admin.scss', :desktop enabled_site_setting :custom_wizard_enabled -config = Rails.application.config -plugin_asset_path = "#{Rails.root}/plugins/discourse-custom-wizard/assets" -config.assets.paths << "#{plugin_asset_path}/javascripts" -config.assets.paths << "#{plugin_asset_path}/stylesheets/wizard" - if Rails.env.production? config.assets.precompile += %w{ wizard-custom-guest.js @@ -59,6 +55,24 @@ class ::Sprockets::DirectiveProcessor end end +## Override necessary due to 'assets/javascripts/wizard', particularly its tests. +def each_globbed_asset + if @path + root_path = "#{File.dirname(@path)}/assets/javascripts/discourse" + + Dir.glob(["#{root_path}/**/*"]).sort.each do |f| + f_str = f.to_s + if File.directory?(f) + yield [f, true] + elsif f_str.end_with?(".js.es6") || f_str.end_with?(".hbs") || f_str.end_with?(".hbr") + yield [f, false] + elsif transpile_js && f_str.end_with?(".js") + yield [f, false] + end + end + end +end + after_initialize do %w[ ../lib/custom_wizard/engine.rb @@ -91,6 +105,7 @@ after_initialize do ../lib/custom_wizard/step_updater.rb ../lib/custom_wizard/step.rb ../lib/custom_wizard/submission.rb + ../lib/custom_wizard/subscription.rb ../lib/custom_wizard/template.rb ../lib/custom_wizard/wizard.rb ../lib/custom_wizard/api/api.rb diff --git a/spec/components/custom_wizard/notice_spec.rb b/spec/components/custom_wizard/notice_spec.rb deleted file mode 100644 index 0b34664d..00000000 --- a/spec/components/custom_wizard/notice_spec.rb +++ /dev/null @@ -1,159 +0,0 @@ -# frozen_string_literal: true - -require_relative '../../plugin_helper' - -describe CustomWizard::Notice do - fab!(:user) { Fabricate(:user) } - let(:subscription_message) { - { - title: "Title of message about subscription", - message: "Message about subscription", - type: "info", - created_at: Time.now - 3.day, - expired_at: nil - } - } - let(:plugin_status) { - { - name: 'discourse-custom-wizard', - status: 'incompatible', - status_changed_at: Time.now - 3.day - } - } - - context "subscription message" do - before do - freeze_time - stub_request(:get, described_class.subscription_message_url).to_return(status: 200, body: { messages: [subscription_message] }.to_json) - described_class.update(skip_plugin: true) - end - - it "converts subscription messages into notices" do - notice = described_class.list.first - expect(notice.type).to eq(described_class.types[:info]) - expect(notice.message).to eq(subscription_message[:message]) - expect(notice.created_at.to_datetime).to be_within(1.second).of (subscription_message[:created_at].to_datetime) - end - - it "expires notice if subscription message is expired" do - subscription_message[:expired_at] = Time.now - stub_request(:get, described_class.subscription_message_url).to_return(status: 200, body: { messages: [subscription_message] }.to_json) - described_class.update(skip_plugin: true) - - notice = described_class.list(include_all: true).first - expect(notice.expired?).to eq(true) - end - - it "dismisses informational subscription notices" do - notice = described_class.list(include_all: true).first - expect(notice.dismissed?).to eq(false) - - notice.dismiss! - expect(notice.dismissed?).to eq(true) - end - - it "dismisses all informational subscription notices" do - 4.times do |index| - subscription_message[:title] += " #{index}" - stub_request(:get, described_class.subscription_message_url).to_return(status: 200, body: { messages: [subscription_message] }.to_json) - described_class.update(skip_plugin: true) - end - expect(described_class.list.count).to eq(5) - described_class.dismiss_all - expect(described_class.list.count).to eq(0) - end - end - - context "plugin status" do - before do - freeze_time - stub_request(:get, described_class.plugin_status_url).to_return(status: 200, body: plugin_status.to_json) - described_class.update(skip_subscription: true) - end - - it "converts warning into notice" do - notice = described_class.list.first - expect(notice.type).to eq(described_class.types[:warning]) - expect(notice.message).to eq(I18n.t("wizard.notice.compatibility_issue.message", domain: described_class.plugin_status_domain)) - expect(notice.created_at.to_datetime).to be_within(1.second).of (plugin_status[:status_changed_at].to_datetime) - end - - it "expires warning notices if status is recommended or compatible" do - plugin_status[:status] = 'compatible' - plugin_status[:status_changed_at] = Time.now - stub_request(:get, described_class.plugin_status_url).to_return(status: 200, body: plugin_status.to_json) - described_class.update(skip_subscription: true) - - notice = described_class.list(type: described_class.types[:warning], include_all: true).first - expect(notice.expired?).to eq(true) - end - - it "hides plugin status warnings" do - notice = described_class.list.first - expect(notice.hidden?).to eq(false) - - notice.hide! - expect(notice.hidden?).to eq(true) - end - end - - it "lists notices not expired more than a day ago" do - subscription_message[:expired_at] = Time.now - 8.hours - stub_request(:get, described_class.subscription_message_url).to_return(status: 200, body: { messages: [subscription_message] }.to_json) - stub_request(:get, described_class.plugin_status_url).to_return(status: 200, body: plugin_status.to_json) - - described_class.update - expect(described_class.list(include_all: true).length).to eq(2) - end - - context "connection errors" do - before do - freeze_time - end - - it "creates an error if connection to notice server fails" do - stub_request(:get, described_class.plugin_status_url).to_return(status: 400, body: plugin_status.to_json) - described_class.update(skip_subscription: true) - - error = CustomWizard::Notice::ConnectionError.new(:plugin_status) - expect(error.current_error.present?).to eq(true) - end - - it "only creates one connection error per type at a time" do - stub_request(:get, described_class.subscription_message_url).to_return(status: 400, body: { messages: [subscription_message] }.to_json) - stub_request(:get, described_class.plugin_status_url).to_return(status: 400, body: plugin_status.to_json) - - 5.times { described_class.update } - - plugin_status_errors = CustomWizard::Notice::ConnectionError.new(:plugin_status) - subscription_message_errors = CustomWizard::Notice::ConnectionError.new(:subscription_message) - - expect(plugin_status_errors.current_error[:count]).to eq(5) - expect(subscription_message_errors.current_error[:count]).to eq(5) - end - - it "creates a connection error notice if connection errors reach limit" do - stub_request(:get, described_class.plugin_status_url).to_return(status: 400, body: plugin_status.to_json) - - error = CustomWizard::Notice::ConnectionError.new(:plugin_status) - error.limit.times { described_class.update(skip_subscription: true) } - notice = described_class.list(type: described_class.types[:connection_error]).first - - expect(error.current_error[:count]).to eq(error.limit) - expect(notice.type).to eq(described_class.types[:connection_error]) - end - - it "expires a connection error notice if connection succeeds" do - stub_request(:get, described_class.plugin_status_url).to_return(status: 400, body: plugin_status.to_json) - error = CustomWizard::Notice::ConnectionError.new(:plugin_status) - error.limit.times { described_class.update(skip_subscription: true) } - - stub_request(:get, described_class.plugin_status_url).to_return(status: 200, body: plugin_status.to_json) - described_class.update(skip_subscription: true) - notice = described_class.list(type: described_class.types[:connection_error], include_all: true).first - - expect(notice.type).to eq(described_class.types[:connection_error]) - expect(notice.expired_at.present?).to eq(true) - end - end -end diff --git a/spec/components/custom_wizard/subscription_spec.rb b/spec/components/custom_wizard/subscription_spec.rb deleted file mode 100644 index 47fb2d3f..00000000 --- a/spec/components/custom_wizard/subscription_spec.rb +++ /dev/null @@ -1,125 +0,0 @@ -# frozen_string_literal: true - -require_relative '../../plugin_helper' - -describe CustomWizard::Subscription do - fab!(:user) { Fabricate(:user) } - - it "initializes subscription authentication and subscription" do - subscription = described_class.new - expect(subscription.authentication.class).to eq(CustomWizard::Subscription::Authentication) - expect(subscription.subscription.class).to eq(CustomWizard::Subscription::Subscription) - end - - it "returns authorized and subscribed states" do - subscription = described_class.new - expect(subscription.authorized?).to eq(false) - expect(subscription.subscribed?).to eq(false) - end - - context "subscription" do - before do - @subscription = described_class.new - end - - it "updates valid subscriptions" do - stub_subscription_request(200, valid_subscription) - expect(@subscription.update).to eq(true) - expect(@subscription.subscribed?).to eq(true) - end - - it "handles invalid subscriptions" do - stub_subscription_request(200, invalid_subscription) - expect(@subscription.update).to eq(false) - expect(@subscription.subscribed?).to eq(false) - end - - it "handles subscription http errors" do - stub_subscription_request(404, {}) - expect(@subscription.update).to eq(false) - expect(@subscription.subscribed?).to eq(false) - end - - it "destroys subscriptions" do - stub_subscription_request(200, valid_subscription) - expect(@subscription.update).to eq(true) - expect(@subscription.destroy_subscription).to eq(true) - expect(@subscription.subscribed?).to eq(false) - end - - it "has class aliases" do - authenticate_subscription - stub_subscription_request(200, valid_subscription) - expect(described_class.update).to eq(true) - expect(described_class.subscribed?).to eq(true) - end - end - - context "authentication" do - before do - @subscription = described_class.new - user.update!(admin: true) - end - - it "generates a valid authentication request url" do - request_id = SecureRandom.hex(32) - uri = URI(@subscription.authentication_url(user.id, request_id)) - expect(uri.host).to eq(@subscription.server) - - parsed_query = Rack::Utils.parse_query uri.query - expect(parsed_query['public_key'].present?).to eq(true) - expect(parsed_query['nonce'].present?).to eq(true) - expect(parsed_query['client_id'].present?).to eq(true) - expect(parsed_query['auth_redirect'].present?).to eq(true) - expect(parsed_query['application_name']).to eq(SiteSetting.title) - expect(parsed_query['scopes']).to eq(@subscription.scope) - end - - def generate_payload(request_id, user_id) - uri = URI(@subscription.authentication_url(user_id, request_id)) - keys = @subscription.authentication.get_keys(request_id) - raw_payload = { - key: "12345", - nonce: keys.nonce, - push: false, - api: UserApiKeysController::AUTH_API_VERSION - }.to_json - public_key = OpenSSL::PKey::RSA.new(keys.pem) - Base64.encode64(public_key.public_encrypt(raw_payload)) - end - - it "handles authentication response if request and response is valid" do - request_id = SecureRandom.hex(32) - payload = generate_payload(request_id, user.id) - - expect(@subscription.authentication_response(request_id, payload)).to eq(true) - expect(@subscription.authorized?).to eq(true) - end - - it "discards authentication response if user who made request as not an admin" do - user.update!(admin: false) - - request_id = SecureRandom.hex(32) - payload = generate_payload(request_id, user.id) - - expect(@subscription.authentication_response(request_id, payload)).to eq(false) - expect(@subscription.authorized?).to eq(false) - end - - it "discards authentication response if request_id is invalid" do - payload = generate_payload(SecureRandom.hex(32), user.id) - - expect(@subscription.authentication_response(SecureRandom.hex(32), payload)).to eq(false) - expect(@subscription.authorized?).to eq(false) - end - - it "destroys authentication" do - request_id = SecureRandom.hex(32) - payload = generate_payload(request_id, user.id) - @subscription.authentication_response(request_id, payload) - - expect(@subscription.destroy_authentication).to eq(true) - expect(@subscription.authorized?).to eq(false) - end - end -end diff --git a/spec/jobs/update_notices_spec.rb b/spec/jobs/update_notices_spec.rb deleted file mode 100644 index df0697b8..00000000 --- a/spec/jobs/update_notices_spec.rb +++ /dev/null @@ -1,29 +0,0 @@ -# frozen_string_literal: true - -require_relative '../plugin_helper' - -describe Jobs::CustomWizardUpdateNotices do - let(:subscription_message) { - { - message: "Message about subscription", - type: "info", - created_at: Time.now - 3.day, - expired_at: nil - } - } - let(:plugin_status) { - { - name: 'discourse-custom-wizard', - status: 'incompatible', - status_changed_at: Time.now - 3.day - } - } - - it "updates the notices" do - stub_request(:get, CustomWizard::Notice.subscription_message_url).to_return(status: 200, body: { messages: [subscription_message] }.to_json) - stub_request(:get, CustomWizard::Notice.plugin_status_url).to_return(status: 200, body: plugin_status.to_json) - - described_class.new.execute - expect(CustomWizard::Notice.list.length).to eq(2) - end -end diff --git a/spec/jobs/update_subscription_spec.rb b/spec/jobs/update_subscription_spec.rb deleted file mode 100644 index c5adaf38..00000000 --- a/spec/jobs/update_subscription_spec.rb +++ /dev/null @@ -1,11 +0,0 @@ -# frozen_string_literal: true - -require_relative '../plugin_helper' - -describe Jobs::CustomWizardUpdateSubscription do - it "updates the subscription" do - stub_subscription_request(200, valid_subscription) - described_class.new.execute - expect(CustomWizard::Subscription.subscribed?).to eq(true) - end -end diff --git a/spec/plugin_helper.rb b/spec/plugin_helper.rb index 6e340ccf..f7818296 100644 --- a/spec/plugin_helper.rb +++ b/spec/plugin_helper.rb @@ -7,38 +7,3 @@ def get_wizard_fixture(path) ).read ).with_indifferent_access end - -def authenticate_subscription - CustomWizard::Subscription::Authentication.any_instance.stubs(:active?).returns(true) -end - -def enable_subscription(type) - # CustomWizard::Subscription.new - CustomWizard::Subscription.any_instance.stubs(:subscribed?).returns(true) - CustomWizard::Subscription.any_instance.stubs(:type).returns(type) -end - -def disable_subscription - CustomWizard::Subscription.any_instance.stubs(:subscribed?).returns(false) -end - -def valid_subscription - { - product_id: "prod_CBTNpi3fqWWkq0", - price_id: "price_id", - price_nickname: "business" - } -end - -def invalid_subscription - { - product_id: "prod_CBTNpi3fqWWkq0", - price_id: "price_id" - } -end - -def stub_subscription_request(status, subscription) - authenticate_subscription - sub = CustomWizard::Subscription.new - stub_request(:get, "https://#{sub.server}/subscription-server/user-subscriptions/#{sub.subscription_type}/#{sub.client_name}").to_return(status: status, body: { subscriptions: [subscription] }.to_json) -end diff --git a/spec/requests/custom_wizard/admin/notice_controller_spec.rb b/spec/requests/custom_wizard/admin/notice_controller_spec.rb deleted file mode 100644 index 33d03432..00000000 --- a/spec/requests/custom_wizard/admin/notice_controller_spec.rb +++ /dev/null @@ -1,72 +0,0 @@ -# frozen_string_literal: true -require_relative '../../../plugin_helper' - -describe CustomWizard::AdminNoticeController do - fab!(:admin_user) { Fabricate(:user, admin: true) } - let(:subscription_message_notice) { - { - title: "Title of message about subscription", - message: "Message about subscription", - type: 0, - created_at: Time.now.iso8601(3), - expired_at: nil - } - } - let(:plugin_status_notice) { - { - title: "The Custom Wizard Plugin is incompatibile with the latest version of Discourse.", - message: "Please check the Custom Wizard Plugin status on [localhost:3000](http://localhost:3000) before updating Discourse.", - type: 1, - archetype: 1, - created_at: Time.now.iso8601(3), - expired_at: nil - } - } - - before do - sign_in(admin_user) - end - - it "lists notices" do - @notice = CustomWizard::Notice.new(subscription_message_notice) - @notice.save - - get "/admin/wizards/notice.json" - expect(response.status).to eq(200) - expect(response.parsed_body.length).to eq(1) - end - - it "dismisses notices" do - @notice = CustomWizard::Notice.new(subscription_message_notice) - @notice.save - - put "/admin/wizards/notice/#{@notice.id}/dismiss.json" - expect(response.status).to eq(200) - - updated = CustomWizard::Notice.find(@notice.id) - expect(updated.dismissed?).to eq(true) - end - - it "dismisses all notices" do - 5.times do |index| - subscription_message_notice[:title] += " #{index}" - @notice = CustomWizard::Notice.new(subscription_message_notice) - @notice.save - end - - put "/admin/wizards/notice/dismiss.json" - expect(response.status).to eq(200) - expect(CustomWizard::Notice.list.size).to eq(0) - end - - it "hides notices" do - @notice = CustomWizard::Notice.new(plugin_status_notice) - @notice.save - - put "/admin/wizards/notice/#{@notice.id}/hide.json" - expect(response.status).to eq(200) - - updated = CustomWizard::Notice.find(@notice.id) - expect(updated.hidden?).to eq(true) - end -end diff --git a/spec/requests/custom_wizard/admin/subscription_controller_spec.rb b/spec/requests/custom_wizard/admin/subscription_controller_spec.rb deleted file mode 100644 index 2f8aad20..00000000 --- a/spec/requests/custom_wizard/admin/subscription_controller_spec.rb +++ /dev/null @@ -1,71 +0,0 @@ -# frozen_string_literal: true -require_relative '../../../plugin_helper' - -describe CustomWizard::AdminSubscriptionController do - fab!(:admin_user) { Fabricate(:user, admin: true) } - - def generate_payload(request_id, user_id) - uri = URI(@subscription.authentication_url(user_id, request_id)) - keys = @subscription.authentication.get_keys(request_id) - raw_payload = { - key: "12345", - nonce: keys.nonce, - push: false, - api: UserApiKeysController::AUTH_API_VERSION - }.to_json - public_key = OpenSSL::PKey::RSA.new(keys.pem) - Base64.encode64(public_key.public_encrypt(raw_payload)) - end - - before do - @subscription = CustomWizard::Subscription.new - sign_in(admin_user) - end - - it "#index" do - get "/admin/wizards/subscription.json" - expect(response.parsed_body['server']).to eq(@subscription.server) - expect(response.parsed_body['authentication'].deep_symbolize_keys).to eq(CustomWizard::Subscription::AuthenticationSerializer.new(@subscription.authentication, root: false).as_json) - expect(response.parsed_body['subscription'].deep_symbolize_keys).to eq(CustomWizard::Subscription::SubscriptionSerializer.new(@subscription.subscription, root: false).as_json) - end - - it "#authorize" do - get "/admin/wizards/subscription/authorize" - expect(response.status).to eq(302) - expect(cookies[:user_api_request_id].present?).to eq(true) - end - - it "#destroy_authentication" do - request_id = SecureRandom.hex(32) - payload = generate_payload(request_id, admin_user.id) - @subscription.authentication_response(request_id, payload) - - delete "/admin/wizards/subscription/authorize.json" - - expect(response.status).to eq(200) - expect(CustomWizard::Subscription.authorized?).to eq(false) - end - - context "subscription" do - before do - stub_subscription_request(200, valid_subscription) - end - - it "handles authentication response and the updates subscription" do - request_id = cookies[:user_api_request_id] = SecureRandom.hex(32) - payload = generate_payload(request_id, admin_user.id) - get "/admin/wizards/subscription/authorize/callback", params: { payload: payload } - - expect(response).to redirect_to("/admin/wizards/subscription") - expect(CustomWizard::Subscription.subscribed?).to eq(true) - end - - it "updates the subscription" do - authenticate_subscription - post "/admin/wizards/subscription.json" - - expect(response.status).to eq(200) - expect(CustomWizard::Subscription.subscribed?).to eq(true) - end - end -end diff --git a/spec/serializers/custom_wizard/notice_serializer_spec.rb b/spec/serializers/custom_wizard/notice_serializer_spec.rb deleted file mode 100644 index 5184d890..00000000 --- a/spec/serializers/custom_wizard/notice_serializer_spec.rb +++ /dev/null @@ -1,22 +0,0 @@ -# frozen_string_literal: true - -require_relative '../../plugin_helper' - -describe CustomWizard::NoticeSerializer do - before do - @notice = CustomWizard::Notice.new( - message: "Message about subscription", - type: "info", - created_at: Time.now - 3.day, - expired_at: nil - ) - @notice.save - end - - it 'should return notice attributes' do - serialized_notice = described_class.new(@notice) - expect(serialized_notice.message).to eq(@notice.message) - expect(serialized_notice.type).to eq(CustomWizard::Notice.types.key(@notice.type)) - expect(serialized_notice.dismissable).to eq(true) - end -end diff --git a/spec/serializers/custom_wizard/subscription/authentication_serializer_spec.rb b/spec/serializers/custom_wizard/subscription/authentication_serializer_spec.rb deleted file mode 100644 index ac29f95a..00000000 --- a/spec/serializers/custom_wizard/subscription/authentication_serializer_spec.rb +++ /dev/null @@ -1,17 +0,0 @@ -# frozen_string_literal: true - -require_relative '../../../plugin_helper' - -describe CustomWizard::Subscription::AuthenticationSerializer do - fab!(:user) { Fabricate(:user) } - - it 'should return subscription authentication attributes' do - auth = CustomWizard::Subscription::Authentication.new(OpenStruct.new(key: '1234', auth_at: Time.now, auth_by: user.id)) - serialized = described_class.new(auth, root: false).as_json - - expect(serialized[:active]).to eq(true) - expect(serialized[:client_id]).to eq(auth.client_id) - expect(serialized[:auth_by]).to eq(auth.auth_by) - expect(serialized[:auth_at]).to eq(auth.auth_at) - end -end diff --git a/spec/serializers/custom_wizard/subscription/subscription_serializer_spec.rb b/spec/serializers/custom_wizard/subscription/subscription_serializer_spec.rb deleted file mode 100644 index 91dc59b1..00000000 --- a/spec/serializers/custom_wizard/subscription/subscription_serializer_spec.rb +++ /dev/null @@ -1,14 +0,0 @@ -# frozen_string_literal: true - -require_relative '../../../plugin_helper' - -describe CustomWizard::Subscription::SubscriptionSerializer do - it 'should return subscription attributes' do - sub = CustomWizard::Subscription::Subscription.new(OpenStruct.new(type: 'standard', updated_at: Time.now)) - serialized = described_class.new(sub, root: false).as_json - - expect(serialized[:active]).to eq(true) - expect(serialized[:type]).to eq('standard') - expect(serialized[:updated_at]).to eq(sub.updated_at) - end -end diff --git a/spec/serializers/custom_wizard/subscription_serializer_spec.rb b/spec/serializers/custom_wizard/subscription_serializer_spec.rb deleted file mode 100644 index c6ea0ef2..00000000 --- a/spec/serializers/custom_wizard/subscription_serializer_spec.rb +++ /dev/null @@ -1,14 +0,0 @@ -# frozen_string_literal: true - -require_relative '../../plugin_helper' - -describe CustomWizard::SubscriptionSerializer do - it 'should return subscription attributes' do - subscription = CustomWizard::Subscription.new - serialized = described_class.new(subscription, root: false) - - expect(serialized.server).to eq(subscription.server) - expect(serialized.authentication.class).to eq(CustomWizard::Subscription::Authentication) - expect(serialized.subscription.class).to eq(CustomWizard::Subscription::Subscription) - end -end