diff --git a/app/controllers/custom_wizard/admin/admin.rb b/app/controllers/custom_wizard/admin/admin.rb index 63a89833..867be56c 100644 --- a/app/controllers/custom_wizard/admin/admin.rb +++ b/app/controllers/custom_wizard/admin/admin.rb @@ -3,17 +3,12 @@ class CustomWizard::AdminController < ::Admin::AdminController before_action :ensure_admin def index + subcription = CustomWizard::Subscription.new render_json_dump( - #TODO replace with appropriate static? - api_section: ["business"].include?(CustomWizard::Subscription.type), - active_notice_count: CustomWizard::Notice.active_count, - featured_notices: ActiveModel::ArraySerializer.new( - CustomWizard::Notice.list( - type: CustomWizard::Notice.types[:info], - archetype: CustomWizard::Notice.archetypes[:subscription_message] - ), - each_serializer: CustomWizard::NoticeSerializer - ) + 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/notice.rb b/app/controllers/custom_wizard/admin/notice.rb deleted file mode 100644 index 81ae00da..00000000 --- a/app/controllers/custom_wizard/admin/notice.rb +++ /dev/null @@ -1,68 +0,0 @@ -# frozen_string_literal: true - -class CustomWizard::AdminNoticeController < CustomWizard::AdminController - before_action :find_notice, only: [:dismiss, :hide] - - def index - type = params[:type] - archetype = params[:archtype] - page = params[:page].to_i - include_all = ActiveRecord::Type::Boolean.new.cast(params[:include_all]) - visible = ActiveRecord::Type::Boolean.new.cast(params[:visible]) - - if type - if type.is_a?(Array) - type = type.map { |t| CustomWizard::Notice.types[t.to_sym] } - else - type = CustomWizard::Notice.types[type.to_sym] - end - end - - if archetype - if archetype.is_a?(Array) - archetype = archetype.map { |t| CustomWizard::Notice.archetypes[archetype.to_sym] } - else - archetype = CustomWizard::Notice.archetypes[archetype.to_sym] - end - end - - notices = CustomWizard::Notice.list( - include_all: include_all, - page: page, - type: type, - archetype: archetype, - visible: visible - ) - - render_serialized(notices, CustomWizard::NoticeSerializer, root: :notices) - end - - def dismiss - if @notice.dismissable? && @notice.dismiss! - render json: success_json.merge(dismissed_at: @notice.dismissed_at) - else - render json: failed_json - end - end - - def hide - if @notice.can_hide? && @notice.hide! - render json: success_json.merge(hidden_at: @notice.hidden_at) - else - render json: failed_json - end - end - - def dismiss_all - if CustomWizard::Notice.dismiss_all - render json: success_json - else - render json: failed_json - end - end - - def find_notice - @notice = CustomWizard::Notice.find(params[:notice_id]) - raise Discourse::InvalidParameters.new(:notice_id) unless @notice - end -end diff --git a/app/controllers/custom_wizard/admin/subscription.rb b/app/controllers/custom_wizard/admin/subscription.rb deleted file mode 100644 index 15ff6396..00000000 --- a/app/controllers/custom_wizard/admin/subscription.rb +++ /dev/null @@ -1,48 +0,0 @@ -# frozen_string_literal: true - -class CustomWizard::AdminSubscriptionController < CustomWizard::AdminController - skip_before_action :check_xhr, :preload_json, :verify_authenticity_token, only: [:authorize, :authorize_callback] - - def index - render_serialized(subscription, CustomWizard::SubscriptionSerializer, root: false) - end - - def authorize - request_id = SecureRandom.hex(32) - cookies[:user_api_request_id] = request_id - redirect_to subscription.authentication_url(current_user.id, request_id).to_s - end - - def authorize_callback - payload = params[:payload] - request_id = cookies[:user_api_request_id] - - subscription.authentication_response(request_id, payload) - subscription.update - - redirect_to '/admin/wizards/subscription' - end - - def destroy_authentication - if subscription.destroy_authentication - render json: success_json - else - render json: failed_json - end - end - - def update_subscription - if subscription.update - serialized_subscription = CustomWizard::Subscription::SubscriptionSerializer.new(subscription.subscription, root: false) - render json: success_json.merge(subscription: serialized_subscription) - else - render json: failed_json - end - end - - protected - - def subscription - @subscription ||= CustomWizard::Subscription.new - end -end diff --git a/app/controllers/custom_wizard/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/app/controllers/custom_wizard/wizard.rb b/app/controllers/custom_wizard/wizard.rb index a7988b0d..bb53670b 100644 --- a/app/controllers/custom_wizard/wizard.rb +++ b/app/controllers/custom_wizard/wizard.rb @@ -11,7 +11,6 @@ class CustomWizard::WizardController < ::ActionController::Base before_action :preload_wizard_json before_action :ensure_plugin_enabled - before_action :update_subscription, only: [:index] before_action :ensure_logged_in, only: [:skip] helper_method :wizard_page_title @@ -45,7 +44,7 @@ class CustomWizard::WizardController < ::ActionController::Base return render json: { error: I18n.t('wizard.no_skip') } end - result = success_json + result = { success: 'OK' } if current_user && wizard.can_access? if redirect_to = wizard.current_submission&.redirect_to @@ -123,8 +122,4 @@ class CustomWizard::WizardController < ::ActionController::Base redirect_to path("/") end end - - def update_subscription - CustomWizard::Subscription.update - end end diff --git a/app/jobs/scheduled/custom_wizard/update_notices.rb b/app/jobs/scheduled/custom_wizard/update_notices.rb deleted file mode 100644 index 25ebdf3b..00000000 --- a/app/jobs/scheduled/custom_wizard/update_notices.rb +++ /dev/null @@ -1,9 +0,0 @@ -# frozen_string_literal: true - -class Jobs::CustomWizardUpdateNotices < ::Jobs::Scheduled - every 5.minutes - - def execute(args = {}) - CustomWizard::Notice.update - end -end diff --git a/app/jobs/scheduled/custom_wizard/update_subscription.rb b/app/jobs/scheduled/custom_wizard/update_subscription.rb deleted file mode 100644 index 72e34435..00000000 --- a/app/jobs/scheduled/custom_wizard/update_subscription.rb +++ /dev/null @@ -1,9 +0,0 @@ -# frozen_string_literal: true - -class Jobs::CustomWizardUpdateSubscription < ::Jobs::Scheduled - every 1.hour - - def execute(args = {}) - CustomWizard::Subscription.update - end -end diff --git a/app/serializers/custom_wizard/notice_serializer.rb b/app/serializers/custom_wizard/notice_serializer.rb deleted file mode 100644 index 4354731d..00000000 --- a/app/serializers/custom_wizard/notice_serializer.rb +++ /dev/null @@ -1,33 +0,0 @@ -# frozen_string_literal: true - -class CustomWizard::NoticeSerializer < ApplicationSerializer - attributes :id, - :title, - :message, - :type, - :archetype, - :created_at, - :expired_at, - :updated_at, - :dismissed_at, - :retrieved_at, - :hidden_at, - :dismissable, - :can_hide - - def dismissable - object.dismissable? - end - - def can_hide - object.can_hide? - end - - def type - CustomWizard::Notice.types.key(object.type) - end - - def messsage - PrettyText.cook(object.message) - end -end diff --git a/app/serializers/custom_wizard/subscription/authentication_serializer.rb b/app/serializers/custom_wizard/subscription/authentication_serializer.rb deleted file mode 100644 index 3d54d0f2..00000000 --- a/app/serializers/custom_wizard/subscription/authentication_serializer.rb +++ /dev/null @@ -1,11 +0,0 @@ -# frozen_string_literal: true -class CustomWizard::Subscription::AuthenticationSerializer < ApplicationSerializer - attributes :active, - :client_id, - :auth_by, - :auth_at - - def active - object.active? - end -end diff --git a/app/serializers/custom_wizard/subscription/subscription_serializer.rb b/app/serializers/custom_wizard/subscription/subscription_serializer.rb deleted file mode 100644 index 95a2b323..00000000 --- a/app/serializers/custom_wizard/subscription/subscription_serializer.rb +++ /dev/null @@ -1,10 +0,0 @@ -# frozen_string_literal: true -class CustomWizard::Subscription::SubscriptionSerializer < ApplicationSerializer - attributes :type, - :active, - :updated_at - - def active - object.active? - end -end diff --git a/app/serializers/custom_wizard/subscription_serializer.rb b/app/serializers/custom_wizard/subscription_serializer.rb deleted file mode 100644 index 067cfbcd..00000000 --- a/app/serializers/custom_wizard/subscription_serializer.rb +++ /dev/null @@ -1,6 +0,0 @@ -# frozen_string_literal: true -class CustomWizard::SubscriptionSerializer < ApplicationSerializer - attributes :server - has_one :authentication, serializer: CustomWizard::Subscription::AuthenticationSerializer, embed: :objects - has_one :subscription, serializer: CustomWizard::Subscription::SubscriptionSerializer, embed: :objects -end diff --git a/assets/javascripts/discourse/components/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..75667c2f 100644 --- a/assets/javascripts/discourse/components/wizard-custom-action.js.es6 +++ b/assets/javascripts/discourse/components/wizard-custom-action.js.es6 @@ -1,8 +1,5 @@ import { default as discourseComputed } from "discourse-common/utils/decorators"; -import wizardSchema, { - requiringAdditionalSubscription, - subscriptionLevel, -} 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 +94,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-notice-row.js.es6 b/assets/javascripts/discourse/components/wizard-notice-row.js.es6 deleted file mode 100644 index ada4384d..00000000 --- a/assets/javascripts/discourse/components/wizard-notice-row.js.es6 +++ /dev/null @@ -1,19 +0,0 @@ -import Component from "@ember/component"; -import NoticeMessage from "../mixins/notice-message"; - -export default Component.extend(NoticeMessage, { - tagName: "tr", - attributeBindings: ["notice.id:data-notice-id"], - classNameBindings: [ - ":wizard-notice-row", - "notice.typeClass", - "notice.expired:expired", - "notice.dismissed:dismissed", - ], - - actions: { - dismiss() { - this.notice.dismiss(); - }, - }, -}); diff --git a/assets/javascripts/discourse/components/wizard-notice.js.es6 b/assets/javascripts/discourse/components/wizard-notice.js.es6 deleted file mode 100644 index ca6b7658..00000000 --- a/assets/javascripts/discourse/components/wizard-notice.js.es6 +++ /dev/null @@ -1,29 +0,0 @@ -import Component from "@ember/component"; -import NoticeMessage from "../mixins/notice-message"; - -export default Component.extend(NoticeMessage, { - attributeBindings: ["notice.id:data-notice-id"], - classNameBindings: [ - ":wizard-notice", - "notice.typeClass", - "notice.dismissed:dismissed", - "notice.expired:expired", - "notice.hidden:hidden", - ], - - actions: { - dismiss() { - this.set("dismissing", true); - this.notice.dismiss().then(() => { - this.set("dismissing", false); - }); - }, - - hide() { - this.set("hiding", true); - this.notice.hide().then(() => { - this.set("hiding", false); - }); - }, - }, -}); diff --git a/assets/javascripts/discourse/components/wizard-subscription-badge.js.es6 b/assets/javascripts/discourse/components/wizard-subscription-badge.js.es6 new file mode 100644 index 00000000..87c985a4 --- /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/subscription-container.js.es6 b/assets/javascripts/discourse/components/wizard-subscription-container.js.es6 similarity index 78% rename from assets/javascripts/discourse/components/subscription-container.js.es6 rename to assets/javascripts/discourse/components/wizard-subscription-container.js.es6 index a12b8949..ccaf2f48 100644 --- a/assets/javascripts/discourse/components/subscription-container.js.es6 +++ b/assets/javascripts/discourse/components/wizard-subscription-container.js.es6 @@ -1,8 +1,9 @@ import Component from "@ember/component"; import discourseComputed from "discourse-common/utils/decorators"; +import Subscription from "../mixins/subscription"; -export default Component.extend({ - classNameBindings: [":subscription-container", "subscribed"], +export default Component.extend(Subscription, { + classNameBindings: [":wizard-subscription-container", "subscribed"], @discourseComputed("subscribed") subscribedIcon(subscribed) { diff --git a/assets/javascripts/discourse/components/wizard-subscription-selector.js.es6 b/assets/javascripts/discourse/components/wizard-subscription-selector.js.es6 index ea1aa5d9..2202f746 100644 --- a/assets/javascripts/discourse/components/wizard-subscription-selector.js.es6 +++ b/assets/javascripts/discourse/components/wizard-subscription-selector.js.es6 @@ -1,6 +1,22 @@ import SingleSelectComponent from "select-kit/components/single-select"; +import Subscription from "../mixins/subscription"; +import wizardSchema from "discourse/plugins/discourse-custom-wizard/discourse/lib/wizard-schema"; +import { + subscriptionTypeSufficient, + subscriptionTypes, +} from "discourse/plugins/discourse-custom-wizard/discourse/lib/wizard-subscription"; +import discourseComputed from "discourse-common/utils/decorators"; +import I18n from "I18n"; -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 +29,47 @@ 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/components/wizard-subscription.js.es6 b/assets/javascripts/discourse/components/wizard-subscription.js.es6 deleted file mode 100644 index 29e47bc2..00000000 --- a/assets/javascripts/discourse/components/wizard-subscription.js.es6 +++ /dev/null @@ -1,55 +0,0 @@ -import Component from "@ember/component"; -import CustomWizardSubscription from "../models/custom-wizard-subscription"; -import { notEmpty } from "@ember/object/computed"; -import discourseComputed from "discourse-common/utils/decorators"; -import I18n from "I18n"; - -export default Component.extend({ - classNameBindings: [ - ":custom-wizard-subscription", - "subscription.active:active:inactive", - ], - subscribed: notEmpty("subscription"), - - @discourseComputed("subscription.type") - title(type) { - return type - ? I18n.t(`admin.wizard.subscription.subscription.title.${type}`) - : I18n.t("admin.wizard.subscription.not_subscribed"); - }, - - @discourseComputed("subscription.active") - stateClass(active) { - return active ? "active" : "inactive"; - }, - - @discourseComputed("stateClass") - stateLabel(stateClass) { - return I18n.t( - `admin.wizard.subscription.subscription.status.${stateClass}` - ); - }, - - actions: { - update() { - this.set("updating", true); - CustomWizardSubscription.update() - .then((result) => { - if (result.success) { - this.setProperties({ - updateIcon: "check", - subscription: result.subscription, - }); - } else { - this.set("updateIcon", "times"); - } - }) - .finally(() => { - this.set("updating", false); - setTimeout(() => { - this.set("updateIcon", null); - }, 7000); - }); - }, - }, -}); diff --git a/assets/javascripts/discourse/connectors/admin-dashboard-top/custom-wizard-critical-notice.hbs b/assets/javascripts/discourse/connectors/admin-dashboard-top/custom-wizard-critical-notice.hbs deleted file mode 100644 index 9d96bed9..00000000 --- a/assets/javascripts/discourse/connectors/admin-dashboard-top/custom-wizard-critical-notice.hbs +++ /dev/null @@ -1,5 +0,0 @@ -{{#if notices}} - {{#each notices as |notice|}} - {{wizard-notice notice=notice showPlugin=true}} - {{/each}} -{{/if}} diff --git a/assets/javascripts/discourse/connectors/admin-dashboard-top/custom-wizard-critical-notice.js.es6 b/assets/javascripts/discourse/connectors/admin-dashboard-top/custom-wizard-critical-notice.js.es6 deleted file mode 100644 index 803e58a4..00000000 --- a/assets/javascripts/discourse/connectors/admin-dashboard-top/custom-wizard-critical-notice.js.es6 +++ /dev/null @@ -1,19 +0,0 @@ -import { getOwner } from "discourse-common/lib/get-owner"; - -export default { - shouldRender(attrs, ctx) { - return ctx.siteSettings.wizard_critical_notices_on_dashboard; - }, - - setupComponent(attrs, component) { - const controller = getOwner(this).lookup("controller:admin-dashboard"); - - component.set("notices", controller.get("customWizardCriticalNotices")); - controller.addObserver("customWizardCriticalNotices.[]", () => { - if (this._state === "destroying") { - return; - } - component.set("notices", controller.get("customWizardCriticalNotices")); - }); - }, -}; diff --git a/assets/javascripts/discourse/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-notices.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards-notices.js.es6 deleted file mode 100644 index 1721e699..00000000 --- a/assets/javascripts/discourse/controllers/admin-wizards-notices.js.es6 +++ /dev/null @@ -1,68 +0,0 @@ -import Controller from "@ember/controller"; -import CustomWizardNotice from "../models/custom-wizard-notice"; -import discourseComputed from "discourse-common/utils/decorators"; -import { notEmpty } from "@ember/object/computed"; -import { A } from "@ember/array"; -import I18n from "I18n"; - -export default Controller.extend({ - messageUrl: "https://thepavilion.io/t/3652", - messageKey: "info", - messageIcon: "info-circle", - messageClass: "info", - hasNotices: notEmpty("notices"), - page: 0, - loadingMore: false, - canLoadMore: true, - - @discourseComputed("notices.[]", "notices.@each.dismissed") - allDismisssed(notices) { - return notices.every((n) => !n.canDismiss || n.dismissed); - }, - - loadMoreNotices() { - if (!this.canLoadMore) { - return; - } - const page = this.get("page"); - this.set("loadingMore", true); - - CustomWizardNotice.list({ page, include_all: true }) - .then((result) => { - if (result.notices.length === 0) { - this.set("canLoadMore", false); - return; - } - - this.get("notices").pushObjects( - A(result.notices.map((notice) => CustomWizardNotice.create(notice))) - ); - }) - .finally(() => this.set("loadingMore", false)); - }, - - actions: { - loadMore() { - if (this.canLoadMore) { - this.set("page", this.page + 1); - this.loadMoreNotices(); - } - }, - - dismissAll() { - bootbox.confirm( - I18n.t("admin.wizard.notice.dismiss_all.confirm"), - I18n.t("no_value"), - I18n.t("yes_value"), - (result) => { - if (result) { - this.set("loadingMore", true); - CustomWizardNotice.dismissAll().finally(() => - this.set("loadingMore", false) - ); - } - } - ); - }, - }, -}); diff --git a/assets/javascripts/discourse/controllers/admin-wizards-subscription.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards-subscription.js.es6 deleted file mode 100644 index 76f16119..00000000 --- a/assets/javascripts/discourse/controllers/admin-wizards-subscription.js.es6 +++ /dev/null @@ -1,62 +0,0 @@ -import Controller from "@ember/controller"; -import discourseComputed from "discourse-common/utils/decorators"; -import CustomWizardSubscription from "../models/custom-wizard-subscription"; -import { alias } from "@ember/object/computed"; - -export default Controller.extend({ - messageUrl: "https://thepavilion.io/t/3652", - messageType: "info", - messageKey: null, - showSubscription: alias("model.authentication.active"), - - setup() { - const authentication = this.get("model.authentication"); - const subscription = this.get("model.subscription"); - const subscribed = subscription && subscription.active; - const authenticated = authentication && authentication.active; - - if (!subscribed) { - this.set("messageKey", authenticated ? "not_subscribed" : "authorize"); - } else { - this.set( - "messageKey", - !authenticated - ? "subscription_expiring" - : subscribed - ? "subscription_active" - : "subscription_inactive" - ); - } - }, - - @discourseComputed("model.server") - messageOpts(server) { - return { server }; - }, - - actions: { - unauthorize() { - this.set("unauthorizing", true); - - CustomWizardSubscription.unauthorize() - .then((result) => { - if (result.success) { - this.setProperties({ - messageKey: "unauthorized", - messageType: "warn", - "model.authentication": null, - "model.subscription": null, - }); - } else { - this.setProperties({ - messageKey: "unauthorize_failed", - messageType: "error", - }); - } - }) - .finally(() => { - this.set("unauthorizing", false); - }); - }, - }, -}); diff --git a/assets/javascripts/discourse/controllers/admin-wizards.js.es6 b/assets/javascripts/discourse/controllers/admin-wizards.js.es6 index 33841460..f99c06cc 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/helpers/notice-badge.js.es6 b/assets/javascripts/discourse/helpers/notice-badge.js.es6 deleted file mode 100644 index ea32b462..00000000 --- a/assets/javascripts/discourse/helpers/notice-badge.js.es6 +++ /dev/null @@ -1,43 +0,0 @@ -import { autoUpdatingRelativeAge } from "discourse/lib/formatter"; -import { iconHTML } from "discourse-common/lib/icon-library"; -import I18n from "I18n"; -import { registerUnbound } from "discourse-common/lib/helpers"; -import { htmlSafe } from "@ember/template"; - -registerUnbound("notice-badge", function (attrs) { - let tag = attrs.url ? "a" : "div"; - let attrStr = ""; - if (attrs.title) { - attrStr += `title='${I18n.t(attrs.title)}'`; - } - if (attrs.url) { - attrStr += `href='${attrs.url}'`; - } - let html = `<${tag} class="${ - attrs.class ? `${attrs.class} ` : "" - }notice-badge" ${attrStr}>`; - if (attrs.icon) { - html += iconHTML(attrs.icon); - } - if (attrs.label) { - if (attrs.icon) { - html += " "; - } - html += `${I18n.t(attrs.label)}`; - } - if (attrs.date) { - if (attrs.icon || attrs.label) { - html += " "; - } - let dateAttrs = {}; - if (attrs.leaveAgo) { - dateAttrs = { - format: "medium", - leaveAgo: true, - }; - } - html += autoUpdatingRelativeAge(new Date(attrs.date), dateAttrs); - } - html += ``; - return htmlSafe(html); -}); diff --git a/assets/javascripts/discourse/initializers/custom-wizard-edits.js.es6 b/assets/javascripts/discourse/initializers/custom-wizard-edits.js.es6 index 6a027dee..6d1410ad 100644 --- a/assets/javascripts/discourse/initializers/custom-wizard-edits.js.es6 +++ b/assets/javascripts/discourse/initializers/custom-wizard-edits.js.es6 @@ -1,8 +1,5 @@ import DiscourseURL from "discourse/lib/url"; import { withPluginApi } from "discourse/lib/plugin-api"; -import CustomWizardNotice from "../models/custom-wizard-notice"; -import { isPresent } from "@ember/utils"; -import { A } from "@ember/array"; import getUrl from "discourse-common/lib/get-url"; export default { @@ -23,50 +20,6 @@ export default { }; withPluginApi("0.8.36", (api) => { - api.modifyClass("route:admin-dashboard", { - pluginId: "custom-wizard", - - setupController(controller) { - this._super(...arguments); - - controller.loadCriticalNotices(); - controller.subscribe(); - }, - }); - - api.modifyClass("controller:admin-dashboard", { - pluginId: "custom-wizard", - criticalNotices: A(), - - unsubscribe() { - this.messageBus.unsubscribe("/custom-wizard/notices"); - }, - - subscribe() { - this.unsubscribe(); - this.messageBus.subscribe("/custom-wizard/notices", (data) => { - if (isPresent(data.active_notice_count)) { - this.loadCriticalNotices(); - } - }); - }, - - loadCriticalNotices() { - CustomWizardNotice.list({ - type: ["connection_error", "warning"], - archetype: "plugin_status", - visible: true, - }).then((result) => { - if (result.notices && result.notices.length) { - const criticalNotices = A( - result.notices.map((n) => CustomWizardNotice.create(n)) - ); - this.set("customWizardCriticalNotices", criticalNotices); - } - }); - }, - }); - api.modifyClass("component:d-navigation", { pluginId: "custom-wizard", actions: { diff --git a/assets/javascripts/discourse/lib/wizard-schema.js.es6 b/assets/javascripts/discourse/lib/wizard-schema.js.es6 index 26c21bd2..97477fe8 100644 --- a/assets/javascripts/discourse/lib/wizard-schema.js.es6 +++ b/assets/javascripts/discourse/lib/wizard-schema.js.es6 @@ -203,23 +203,8 @@ 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, @@ -227,77 +212,8 @@ const wizardSchema = { field, custom_field, action, - subscription_levels, }; -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..adecf063 --- /dev/null +++ b/assets/javascripts/discourse/lib/wizard-subscription.js.es6 @@ -0,0 +1,22 @@ +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/notice-message.js.es6 b/assets/javascripts/discourse/mixins/notice-message.js.es6 deleted file mode 100644 index 76e311bb..00000000 --- a/assets/javascripts/discourse/mixins/notice-message.js.es6 +++ /dev/null @@ -1,68 +0,0 @@ -import Mixin from "@ember/object/mixin"; -import { bind, scheduleOnce } from "@ember/runloop"; -import { cookAsync } from "discourse/lib/text"; -import { createPopper } from "@popperjs/core"; - -export default Mixin.create({ - showCookedMessage: false, - - didReceiveAttrs() { - const message = this.notice.message; - cookAsync(message).then((cooked) => { - this.set("cookedMessage", cooked); - }); - }, - - createMessageModal() { - let container = this.element.querySelector(".notice-message"); - let modal = this.element.querySelector(".cooked-notice-message"); - - this._popper = createPopper(container, modal, { - strategy: "absolute", - placement: "bottom-start", - modifiers: [ - { - name: "preventOverflow", - }, - { - name: "offset", - options: { - offset: [0, 5], - }, - }, - ], - }); - }, - - didInsertElement() { - $(document).on("click", bind(this, this.documentClick)); - }, - - willDestroyElement() { - $(document).off("click", bind(this, this.documentClick)); - }, - - documentClick(event) { - if (this._state === "destroying") { - return; - } - - if ( - !event.target.closest( - `[data-notice-id="${this.notice.id}"] .notice-message` - ) - ) { - this.set("showCookedMessage", false); - } - }, - - actions: { - toggleCookedMessage() { - this.toggleProperty("showCookedMessage"); - - if (this.showCookedMessage) { - scheduleOnce("afterRender", this, this.createMessageModal); - } - }, - }, -}); diff --git a/assets/javascripts/discourse/mixins/subscription.js.es6 b/assets/javascripts/discourse/mixins/subscription.js.es6 new file mode 100644 index 00000000..574f8585 --- /dev/null +++ b/assets/javascripts/discourse/mixins/subscription.js.es6 @@ -0,0 +1,30 @@ +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/models/custom-wizard-notice.js.es6 b/assets/javascripts/discourse/models/custom-wizard-notice.js.es6 deleted file mode 100644 index 035e2ad5..00000000 --- a/assets/javascripts/discourse/models/custom-wizard-notice.js.es6 +++ /dev/null @@ -1,74 +0,0 @@ -import EmberObject from "@ember/object"; -import discourseComputed from "discourse-common/utils/decorators"; -import { ajax } from "discourse/lib/ajax"; -import { popupAjaxError } from "discourse/lib/ajax-error"; -import { and, not, notEmpty } from "@ember/object/computed"; -import { dasherize } from "@ember/string"; -import I18n from "I18n"; - -const CustomWizardNotice = EmberObject.extend({ - expired: notEmpty("expired_at"), - dismissed: notEmpty("dismissed_at"), - hidden: notEmpty("hidden_at"), - notHidden: not("hidden"), - notDismissed: not("dismissed"), - canDismiss: and("dismissable", "notDismissed"), - canHide: and("can_hide", "notHidden"), - - @discourseComputed("type") - typeClass(type) { - return dasherize(type); - }, - - @discourseComputed("type") - typeLabel(type) { - return I18n.t(`admin.wizard.notice.type.${type}`); - }, - - dismiss() { - if (!this.get("canDismiss")) { - return; - } - - return ajax(`/admin/wizards/notice/${this.get("id")}/dismiss`, { - type: "PUT", - }) - .then((result) => { - if (result.success) { - this.set("dismissed_at", result.dismissed_at); - } - }) - .catch(popupAjaxError); - }, - - hide() { - if (!this.get("canHide")) { - return; - } - - return ajax(`/admin/wizards/notice/${this.get("id")}/hide`, { type: "PUT" }) - .then((result) => { - if (result.success) { - this.set("hidden_at", result.hidden_at); - } - }) - .catch(popupAjaxError); - }, -}); - -CustomWizardNotice.reopenClass({ - list(data = {}) { - return ajax("/admin/wizards/notice", { - type: "GET", - data, - }).catch(popupAjaxError); - }, - - dismissAll() { - return ajax("/admin/wizards/notice/dismiss", { - type: "PUT", - }).catch(popupAjaxError); - }, -}); - -export default CustomWizardNotice; diff --git a/assets/javascripts/discourse/models/custom-wizard-subscription.js.es6 b/assets/javascripts/discourse/models/custom-wizard-subscription.js.es6 deleted file mode 100644 index 469460d5..00000000 --- a/assets/javascripts/discourse/models/custom-wizard-subscription.js.es6 +++ /dev/null @@ -1,33 +0,0 @@ -import { ajax } from "discourse/lib/ajax"; -import { popupAjaxError } from "discourse/lib/ajax-error"; -import EmberObject from "@ember/object"; - -const CustomWizardSubscription = EmberObject.extend(); - -const basePath = "/admin/wizards/subscription"; - -CustomWizardSubscription.reopenClass({ - status() { - return ajax(basePath, { - type: "GET", - }).catch(popupAjaxError); - }, - - authorize() { - window.location.href = `${basePath}/authorize`; - }, - - unauthorize() { - return ajax(`${basePath}/authorize`, { - type: "DELETE", - }).catch(popupAjaxError); - }, - - update() { - return ajax(basePath, { - type: "POST", - }).catch(popupAjaxError); - }, -}); - -export default CustomWizardSubscription; diff --git a/assets/javascripts/discourse/routes/admin-wizards-custom-fields.js.es6 b/assets/javascripts/discourse/routes/admin-wizards-custom-fields.js.es6 index 4992f92d..a04d36f9 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, }); }, }); diff --git a/assets/javascripts/discourse/routes/admin-wizards-notices.js.es6 b/assets/javascripts/discourse/routes/admin-wizards-notices.js.es6 deleted file mode 100644 index 1d8b7cc8..00000000 --- a/assets/javascripts/discourse/routes/admin-wizards-notices.js.es6 +++ /dev/null @@ -1,17 +0,0 @@ -import CustomWizardNotice from "../models/custom-wizard-notice"; -import DiscourseRoute from "discourse/routes/discourse"; -import { A } from "@ember/array"; - -export default DiscourseRoute.extend({ - model() { - return CustomWizardNotice.list({ include_all: true }); - }, - - setupController(controller, model) { - controller.setProperties({ - notices: A( - model.notices.map((notice) => CustomWizardNotice.create(notice)) - ), - }); - }, -}); diff --git a/assets/javascripts/discourse/routes/admin-wizards-subscription.js.es6 b/assets/javascripts/discourse/routes/admin-wizards-subscription.js.es6 deleted file mode 100644 index 4824425a..00000000 --- a/assets/javascripts/discourse/routes/admin-wizards-subscription.js.es6 +++ /dev/null @@ -1,19 +0,0 @@ -import CustomWizardSubscription from "../models/custom-wizard-subscription"; -import DiscourseRoute from "discourse/routes/discourse"; - -export default DiscourseRoute.extend({ - model() { - return CustomWizardSubscription.status(); - }, - - setupController(controller, model) { - controller.set("model", model); - controller.setup(); - }, - - actions: { - authorize() { - CustomWizardSubscription.authorize(); - }, - }, -}); diff --git a/assets/javascripts/discourse/routes/admin-wizards-wizard-show.js.es6 b/assets/javascripts/discourse/routes/admin-wizards-wizard-show.js.es6 index d6263471..cb2d54c3 100644 --- a/assets/javascripts/discourse/routes/admin-wizards-wizard-show.js.es6 +++ b/assets/javascripts/discourse/routes/admin-wizards-wizard-show.js.es6 @@ -39,8 +39,6 @@ export default DiscourseRoute.extend({ currentStep: wizard.steps[0], currentAction: wizard.actions[0], creating: model.create, - subscribed: parentModel.subscribed, - subscription: parentModel.subscription, }; 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..1fa786d3 100644 --- a/assets/javascripts/discourse/routes/admin-wizards.js.es6 +++ b/assets/javascripts/discourse/routes/admin-wizards.js.es6 @@ -7,16 +7,12 @@ 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) { 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-notices.hbs b/assets/javascripts/discourse/templates/admin-wizards-notices.hbs deleted file mode 100644 index 039afe49..00000000 --- a/assets/javascripts/discourse/templates/admin-wizards-notices.hbs +++ /dev/null @@ -1,49 +0,0 @@ -
-

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

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

{{i18n "search.no_results"}}

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

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

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

{{title}}

- -
- - {{#if updating}} - {{loading-spinner size="small"}} - {{else if updateIcon}} - {{d-icon updateIcon}} - {{/if}} - - {{d-button - icon="sync" - action=(action "update") - disabled=updating - title="admin.wizard.subscription.subscription.update" - label="admin.wizard.subscription.subscription.update"}} -
-
- -{{#if subscribed}} -
-
{{stateLabel}}
- - {{#if subscription.updated_at}} -
- {{i18n "admin.wizard.subscription.subscription.last_updated"}} {{format-date subscription.updated_at leaveAgo="true"}} -
- {{/if}} -
-{{/if}} diff --git a/assets/javascripts/wizard/tests/bootstrap.js.es6 b/assets/javascripts/wizard/tests/bootstrap.js.es6 index d2c503ad..dec0c4b4 100644 --- a/assets/javascripts/wizard/tests/bootstrap.js.es6 +++ b/assets/javascripts/wizard/tests/bootstrap.js.es6 @@ -1,17 +1,19 @@ // discourse-skip-module -document.addEventListener("DOMContentLoaded", function () { - document.body.insertAdjacentHTML( - "afterbegin", - ` -
- - ` - ); -}); +if (window.location.pathname.indexOf("/w/") > -1 && Ember.testing) { + document.addEventListener("DOMContentLoaded", function () { + document.body.insertAdjacentHTML( + "afterbegin", + ` +
+ + ` + ); + }); -Object.keys(requirejs.entries).forEach(function (entry) { - if (/\-test/.test(entry)) { - requirejs(entry); - } -}); + Object.keys(requirejs.entries).forEach(function (entry) { + if (/\-test/.test(entry)) { + requirejs(entry); + } + }); +} diff --git a/assets/stylesheets/admin/admin.scss b/assets/stylesheets/admin/admin.scss index c026263a..f1911f2c 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: 0.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: 0.5em 0.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/locales/server.en.yml b/config/locales/server.en.yml index d0ca29a6..08cf5336 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -53,20 +53,7 @@ en: after_signup_after_time: "You can't use 'after time' and 'after signup' on the same wizard." after_time: "After time setting is invalid." liquid_syntax_error: "Liquid syntax error in %{attribute}: %{message}" - - notice: - connection_error: "Failed to connect to http://%{domain}" - compatibility_issue: - title: The Custom Wizard Plugin is incompatibile with the latest version of Discourse. - message: Please check the Custom Wizard Plugin status on [%{domain}](http://%{domain}) before updating Discourse. - plugin_status: - connection_error: - title: Unable to connect to the Custom Wizard Plugin status server - message: Please check the Custom Wizard Plugin status on [%{domain}](http://%{domain}) before updating Discourse. If this issue persists contact support@thepavilion.io for further assistance. - subscription_message: - connection_error: - title: Unable to connect to the Custom Wizard Plugin subscription server - message: If this issue persists contact support@thepavilion.io for further assistance. + subscription: "%{type} %{property} is subscription only" site_settings: custom_wizard_enabled: "Enable custom wizards." diff --git a/config/routes.rb b/config/routes.rb index 514605de..2d9f52c6 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -45,17 +45,5 @@ Discourse::Application.routes.append do get 'admin/wizards/manager/export' => 'admin_manager#export' post 'admin/wizards/manager/import' => 'admin_manager#import' delete 'admin/wizards/manager/destroy' => 'admin_manager#destroy' - - get 'admin/wizards/subscription' => 'admin_subscription#index' - post 'admin/wizards/subscription' => 'admin_subscription#update_subscription' - get 'admin/wizards/subscription/authorize' => 'admin_subscription#authorize' - get 'admin/wizards/subscription/authorize/callback' => 'admin_subscription#authorize_callback' - delete 'admin/wizards/subscription/authorize' => 'admin_subscription#destroy_authentication' - - get 'admin/wizards/notice' => 'admin_notice#index' - put 'admin/wizards/notice/:notice_id/dismiss' => 'admin_notice#dismiss' - put 'admin/wizards/notice/:notice_id/hide' => 'admin_notice#hide' - put 'admin/wizards/notice/dismiss' => 'admin_notice#dismiss_all' - get 'admin/wizards/notices' => 'admin_notice#index' end end diff --git a/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..29423ec6 100644 --- a/lib/custom_wizard/custom_field.rb +++ b/lib/custom_wizard/custom_field.rb @@ -84,7 +84,7 @@ class ::CustomWizard::CustomField next end - if attr == 'klass' && @subscription.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/notice.rb b/lib/custom_wizard/notice.rb deleted file mode 100644 index 9d5c1edf..00000000 --- a/lib/custom_wizard/notice.rb +++ /dev/null @@ -1,358 +0,0 @@ -# frozen_string_literal: true - -class CustomWizard::Notice - include ActiveModel::Serialization - - PLUGIN_STATUS_DOMAINS = { - "main" => "plugins.discourse.pavilion.tech", - "master" => "plugins.discourse.pavilion.tech", - "tests-passed" => "plugins.discourse.pavilion.tech", - "stable" => "stable.plugins.discourse.pavilion.tech" - } - SUBSCRIPTION_MESSAGE_DOMAIN = "test.thepavilion.io" - LOCALHOST_DOMAIN = "localhost:3000" - PLUGIN_STATUSES_TO_WARN = %w(incompatible tests_failing) - CHECK_PLUGIN_STATUS_ON_BRANCH = %w(tests-passed main stable) - PAGE_LIMIT = 30 - - attr_reader :id, - :title, - :message, - :type, - :archetype, - :created_at - - attr_accessor :retrieved_at, - :updated_at, - :dismissed_at, - :expired_at, - :hidden_at - - def initialize(attrs) - @id = self.class.generate_notice_id(attrs[:title], attrs[:created_at]) - @title = attrs[:title] - @message = attrs[:message] - @type = attrs[:type].to_i - @archetype = attrs[:archetype].to_i - @created_at = attrs[:created_at] - @updated_at = attrs[:updated_at] - @retrieved_at = attrs[:retrieved_at] - @dismissed_at = attrs[:dismissed_at] - @expired_at = attrs[:expired_at] - @hidden_at = attrs[:hidden_at] - end - - def dismiss! - if dismissable? - self.dismissed_at = DateTime.now.iso8601(3) - self.save_and_publish - end - end - - def hide! - if can_hide? - self.hidden_at = DateTime.now.iso8601(3) - self.save_and_publish - end - end - - def expire! - if !expired? - self.expired_at = DateTime.now.iso8601(3) - self.save_and_publish - end - end - - def save_and_publish - if self.save - self.class.publish_notice_count - true - else - false - end - end - - def expired? - expired_at.present? - end - - def dismissed? - dismissed_at.present? - end - - def dismissable? - !expired? && !dismissed? && type === self.class.types[:info] - end - - def hidden? - hidden_at.present? - end - - def can_hide? - !hidden? && ( - type === self.class.types[:connection_error] || - type === self.class.types[:warning] - ) && ( - archetype === self.class.archetypes[:plugin_status] - ) - end - - def save - attrs = { - expired_at: expired_at, - updated_at: updated_at, - retrieved_at: retrieved_at, - created_at: created_at, - title: title, - message: message, - type: type, - archetype: archetype - } - - if current = self.class.find(self.id) - attrs[:dismissed_at] = current.dismissed_at || self.dismissed_at - attrs[:hidden_at] = current.hidden_at || self.hidden_at - end - - self.class.store(id, attrs) - end - - def self.types - @types ||= Enum.new( - info: 0, - warning: 1, - connection_error: 2 - ) - end - - def self.archetypes - @archetypes ||= Enum.new( - subscription_message: 0, - plugin_status: 1 - ) - end - - def self.update(skip_subscription: false, skip_plugin: false) - notices = [] - - if !skip_subscription - subscription_messages = request(:subscription_message) - - if subscription_messages.present? - subscription_notices = convert_subscription_messages_to_notices(subscription_messages[:messages]) - notices.push(*subscription_notices) - end - end - - if !skip_plugin && request_plugin_status? - plugin_status = request(:plugin_status) - - if plugin_status.present? && plugin_status[:status].present? - plugin_notice = convert_plugin_status_to_notice(plugin_status) - notices.push(plugin_notice) if plugin_notice - end - end - - if notices.any? - notices.each do |notice_data| - notice = new(notice_data) - notice.retrieved_at = DateTime.now.iso8601(3) - notice.save - end - end - - publish_notice_count - end - - def self.publish_notice_count - payload = { - active_notice_count: CustomWizard::Notice.active_count - } - MessageBus.publish("/custom-wizard/notices", payload, group_ids: [Group::AUTO_GROUPS[:admins]]) - end - - def self.convert_subscription_messages_to_notices(messages) - messages.reduce([]) do |result, message| - id = generate_notice_id(message[:title], message[:created_at]) - result.push( - id: id, - title: message[:title], - message: message[:message], - type: types[message[:type].to_sym], - archetype: archetypes[:subscription_message], - created_at: message[:created_at], - expired_at: message[:expired_at] - ) - result - end - end - - def self.convert_plugin_status_to_notice(plugin_status) - notice = nil - - if PLUGIN_STATUSES_TO_WARN.include?(plugin_status[:status]) - title = I18n.t('wizard.notice.compatibility_issue.title') - created_at = plugin_status[:status_changed_at] - id = generate_notice_id(title, created_at) - - unless exists?(id) - message = I18n.t('wizard.notice.compatibility_issue.message', domain: plugin_status_domain) - notice = { - id: id, - title: title, - message: message, - type: types[:warning], - archetype: archetypes[:plugin_status], - created_at: created_at - } - end - else - expire_all(types[:warning], archetypes[:plugin_status]) - end - - notice - end - - def self.notify_connection_errors(archetype) - domain = self.send("#{archetype.to_s}_domain") - title = I18n.t("wizard.notice.#{archetype.to_s}.connection_error.title") - notices = list(type: types[:connection_error], archetype: archetypes[archetype.to_sym], title: title) - - if notices.any? - notice = notices.first - notice.updated_at = DateTime.now.iso8601(3) - notice.save - else - notice = new( - title: title, - message: I18n.t("wizard.notice.#{archetype.to_s}.connection_error.message", domain: domain), - archetype: archetypes[archetype.to_sym], - type: types[:connection_error], - created_at: DateTime.now.iso8601(3), - updated_at: DateTime.now.iso8601(3) - ) - notice.save - end - end - - def self.request_plugin_status? - CHECK_PLUGIN_STATUS_ON_BRANCH.include?(Discourse.git_branch) || Rails.env.test? || Rails.env.development? - end - - def self.subscription_message_domain - return LOCALHOST_DOMAIN if (Rails.env.test? || Rails.env.development?) - SUBSCRIPTION_MESSAGE_DOMAIN - end - - def self.subscription_message_url - "https://#{subscription_message_domain}/subscription-server/messages.json" - end - - def self.plugin_status_domain - return LOCALHOST_DOMAIN if (Rails.env.test? || Rails.env.development?) - PLUGIN_STATUS_DOMAINS[Discourse.git_branch] - end - - def self.plugin_status_url - "https://#{plugin_status_domain}/plugin-manager/status/discourse-custom-wizard" - end - - def self.request(archetype) - url = self.send("#{archetype.to_s}_url") - - begin - response = Excon.get(url) - rescue Excon::Error::Socket, Excon::Error::Timeout => e - response = nil - end - connection_error = CustomWizard::Notice::ConnectionError.new(archetype) - - if response && response.status == 200 - connection_error.expire! - expire_all(types[:connection_error], archetypes[archetype.to_sym]) - - begin - data = JSON.parse(response.body).deep_symbolize_keys - rescue JSON::ParserError - return nil - end - - data - else - connection_error.create! - notify_connection_errors(archetype) if connection_error.reached_limit? - nil - end - end - - def self.namespace - "#{CustomWizard::PLUGIN_NAME}_notice" - end - - def self.find(id) - raw = PluginStore.get(namespace, id) - new(raw.symbolize_keys) if raw.present? - end - - def self.exists?(id) - PluginStoreRow.where(plugin_name: namespace, key: id).exists? - end - - def self.store(id, raw_notice) - PluginStore.set(namespace, id, raw_notice) - end - - def self.list_query(type: nil, archetype: nil, title: nil, include_all: false, page: nil, visible: false) - query = PluginStoreRow.where(plugin_name: namespace) - query = query.where("(value::json->>'hidden_at') IS NULL") if visible - query = query.where("(value::json->>'dismissed_at') IS NULL") unless include_all - query = query.where("(value::json->>'expired_at') IS NULL") unless include_all - query = query.where("(value::json->>'archetype')::integer = ?", archetype) if archetype - if type - type_query_str = type.is_a?(Array) ? "(value::json->>'type')::integer IN (?)" : "(value::json->>'type')::integer = ?" - query = query.where(type_query_str, type) - end - query = query.where("(value::json->>'title')::text = ?", title) if title - query = query.limit(PAGE_LIMIT).offset(page.to_i * PAGE_LIMIT) if !page.nil? - query.order("value::json->>'expired_at' DESC, value::json->>'updated_at' DESC,value::json->>'dismissed_at' DESC, value::json->>'created_at' DESC") - end - - def self.list(type: nil, archetype: nil, title: nil, include_all: false, page: 0, visible: false) - list_query(type: type, archetype: archetype, title: title, include_all: include_all, page: page, visible: visible) - .map { |r| self.new(JSON.parse(r.value).symbolize_keys) } - end - - def self.active_count - list_query.count - end - - def self.dismiss_all - dismissed_count = PluginStoreRow.where(" - plugin_name = '#{namespace}' AND - (value::json->>'type')::integer = #{types[:info]} AND - (value::json->>'expired_at') IS NULL AND - (value::json->>'dismissed_at') IS NULL - ").update_all(" - value = jsonb_set(value::jsonb, '{dismissed_at}', (to_json(now())::text)::jsonb, true) - ") - publish_notice_count if dismissed_count.to_i > 0 - dismissed_count - end - - def self.expire_all(type, archetype) - expired_count = PluginStoreRow.where(" - plugin_name = '#{namespace}' AND - (value::json->>'type')::integer = #{type} AND - (value::json->>'archetype')::integer = #{archetype} AND - (value::json->>'expired_at') IS NULL - ").update_all(" - value = jsonb_set(value::jsonb, '{expired_at}', (to_json(now())::text)::jsonb, true) - ") - publish_notice_count if expired_count.to_i > 0 - expired_count - end - - def self.generate_notice_id(title, created_at) - Digest::SHA1.hexdigest("#{title}-#{created_at}") - end -end diff --git a/lib/custom_wizard/notice/connection_error.rb b/lib/custom_wizard/notice/connection_error.rb deleted file mode 100644 index a1d834c6..00000000 --- a/lib/custom_wizard/notice/connection_error.rb +++ /dev/null @@ -1,77 +0,0 @@ -# frozen_string_literal: true - -class CustomWizard::Notice::ConnectionError - - attr_reader :archetype - - def initialize(archetype) - @archetype = archetype - end - - def create! - if attrs = current_error - key = "#{archetype.to_s}_error_#{attrs[:id]}" - attrs[:updated_at] = Time.now - attrs[:count] = attrs[:count].to_i + 1 - else - domain = CustomWizard::Notice.send("#{archetype.to_s}_domain") - id = SecureRandom.hex(8) - attrs = { - id: id, - message: I18n.t("wizard.notice.connection_error", domain: domain), - archetype: CustomWizard::Notice.archetypes[archetype.to_sym], - created_at: Time.now, - count: 1 - } - key = "#{archetype.to_s}_error_#{id}" - end - - PluginStore.set(namespace, key, attrs) - - @current_error = nil - end - - def expire! - if query = current_error(query_only: true) - record = query.first - error = JSON.parse(record.value) - error['expired_at'] = Time.now - record.value = error.to_json - record.save - end - end - - def plugin_status_limit - 5 - end - - def subscription_message_limit - 10 - end - - def limit - self.send("#{archetype.to_s}_limit") - end - - def reached_limit? - return false unless current_error.present? - current_error[:count].to_i >= limit - end - - def namespace - "#{CustomWizard::PLUGIN_NAME}_notice_connection" - end - - def current_error(query_only: false) - @current_error ||= begin - query = PluginStoreRow.where(plugin_name: namespace) - query = query.where("(value::json->>'archetype')::integer = ?", CustomWizard::Notice.archetypes[archetype.to_sym]) - query = query.where("(value::json->>'expired_at') IS NULL") - - return nil if !query.exists? - return query if query_only - - JSON.parse(query.first.value).deep_symbolize_keys - end - end -end diff --git a/lib/custom_wizard/subscription.rb b/lib/custom_wizard/subscription.rb index 7bac7f59..114f6743 100644 --- a/lib/custom_wizard/subscription.rb +++ b/lib/custom_wizard/subscription.rb @@ -1,240 +1,170 @@ # frozen_string_literal: true - class CustomWizard::Subscription - include ActiveModel::Serialization + STANDARD_PRODUCT_ID = 'prod_LNAGVAaIqDsHmB' + BUSINESS_PRODUCT_ID = 'prod_LNABQ50maBQ1pY' - attr_accessor :authentication, - :subscription - - SUBSCRIPTION_LEVELS = { - standard: { - actions: ["send_message", "add_to_group", "watch_categories"], - custom_fields: { - klass: [], - type: ["json"], + def self.attributes + { + wizard: { + required: { + none: [], + standard: ['*'], + business: ['*'] + }, + permitted: { + none: [], + standard: ['*'], + business: ['*'] + } }, - }, - business: { - actions: ["create_category", "create_group", "send_to_api"], - custom_fields: { - klass: ["group", "category"], - type: [], + 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: ['text', 'textarea', 'text_only', 'date', 'time', 'date_time', 'number', 'checkbox', 'dropdown', 'upload'], + standard: ['*'], + business: ['*'] + }, + realtime_validations: { + none: [], + standard: ['*'], + business: ['*'] + } + }, + action: { + type: { + none: ['create_topic', 'update_profile', 'open_composer', 'route_to'], + standard: ['create_topic', 'update_profile', 'open_composer', 'route_to', 'send_message', 'watch_categories', 'add_to_group'], + business: ['*'] + } + }, + custom_field: { + klass: { + none: ['topic', 'post'], + standard: ['topic', 'post'], + business: ['*'] + }, + type: { + none: ['string', 'boolean', 'integer'], + standard: ['string', 'boolean', 'integer'], + business: ['*'] + } + } + } + end def initialize - @authentication = CustomWizard::Subscription::Authentication.new(get_authentication) - @subscription = CustomWizard::Subscription::Subscription.new(get_subscription) + @subscription = find_subscription end - def authorized? - @authentication.active? - end + def includes?(feature, attribute, value) + attributes = self.class.attributes[feature] - def subscribed? - @subscription.active? - end + ## Attribute is not part of a subscription + return true unless attributes.present? && attributes.key?(attribute) - def server - "test.thepavilion.io" - end + values = attributes[attribute][type] - def subscription_type - "stripe" + ## Subscription type does not support the attribute. + return false if values.blank? + + ## Subscription type supports all values of the attribute. + return true if values.first === "*" + + ## Subscription type supports some values of the attributes. + values.include?(value) end def type - @subscription.type + return :none unless subscribed? + return :standard if standard? + return :business if business? end - def client_name - "custom-wizard" + def subscribed? + standard? || business? end - def scope - "discourse-subscription-server:user_subscription" + def standard? + @subscription.product_id === STANDARD_PRODUCT_ID end - def requires_additional_subscription(kategory, sub_kategory) - case kategory - when "actions" - case self.type - when "business" - [] - when "standard" - SUBSCRIPTION_LEVELS[:business][kategory.to_sym] - else - SUBSCRIPTION_LEVELS[:standard][kategory.to_sym] + SUBSCRIPTION_LEVELS[:business][kategory.to_sym] - end - when "custom_fields" - case self.type - when "business" - [] - when "standard" - SUBSCRIPTION_LEVELS[:business][kategory.to_sym][sub_kategory.to_sym] - else - SUBSCRIPTION_LEVELS[:standard][kategory.to_sym][sub_kategory.to_sym] + SUBSCRIPTION_LEVELS[:business][kategory.to_sym][sub_kategory.to_sym] - end - else - [] - end + def business? + @subscription.product_id === BUSINESS_PRODUCT_ID end - def update - if @authentication.active? - response = Excon.get( - "https://#{server}/subscription-server/user-subscriptions/#{subscription_type}/#{client_name}", - headers: { - "User-Api-Key" => @authentication.api_key - } - ) + def client_installed? + defined?(SubscriptionClient) == 'constant' && SubscriptionClient.class == Module + end - if response.status == 200 - begin - data = JSON.parse(response.body).deep_symbolize_keys - rescue JSON::ParserError - return false - end + def find_subscription + subscription = nil - return false unless data && data.is_a?(Hash) - subscriptions = data[:subscriptions] - - if subscriptions.present? && type = subscriptions.first[:price_nickname] - @subscription = set_subscription(type) - return true - end - end + if client_installed? + subscription = SubscriptionClientSubscription.active + .where(product_id: [STANDARD_PRODUCT_ID, BUSINESS_PRODUCT_ID]) + .order("product_id = '#{BUSINESS_PRODUCT_ID}' DESC") + .first end - destroy_subscription - false - end - - def destroy_subscription - if remove_subscription - @subscription = CustomWizard::Subscription::Subscription.new(get_subscription) - !@subscription.active? - else - false + unless subscription + subscription = OpenStruct.new(product_id: nil) end - end - def authentication_url(user_id, request_id) - keys = @authentication.generate_keys(user_id, request_id) - params = { - public_key: keys.public_key, - nonce: keys.nonce, - client_id: @authentication.client_id, - auth_redirect: "#{Discourse.base_url}/admin/wizards/subscription/authorize/callback", - application_name: SiteSetting.title, - scopes: scope - } - - uri = URI.parse("https://#{server}/user-api-key/new") - uri.query = URI.encode_www_form(params) - uri.to_s - end - - def authentication_response(request_id, payload) - data = @authentication.decrypt_payload(request_id, payload) - return false unless data.is_a?(Hash) && data[:key] && data[:user_id] - - api_key = data[:key] - user_id = data[:user_id] - user = User.find(user_id) - - if user&.admin - @authentication = set_authentication(api_key, user.id) - true - else - false - end - end - - def destroy_authentication - if remove_authentication - @authentication = CustomWizard::Subscription::Authentication.new(get_authentication) - !@authentication.active? - else - false - end + subscription end def self.subscribed? - self.new.subscribed? + new.subscribed? + end + + def self.business? + new.business? + end + + def self.standard? + new.standard? end def self.type - self.new.type + new.type end - def self.requires_additional_subscription(kategory, sub_kategory) - self.new.requires_additional_subscription(kategory, sub_kategory) + def self.client_installed? + new.client_installed? end - def self.authorized? - self.new.authorized? - end - - def self.update - self.new.update - end - - def self.namespace - "custom_wizard_subscription" - end - - private - - def subscription_db_key - "subscription" - end - - def authentication_db_key - "authentication" - end - - def get_subscription - raw = PluginStore.get(self.class.namespace, subscription_db_key) - - if raw.present? - OpenStruct.new( - type: raw['type'], - updated_at: raw['updated_at'] - ) - end - end - - def remove_subscription - PluginStore.remove(self.class.namespace, subscription_db_key) - end - - def set_subscription(type) - PluginStore.set(CustomWizard::Subscription.namespace, subscription_db_key, type: type, updated_at: Time.now) - CustomWizard::Subscription::Subscription.new(get_subscription) - end - - def get_authentication - raw = PluginStore.get(self.class.namespace, authentication_db_key) - OpenStruct.new( - key: raw && raw['key'], - auth_by: raw && raw['auth_by'], - auth_at: raw && raw['auth_at'] - ) - end - - def set_authentication(key, user_id) - PluginStore.set(self.class.namespace, authentication_db_key, - key: key, - auth_by: user_id, - auth_at: Time.now - ) - CustomWizard::Subscription::Authentication.new(get_authentication) - end - - def remove_authentication - PluginStore.remove(self.class.namespace, authentication_db_key) - get_authentication + def self.includes?(feature, attribute, value) + new.includes?(feature, attribute, value) end end diff --git a/lib/custom_wizard/subscription/authentication.rb b/lib/custom_wizard/subscription/authentication.rb deleted file mode 100644 index 7ad02d2c..00000000 --- a/lib/custom_wizard/subscription/authentication.rb +++ /dev/null @@ -1,95 +0,0 @@ -# frozen_string_literal: true -class CustomWizard::Subscription::Authentication - include ActiveModel::Serialization - - attr_reader :client_id, - :auth_by, - :auth_at, - :api_key - - def initialize(auth) - if auth - @api_key = auth.key - @auth_at = auth.auth_at - @auth_by = auth.auth_by - end - - @client_id = get_client_id || set_client_id - end - - def active? - @api_key.present? - end - - def generate_keys(user_id, request_id) - rsa = OpenSSL::PKey::RSA.generate(2048) - nonce = SecureRandom.hex(32) - set_keys(request_id, user_id, rsa, nonce) - - OpenStruct.new(nonce: nonce, public_key: rsa.public_key) - end - - def decrypt_payload(request_id, payload) - keys = get_keys(request_id) - - return false unless keys.present? && keys.pem - delete_keys(request_id) - - rsa = OpenSSL::PKey::RSA.new(keys.pem) - decrypted_payload = rsa.private_decrypt(Base64.decode64(payload)) - - return false unless decrypted_payload.present? - - begin - data = JSON.parse(decrypted_payload).symbolize_keys - rescue JSON::ParserError - return false - end - - return false unless data[:nonce] == keys.nonce - data[:user_id] = keys.user_id - - data - end - - def get_keys(request_id) - raw = PluginStore.get(CustomWizard::Subscription.namespace, "#{keys_db_key}_#{request_id}") - OpenStruct.new( - user_id: raw && raw['user_id'], - pem: raw && raw['pem'], - nonce: raw && raw['nonce'] - ) - end - - private - - def keys_db_key - "keys" - end - - def client_id_db_key - "client_id" - end - - def set_keys(request_id, user_id, rsa, nonce) - PluginStore.set(CustomWizard::Subscription.namespace, "#{keys_db_key}_#{request_id}", - user_id: user_id, - pem: rsa.export, - nonce: nonce - ) - end - - def delete_keys(request_id) - PluginStore.remove(CustomWizard::Subscription.namespace, "#{keys_db_key}_#{request_id}") - end - - def get_client_id - PluginStore.get(CustomWizard::Subscription.namespace, client_id_db_key) - end - - def set_client_id - client_id = SecureRandom.hex(32) - PluginStore.set(CustomWizard::Subscription.namespace, client_id_db_key, client_id) - client_id - end -end diff --git a/lib/custom_wizard/subscription/subscription.rb b/lib/custom_wizard/subscription/subscription.rb deleted file mode 100644 index 0b3bb84d..00000000 --- a/lib/custom_wizard/subscription/subscription.rb +++ /dev/null @@ -1,22 +0,0 @@ -# frozen_string_literal: true -class CustomWizard::Subscription::Subscription - include ActiveModel::Serialization - - attr_reader :type, - :updated_at - - def initialize(subscription) - if subscription - @type = subscription.type - @updated_at = subscription.updated_at - end - end - - def types - %w(none standard business) - end - - def active? - types.include?(type) && updated_at.to_datetime > (Time.zone.now - 2.hours).to_datetime - end -end diff --git a/lib/custom_wizard/validators/template.rb b/lib/custom_wizard/validators/template.rb index 9ae37170..079f9884 100644 --- a/lib/custom_wizard/validators/template.rb +++ b/lib/custom_wizard/validators/template.rb @@ -54,34 +54,6 @@ class CustomWizard::TemplateValidator } end - 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 - ] - } - } - end - private def check_required(object, type) @@ -93,16 +65,10 @@ 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)) - ) + object.keys.each do |property| + value = object[property] - if is_subscription && !@subscription.subscribed? + if !@subscription.includes?(type, property.to_sym, value) errors.add :base, I18n.t("wizard.validation.subscription", type: type.to_s, property: property) end end @@ -148,10 +114,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 e745c3a8..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 @@ -70,15 +84,11 @@ after_initialize do ../app/controllers/custom_wizard/admin/logs.rb ../app/controllers/custom_wizard/admin/manager.rb ../app/controllers/custom_wizard/admin/custom_fields.rb - ../app/controllers/custom_wizard/admin/subscription.rb - ../app/controllers/custom_wizard/admin/notice.rb ../app/controllers/custom_wizard/wizard.rb ../app/controllers/custom_wizard/steps.rb ../app/controllers/custom_wizard/realtime_validations.rb ../app/jobs/regular/refresh_api_access_token.rb ../app/jobs/regular/set_after_time_wizard.rb - ../app/jobs/scheduled/custom_wizard/update_subscription.rb - ../app/jobs/scheduled/custom_wizard/update_notices.rb ../lib/custom_wizard/validators/template.rb ../lib/custom_wizard/validators/update.rb ../lib/custom_wizard/action_result.rb @@ -95,13 +105,9 @@ 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/notice.rb - ../lib/custom_wizard/notice/connection_error.rb - ../lib/custom_wizard/subscription.rb - ../lib/custom_wizard/subscription/subscription.rb - ../lib/custom_wizard/subscription/authentication.rb ../lib/custom_wizard/api/api.rb ../lib/custom_wizard/api/authorization.rb ../lib/custom_wizard/api/endpoint.rb @@ -122,10 +128,6 @@ after_initialize do ../app/serializers/custom_wizard/log_serializer.rb ../app/serializers/custom_wizard/submission_serializer.rb ../app/serializers/custom_wizard/realtime_validation/similar_topics_serializer.rb - ../app/serializers/custom_wizard/subscription/authentication_serializer.rb - ../app/serializers/custom_wizard/subscription/subscription_serializer.rb - ../app/serializers/custom_wizard/subscription_serializer.rb - ../app/serializers/custom_wizard/notice_serializer.rb ../lib/custom_wizard/extensions/extra_locales_controller.rb ../lib/custom_wizard/extensions/invites_controller.rb ../lib/custom_wizard/extensions/users_controller.rb @@ -271,14 +273,6 @@ after_initialize do "#{serializer_klass}_serializer".classify.constantize.prepend CustomWizardCustomFieldSerializer end - AdminDashboardData.add_problem_check do - warning_notices = CustomWizard::Notice.list( - type: CustomWizard::Notice.types[:warning], - archetype: CustomWizard::Notice.archetypes[:plugin_status] - ) - warning_notices.any? ? ActionView::Base.full_sanitizer.sanitize(warning_notices.first.message, tags: %w(a)) : nil - end - reloadable_patch do |plugin| ::TagsController.prepend CustomWizardTagsController ::DiscourseTagging.singleton_class.prepend CustomWizardDiscourseTagging diff --git a/spec/components/custom_wizard/action_spec.rb b/spec/components/custom_wizard/action_spec.rb index 6149272e..5155a7f4 100644 --- a/spec/components/custom_wizard/action_spec.rb +++ b/spec/components/custom_wizard/action_spec.rb @@ -8,6 +8,7 @@ describe CustomWizard::Action do let(:wizard_template) { get_wizard_fixture("wizard") } let(:open_composer) { get_wizard_fixture("actions/open_composer") } let(:create_category) { get_wizard_fixture("actions/create_category") } + let(:watch_categories) { get_wizard_fixture("actions/watch_categories") } let(:create_group) { get_wizard_fixture("actions/create_group") } let(:add_to_group) { get_wizard_fixture("actions/add_to_group") } let(:send_message) { get_wizard_fixture("actions/send_message") } @@ -158,17 +159,6 @@ describe CustomWizard::Action do end end - it 'watches categories' do - wizard = CustomWizard::Builder.new(@template[:id], user).build - wizard.create_updater(wizard.steps[0].id, step_1_field_1: "Text input").update - wizard.create_updater(wizard.steps[1].id, {}).update - - expect(CategoryUser.where( - category_id: category.id, - user_id: user.id - ).first.notification_level).to eq(0) - end - it 're-routes a user' do wizard = CustomWizard::Builder.new(@template[:id], user).build updater = wizard.create_updater(wizard.steps.last.id, {}) @@ -176,11 +166,25 @@ describe CustomWizard::Action do expect(updater.result[:redirect_on_next]).to eq("https://google.com") end - context "subscription actions" do + context "standard subscription actions" do before do enable_subscription("standard") end + it 'watches categories' do + watch_categories[:categories][0][:output] = category.id + wizard_template[:actions] << watch_categories + update_template(wizard_template) + + wizard = CustomWizard::Builder.new(@template[:id], user).build + wizard.create_updater(wizard.steps[0].id, step_1_field_1: "Text input").update + + expect(CategoryUser.where( + category_id: category.id, + user_id: user.id + ).first.notification_level).to eq(2) + end + it '#send_message' do wizard_template['actions'] << send_message update_template(wizard_template) @@ -233,9 +237,16 @@ describe CustomWizard::Action do expect(topic.first.allowed_groups.map(&:name)).to include('cool_group', 'cool_group_1') expect(post.exists?).to eq(true) end + end + + context "business subscription actions" do + before do + enable_subscription("business") + end it '#create_category' do wizard_template['actions'] << create_category + wizard_template['actions'] << create_group update_template(wizard_template) wizard = CustomWizard::Builder.new(@template[:id], user).build diff --git a/spec/components/custom_wizard/builder_spec.rb b/spec/components/custom_wizard/builder_spec.rb index 1d7b1213..ebcc355b 100644 --- a/spec/components/custom_wizard/builder_spec.rb +++ b/spec/components/custom_wizard/builder_spec.rb @@ -100,6 +100,7 @@ describe CustomWizard::Builder do context "with restricted permissions" do before do + enable_subscription("standard") @template[:permitted] = permitted_json["permitted"] CustomWizard::Template.save(@template.as_json) end @@ -304,7 +305,7 @@ describe CustomWizard::Builder do before do enable_subscription("standard") @template[:steps][0][:fields][0][:condition] = user_condition_json['condition'] - @template[:steps][2][:fields][5][:condition] = boolean_field_condition_json['condition'] + @template[:steps][2][:fields][0][:condition] = boolean_field_condition_json['condition'] CustomWizard::Template.save(@template.as_json) end @@ -325,7 +326,7 @@ describe CustomWizard::Builder do builder = CustomWizard::Builder.new(@template[:id], user) wizard = builder.build - expect(wizard.steps.last.fields.last.id).to eq(@template[:steps][2][:fields][5]['id']) + expect(wizard.steps.last.fields.last.id).to eq(@template[:steps][2][:fields][0]['id']) end end end diff --git a/spec/components/custom_wizard/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 index 47fb2d3f..984eff9f 100644 --- a/spec/components/custom_wizard/subscription_spec.rb +++ b/spec/components/custom_wizard/subscription_spec.rb @@ -1,125 +1,104 @@ # 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) + def undefine_client_classes + Object.send(:remove_const, :SubscriptionClient) if Object.constants.include?(:SubscriptionClient) + Object.send(:remove_const, :SubscriptionClientSubscription) if Object.constants.include?(:SubscriptionClientSubscription) end - it "returns authorized and subscribed states" do - subscription = described_class.new - expect(subscription.authorized?).to eq(false) - expect(subscription.subscribed?).to eq(false) + def define_client_classes + load File.expand_path("#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/subscription_client.rb", __FILE__) end - context "subscription" do + def stub_client_methods + [:active, :where, :order, :first].each do |method| + SubscriptionClientSubscription.stubs(method) + .returns(SubscriptionClientSubscription) + end + SubscriptionClientSubscription.stubs(:product_id).returns(SecureRandom.hex(8)) + end + + after do + undefine_client_classes + end + + it "detects the subscription client" do + expect(described_class.client_installed?).to eq(false) + end + + context "without a subscription client" do + it "is not subscribed" do + expect(described_class.subscribed?).to eq(false) + end + + it "has none type" do + subscription = described_class.new + expect(subscription.type).to eq(:none) + end + + it "non subscriber features are included" do + expect(described_class.includes?(:wizard, :after_signup, true)).to eq(true) + end + + it "ubscriber features are not included" do + expect(described_class.includes?(:wizard, :permitted, {})).to eq(false) + end + end + + context "with subscription client" do before do - @subscription = described_class.new + define_client_classes + stub_client_methods end - it "updates valid subscriptions" do - stub_subscription_request(200, valid_subscription) - expect(@subscription.update).to eq(true) - expect(@subscription.subscribed?).to eq(true) + it "detects the subscription client" do + expect(described_class.client_installed?).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) + context "without a subscription" do + it "has none type" do + expect(described_class.type).to eq(:none) + end + + it "non subscriber features are included" do + expect(described_class.includes?(:wizard, :after_signup, true)).to eq(true) + end + + it "subscriber features are not included" do + expect(described_class.includes?(:wizard, :permitted, {})).to eq(false) + end end - it "handles subscription http errors" do - stub_subscription_request(404, {}) - expect(@subscription.update).to eq(false) - expect(@subscription.subscribed?).to eq(false) + context "with standard subscription" do + before do + SubscriptionClientSubscription.stubs(:product_id).returns(CustomWizard::Subscription::STANDARD_PRODUCT_ID) + end + + it "detects standard type" do + expect(described_class.type).to eq(:standard) + end + + it "standard features are included" do + expect(described_class.includes?(:wizard, :type, 'send_message')).to eq(true) + end + + it "business features are not included" do + expect(described_class.includes?(:action, :type, 'create_category')).to eq(false) + end end - 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 + context "with business subscription" do + before do + SubscriptionClientSubscription.stubs(:product_id).returns(CustomWizard::Subscription::BUSINESS_PRODUCT_ID) + 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 + it "detects business type" do + expect(described_class.type).to eq(:business) + 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) + it "business are included" do + expect(described_class.includes?(:action, :type, 'create_category')).to eq(true) + end end end end diff --git a/spec/components/custom_wizard/template_spec.rb b/spec/components/custom_wizard/template_spec.rb index 14e1659b..63ea25c6 100644 --- a/spec/components/custom_wizard/template_spec.rb +++ b/spec/components/custom_wizard/template_spec.rb @@ -47,6 +47,8 @@ describe CustomWizard::Template do context "wizard template list" do before do + enable_subscription('standard') + template_json_2 = template_json.dup template_json_2["id"] = 'super_mega_fun_wizard_2' template_json_2["permitted"] = permitted_json['permitted'] diff --git a/spec/components/custom_wizard/template_validator_spec.rb b/spec/components/custom_wizard/template_validator_spec.rb index 3561e272..b149706f 100644 --- a/spec/components/custom_wizard/template_validator_spec.rb +++ b/spec/components/custom_wizard/template_validator_spec.rb @@ -5,6 +5,8 @@ describe CustomWizard::TemplateValidator do let(:template) { get_wizard_fixture("wizard") } let(:create_category) { get_wizard_fixture("actions/create_category") } let(:user_condition) { get_wizard_fixture("condition/user_condition") } + let(:permitted_json) { get_wizard_fixture("wizard/permitted") } + let(:composer_preview) { get_wizard_fixture("field/composer_preview") } let(:valid_liquid_template) { <<-LIQUID.strip @@ -104,7 +106,7 @@ describe CustomWizard::TemplateValidator do context "without subscription" do it "invalidates subscription wizard attributes" do - template[:save_submissions] = false + template[:permitted] = permitted_json['permitted'] expect( CustomWizard::TemplateValidator.new(template).perform ).to eq(false) @@ -134,11 +136,11 @@ describe CustomWizard::TemplateValidator do context "with subscription" do before do - enable_subscription("standard") + enable_subscription("business") end it "validates wizard attributes" do - template[:save_submissions] = false + template[:permitted] = permitted_json['permitted'] expect( CustomWizard::TemplateValidator.new(template).perform ).to eq(true) @@ -227,7 +229,9 @@ describe CustomWizard::TemplateValidator do end it "validates preview templates" do - template[:steps][0][:fields][4][:preview_template] = invalid_liquid_template + enable_subscription("standard") + template[:steps][0][:fields] << composer_preview + template[:steps][0][:fields][3][:preview_template] = invalid_liquid_template expect_validation_failure("step_1_field_5.preview_template", liquid_syntax_error) end end diff --git a/spec/components/custom_wizard/update_validator_spec.rb b/spec/components/custom_wizard/update_validator_spec.rb index a01f70d7..7caa1784 100644 --- a/spec/components/custom_wizard/update_validator_spec.rb +++ b/spec/components/custom_wizard/update_validator_spec.rb @@ -3,6 +3,7 @@ describe CustomWizard::UpdateValidator do fab!(:user) { Fabricate(:user) } let(:template) { get_wizard_fixture("wizard") } + let(:url_field) { get_wizard_fixture("field/url") } before do CustomWizard::Template.save(template, skip_jobs: true) @@ -21,7 +22,6 @@ describe CustomWizard::UpdateValidator do @template[:steps][0][:fields][0][:min_length] = min_length @template[:steps][0][:fields][1][:min_length] = min_length - @template[:steps][0][:fields][2][:min_length] = min_length CustomWizard::Template.save(@template) @@ -34,11 +34,6 @@ describe CustomWizard::UpdateValidator do expect( updater.errors.messages[:step_1_field_2].first ).to eq(I18n.t('wizard.field.too_short', label: 'Textarea', min: min_length)) - - updater = perform_validation('step_1', step_1_field_3: 'Te') - expect( - updater.errors.messages[:step_1_field_3].first - ).to eq(I18n.t('wizard.field.too_short', label: 'Composer', min: min_length)) end it 'prevents submission if the length is over the max length' do @@ -46,7 +41,6 @@ describe CustomWizard::UpdateValidator do @template[:steps][0][:fields][0][:max_length] = max_length @template[:steps][0][:fields][1][:max_length] = max_length - @template[:steps][0][:fields][2][:max_length] = max_length CustomWizard::Template.save(@template) long_string = "Our Competitive Capability solution offers platforms a suite of wholesale offerings. In the future, will you be able to effectively revolutionize synergies in your business? In the emerging market space, industry is ethically investing its mission critical executive searches. Key players will take ownership of their capabilities by iteratively right-sizing world-class visibilities. " @@ -59,11 +53,6 @@ describe CustomWizard::UpdateValidator do expect( updater.errors.messages[:step_1_field_2].first ).to eq(I18n.t('wizard.field.too_long', label: 'Textarea', max: max_length)) - - updater = perform_validation('step_1', step_1_field_3: long_string) - expect( - updater.errors.messages[:step_1_field_3].first - ).to eq(I18n.t('wizard.field.too_long', label: 'Composer', max: max_length)) end it "allows submission if the length is under or equal to the max length" do @@ -71,7 +60,6 @@ describe CustomWizard::UpdateValidator do @template[:steps][0][:fields][0][:max_length] = max_length @template[:steps][0][:fields][1][:max_length] = max_length - @template[:steps][0][:fields][2][:max_length] = max_length CustomWizard::Template.save(@template) hundred_chars_string = "This is a line, exactly hundred characters long and not more even a single character more than that." @@ -84,11 +72,6 @@ describe CustomWizard::UpdateValidator do expect( updater.errors.messages[:step_1_field_2].first ).to eq(nil) - - updater = perform_validation('step_1', step_1_field_3: hundred_chars_string) - expect( - updater.errors.messages[:step_1_field_3].first - ).to eq(nil) end it "applies min length only if the input is non-empty" do @@ -131,25 +114,33 @@ describe CustomWizard::UpdateValidator do ).to eq(I18n.t('wizard.field.required', label: 'Textarea')) end - it 'validates url fields' do - updater = perform_validation('step_2', step_2_field_6: 'https://discourse.com') - expect( - updater.errors.messages[:step_2_field_6].first - ).to eq(nil) - end + context "subscription fields" do + before do + enable_subscription("standard") + end - it 'does not validate url fields with non-url inputs' do - updater = perform_validation('step_2', step_2_field_6: 'discourse') - expect( - updater.errors.messages[:step_2_field_6].first - ).to eq(I18n.t('wizard.field.not_url', label: 'Url')) - end + it 'validates url fields' do + updater = perform_validation('step_2', step_2_field_6: 'https://discourse.com') + expect( + updater.errors.messages[:step_2_field_6].first + ).to eq(nil) + end - it 'validates empty url fields' do - updater = perform_validation('step_2', step_2_field_6: '') - expect( - updater.errors.messages[:step_2_field_6].first - ).to eq(nil) + it 'does not validate url fields with non-url inputs' do + template[:steps][0][:fields] << url_field + CustomWizard::Template.save(template) + updater = perform_validation('step_1', step_2_field_6: 'discourse') + expect( + updater.errors.messages[:step_2_field_6].first + ).to eq(I18n.t('wizard.field.not_url', label: 'Url')) + end + + it 'validates empty url fields' do + updater = perform_validation('step_2', step_2_field_6: '') + expect( + updater.errors.messages[:step_2_field_6].first + ).to eq(nil) + end end it 'validates date fields' do diff --git a/spec/components/custom_wizard/wizard_spec.rb b/spec/components/custom_wizard/wizard_spec.rb index 00354bf1..5dc1cb95 100644 --- a/spec/components/custom_wizard/wizard_spec.rb +++ b/spec/components/custom_wizard/wizard_spec.rb @@ -113,69 +113,91 @@ describe CustomWizard::Wizard do expect(wizard.completed?).to eq(false) end - it "permits admins" do - expect( - CustomWizard::Wizard.new(@permitted_template, admin_user).permitted? - ).to eq(true) - end + context "with subscription" do + before do + enable_subscription("standard") + end - it "permits permitted users" do - expect( - CustomWizard::Wizard.new(@permitted_template, trusted_user).permitted? - ).to eq(true) - end + it "permits admins" do + expect( + CustomWizard::Wizard.new(@permitted_template, admin_user).permitted? + ).to eq(true) + end - it "permits everyone if everyone is permitted" do - @permitted_template['permitted'][0]['output'] = Group::AUTO_GROUPS[:everyone] - expect( - CustomWizard::Wizard.new(@permitted_template, user).permitted? - ).to eq(true) - end + it "permits permitted users" do + expect( + CustomWizard::Wizard.new(@permitted_template, trusted_user).permitted? + ).to eq(true) + end - it "does not permit unpermitted users" do - expect( - CustomWizard::Wizard.new(@permitted_template, user).permitted? - ).to eq(false) - end + it "permits everyone if everyone is permitted" do + @permitted_template['permitted'][0]['output'] = Group::AUTO_GROUPS[:everyone] + expect( + CustomWizard::Wizard.new(@permitted_template, user).permitted? + ).to eq(true) + end - it "does not let an unpermitted user access a wizard" do - expect( - CustomWizard::Wizard.new(@permitted_template, user).can_access? - ).to eq(false) - end + it "does not permit unpermitted users" do + expect( + CustomWizard::Wizard.new(@permitted_template, user).permitted? + ).to eq(false) + end - it "lets a permitted user access an incomplete wizard" do - expect( - CustomWizard::Wizard.new(@permitted_template, trusted_user).can_access? - ).to eq(true) - end + it "does not let an unpermitted user access a wizard" do + expect( + CustomWizard::Wizard.new(@permitted_template, user).can_access? + ).to eq(false) + end - it "lets a permitted user access a complete wizard with multiple submissions" do - append_steps + it "lets a permitted user access an incomplete wizard" do + expect( + CustomWizard::Wizard.new(@permitted_template, trusted_user).can_access? + ).to eq(true) + end - progress_step("step_1", acting_user: trusted_user) - progress_step("step_2", acting_user: trusted_user) - progress_step("step_3", acting_user: trusted_user) + it "lets a permitted user access a complete wizard with multiple submissions" do + append_steps - @permitted_template["multiple_submissions"] = true + progress_step("step_1", acting_user: trusted_user) + progress_step("step_2", acting_user: trusted_user) + progress_step("step_3", acting_user: trusted_user) - expect( - CustomWizard::Wizard.new(@permitted_template, trusted_user).can_access? - ).to eq(true) - end + @permitted_template["multiple_submissions"] = true - it "does not let an unpermitted user access a complete wizard without multiple submissions" do - append_steps + expect( + CustomWizard::Wizard.new(@permitted_template, trusted_user).can_access? + ).to eq(true) + end - progress_step("step_1", acting_user: trusted_user) - progress_step("step_2", acting_user: trusted_user) - progress_step("step_3", acting_user: trusted_user) + it "does not let an unpermitted user access a complete wizard without multiple submissions" do + append_steps - @permitted_template['multiple_submissions'] = false + progress_step("step_1", acting_user: trusted_user) + progress_step("step_2", acting_user: trusted_user) + progress_step("step_3", acting_user: trusted_user) - expect( - CustomWizard::Wizard.new(@permitted_template, trusted_user).can_access? - ).to eq(false) + @permitted_template['multiple_submissions'] = false + + expect( + CustomWizard::Wizard.new(@permitted_template, trusted_user).can_access? + ).to eq(false) + end + + it "sets wizard redirects if user is permitted" do + CustomWizard::Template.save(@permitted_template, skip_jobs: true) + CustomWizard::Wizard.set_user_redirect('super_mega_fun_wizard', trusted_user) + expect( + trusted_user.custom_fields['redirect_to_wizard'] + ).to eq("super_mega_fun_wizard") + end + + it "does not set a wizard redirect if user is not permitted" do + CustomWizard::Template.save(@permitted_template, skip_jobs: true) + CustomWizard::Wizard.set_user_redirect('super_mega_fun_wizard', user) + expect( + trusted_user.custom_fields['redirect_to_wizard'] + ).to eq(nil) + end end it "lists the site groups" do @@ -203,6 +225,7 @@ describe CustomWizard::Wizard do context "class methods" do before do + enable_subscription("standard") CustomWizard::Template.save(@permitted_template, skip_jobs: true) template_json_2 = template_json.dup @@ -238,20 +261,4 @@ describe CustomWizard::Wizard do expect(CustomWizard::Wizard.prompt_completion(user).length).to eq(1) end end - - it "sets wizard redirects if user is permitted" do - CustomWizard::Template.save(@permitted_template, skip_jobs: true) - CustomWizard::Wizard.set_user_redirect('super_mega_fun_wizard', trusted_user) - expect( - trusted_user.custom_fields['redirect_to_wizard'] - ).to eq("super_mega_fun_wizard") - end - - it "does not set a wizard redirect if user is not permitted" do - CustomWizard::Template.save(@permitted_template, skip_jobs: true) - CustomWizard::Wizard.set_user_redirect('super_mega_fun_wizard', user) - expect( - trusted_user.custom_fields['redirect_to_wizard'] - ).to eq(nil) - end end diff --git a/spec/fixtures/actions/watch_categories.json b/spec/fixtures/actions/watch_categories.json new file mode 100644 index 00000000..20644f44 --- /dev/null +++ b/spec/fixtures/actions/watch_categories.json @@ -0,0 +1,23 @@ +{ + "id": "action_5", + "run_after": "step_1", + "type": "watch_categories", + "notification_level": "tracking", + "wizard_user": true, + "categories": [ + { + "type": "assignment", + "output": "", + "output_type": "text", + "output_connector": "set" + } + ], + "mute_remainder": [ + { + "type": "assignment", + "output": "true", + "output_type": "text", + "output_connector": "set" + } + ] +} diff --git a/spec/fixtures/field/advanced_types.json b/spec/fixtures/field/advanced_types.json new file mode 100644 index 00000000..63357cdb --- /dev/null +++ b/spec/fixtures/field/advanced_types.json @@ -0,0 +1,27 @@ +{ + "fields": [ + { + "id": "step_3_field_2", + "label": "Tag", + "type": "tag" + }, + { + "id": "step_3_field_3", + "label": "Category", + "type": "category", + "limit": 1, + "property": "id" + }, + { + "id": "step_3_field_4", + "label": "Group", + "type": "group" + }, + { + "id": "step_3_field_5", + "label": "User Selector", + "description": "", + "type": "user_selector" + } + ] +} diff --git a/spec/fixtures/field/composer_preview.json b/spec/fixtures/field/composer_preview.json new file mode 100644 index 00000000..f4bec04a --- /dev/null +++ b/spec/fixtures/field/composer_preview.json @@ -0,0 +1,7 @@ +{ + "id": "step_1_field_5", + "label": "I'm a preview", + "description": "", + "type": "composer_preview", + "preview_template": "w{step_1_field_1}" +} diff --git a/spec/fixtures/field/url.json b/spec/fixtures/field/url.json new file mode 100644 index 00000000..e3153fe9 --- /dev/null +++ b/spec/fixtures/field/url.json @@ -0,0 +1,5 @@ +{ + "id": "step_2_field_6", + "label": "Url", + "type": "url" +} diff --git a/spec/fixtures/subscription_client.rb b/spec/fixtures/subscription_client.rb new file mode 100644 index 00000000..a041b507 --- /dev/null +++ b/spec/fixtures/subscription_client.rb @@ -0,0 +1,4 @@ +# frozen_string_literal: true + +module SubscriptionClient; end +class SubscriptionClientSubscription; end diff --git a/spec/fixtures/wizard.json b/spec/fixtures/wizard.json index a05c1782..de5e636e 100644 --- a/spec/fixtures/wizard.json +++ b/spec/fixtures/wizard.json @@ -33,23 +33,11 @@ "type": "textarea", "min_length": "" }, - { - "id": "step_1_field_3", - "label": "Composer", - "type": "composer" - }, { "id": "step_1_field_4", "label": "I'm only text", "description": "", "type": "text_only" - }, - { - "id": "step_1_field_5", - "label": "I'm a preview", - "description": "", - "type": "composer_preview", - "preview_template": "w{step_1_field_1}" } ], "description": "Text inputs!" @@ -87,11 +75,6 @@ "label": "Checkbox", "type": "checkbox" }, - { - "id": "step_2_field_6", - "label": "Url", - "type": "url" - }, { "id": "step_2_field_7", "label": "Upload", @@ -141,35 +124,6 @@ ] } ] - }, - { - "id": "step_3_field_2", - "label": "Tag", - "type": "tag" - }, - { - "id": "step_3_field_3", - "label": "Category", - "type": "category", - "limit": 1, - "property": "id" - }, - { - "id": "step_3_field_4", - "label": "Group", - "type": "group" - }, - { - "id": "step_3_field_5", - "label": "User Selector", - "description": "", - "type": "user_selector" - }, - { - "id": "step_3_field_6", - "label": "Conditional User Selector", - "description": "Shown when checkbox in step_2_field_5 is true", - "type": "user_selector" } ], "description": "Unfortunately not the edible type :sushi: " @@ -264,29 +218,6 @@ } ] }, - { - "id": "action_5", - "run_after": "step_1", - "type": "watch_categories", - "notification_level": "tracking", - "wizard_user": true, - "categories": [ - { - "type": "assignment", - "output": "action_8", - "output_type": "wizard_action", - "output_connector": "set" - } - ], - "mute_remainder": [ - { - "type": "assignment", - "output": "true", - "output_type": "text", - "output_connector": "set" - } - ] - }, { "id": "action_4", "run_after": "step_2", diff --git a/spec/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..a0189de1 100644 --- a/spec/plugin_helper.rb +++ b/spec/plugin_helper.rb @@ -8,37 +8,8 @@ def get_wizard_fixture(path) ).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) + CustomWizard::Subscription.stubs(:client_installed?).returns(true) + CustomWizard::Subscription.stubs("#{type}?".to_sym).returns(true) + CustomWizard::Subscription.any_instance.stubs("#{type}?".to_sym).returns(true) end diff --git a/spec/requests/custom_wizard/admin/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/requests/custom_wizard/admin/wizard_controller_spec.rb b/spec/requests/custom_wizard/admin/wizard_controller_spec.rb index fd2fa006..9f63cb6b 100644 --- a/spec/requests/custom_wizard/admin/wizard_controller_spec.rb +++ b/spec/requests/custom_wizard/admin/wizard_controller_spec.rb @@ -8,6 +8,7 @@ describe CustomWizard::AdminWizardController do before do CustomWizard::Template.save(template, skip_jobs: true) + enable_subscription("standard") template_2 = template.dup template_2["id"] = 'super_mega_fun_wizard_2' diff --git a/spec/requests/custom_wizard/steps_controller_spec.rb b/spec/requests/custom_wizard/steps_controller_spec.rb index 26ba817a..e05ba917 100644 --- a/spec/requests/custom_wizard/steps_controller_spec.rb +++ b/spec/requests/custom_wizard/steps_controller_spec.rb @@ -34,6 +34,7 @@ describe CustomWizard::StepsController do end it "when the user cant access the wizard" do + enable_subscription("standard") new_template = wizard_template.dup new_template["permitted"] = permitted_json["permitted"] CustomWizard::Template.save(new_template, skip_jobs: true) diff --git a/spec/requests/custom_wizard/wizard_controller_spec.rb b/spec/requests/custom_wizard/wizard_controller_spec.rb index 3d081b8a..aa1f479b 100644 --- a/spec/requests/custom_wizard/wizard_controller_spec.rb +++ b/spec/requests/custom_wizard/wizard_controller_spec.rb @@ -40,14 +40,15 @@ describe CustomWizard::WizardController do end it 'lets user skip if user cant access wizard' do + enable_subscription("standard") @template["permitted"] = permitted_json["permitted"] CustomWizard::Template.save(@template, skip_jobs: true) - put '/w/super-mega-fun-wizard/skip.json' expect(response.status).to eq(200) end it 'returns a no skip message if user is not allowed to skip' do + enable_subscription("standard") @template['required'] = 'true' CustomWizard::Template.save(@template) put '/w/super-mega-fun-wizard/skip.json' diff --git a/spec/serializers/custom_wizard/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 diff --git a/spec/serializers/custom_wizard/wizard_field_serializer_spec.rb b/spec/serializers/custom_wizard/wizard_field_serializer_spec.rb index c30279b7..1ac2579e 100644 --- a/spec/serializers/custom_wizard/wizard_field_serializer_spec.rb +++ b/spec/serializers/custom_wizard/wizard_field_serializer_spec.rb @@ -19,7 +19,7 @@ describe CustomWizard::FieldSerializer do expect(json_array.size).to eq(@wizard.steps.first.fields.size) expect(json_array[0][:label]).to eq("

Text

") expect(json_array[0][:description]).to eq("Text field description.") - expect(json_array[3][:index]).to eq(3) + expect(json_array[2][:index]).to eq(2) end it "should return optional field attributes" do @@ -29,6 +29,6 @@ describe CustomWizard::FieldSerializer do scope: Guardian.new(user) ).as_json expect(json_array[0][:format]).to eq("YYYY-MM-DD") - expect(json_array[6][:file_types]).to eq(".jpg,.jpeg,.png") + expect(json_array[5][:file_types]).to eq(".jpg,.jpeg,.png") end end diff --git a/spec/serializers/custom_wizard/wizard_serializer_spec.rb b/spec/serializers/custom_wizard/wizard_serializer_spec.rb index 030f109e..526f5259 100644 --- a/spec/serializers/custom_wizard/wizard_serializer_spec.rb +++ b/spec/serializers/custom_wizard/wizard_serializer_spec.rb @@ -5,6 +5,7 @@ describe CustomWizard::WizardSerializer do fab!(:category) { Fabricate(:category) } let(:template) { get_wizard_fixture("wizard") } let(:similar_topics_validation) { get_wizard_fixture("field/validation/similar_topics") } + let(:advanced_fields) { get_wizard_fixture("field/advanced_types") } before do CustomWizard::Template.save(template, skip_jobs: true) @@ -52,43 +53,51 @@ describe CustomWizard::WizardSerializer do expect(json[:wizard][:uncategorized_category_id].present?).to eq(false) end - it "should return categories if there is a category selector field" do - json = CustomWizard::WizardSerializer.new( - CustomWizard::Builder.new(@template[:id], user).build, - scope: Guardian.new(user) - ).as_json - expect(json[:wizard][:categories].present?).to eq(true) - expect(json[:wizard][:uncategorized_category_id].present?).to eq(true) - end + context "advanced fields" do + before do + enable_subscription("standard") + @template[:steps][0][:fields].push(*advanced_fields['fields']) + CustomWizard::Template.save(@template, skip_jobs: true) + end - it "should return categories if there is a similar topics validation scoped to category(s)" do - @template[:steps][0][:fields][0][:validations] = similar_topics_validation[:validations] - CustomWizard::Template.save(@template) + it "should return categories if there is a category selector field" do + json = CustomWizard::WizardSerializer.new( + CustomWizard::Builder.new(@template[:id], user).build, + scope: Guardian.new(user) + ).as_json + expect(json[:wizard][:categories].present?).to eq(true) + expect(json[:wizard][:uncategorized_category_id].present?).to eq(true) + end - json = CustomWizard::WizardSerializer.new( - CustomWizard::Builder.new(@template[:id], user).build, - scope: Guardian.new(user) - ).as_json - expect(json[:wizard][:categories].present?).to eq(true) - expect(json[:wizard][:uncategorized_category_id].present?).to eq(true) - end + it "should return categories if there is a similar topics validation scoped to category(s)" do + @template[:steps][0][:fields][0][:validations] = similar_topics_validation[:validations] + CustomWizard::Template.save(@template) - it 'should return groups if there is a group selector field' do - json = CustomWizard::WizardSerializer.new( - CustomWizard::Builder.new(@template[:id], user).build, - scope: Guardian.new(user) - ).as_json - expect(json[:wizard][:groups].length).to eq(8) - end + json = CustomWizard::WizardSerializer.new( + CustomWizard::Builder.new(@template[:id], user).build, + scope: Guardian.new(user) + ).as_json + expect(json[:wizard][:categories].present?).to eq(true) + expect(json[:wizard][:uncategorized_category_id].present?).to eq(true) + end - it 'should not return groups if there is not a group selector field' do - @template[:steps][2][:fields].delete_at(3) - CustomWizard::Template.save(@template) + it 'should return groups if there is a group selector field' do + json = CustomWizard::WizardSerializer.new( + CustomWizard::Builder.new(@template[:id], user).build, + scope: Guardian.new(user) + ).as_json + expect(json[:wizard][:groups].length).to eq(8) + end - json = CustomWizard::WizardSerializer.new( - CustomWizard::Builder.new(@template[:id], user).build, - scope: Guardian.new(user) - ).as_json - expect(json[:wizard][:groups].present?).to eq(false) + it 'should not return groups if there is not a group selector field' do + @template[:steps][0][:fields].reject! { |f| f["type"] === "group" } + CustomWizard::Template.save(@template) + + json = CustomWizard::WizardSerializer.new( + CustomWizard::Builder.new(@template[:id], user).build, + scope: Guardian.new(user) + ).as_json + expect(json[:wizard][:groups].present?).to eq(false) + end end end