Merge pull request #192 from paviliondev/remove_subs_and_notices
Remove subs and notices
Dieser Commit ist enthalten in:
Commit
c56e5dcbf8
100 geänderte Dateien mit 828 neuen und 3100 gelöschten Zeilen
|
@ -3,17 +3,12 @@ class CustomWizard::AdminController < ::Admin::AdminController
|
||||||
before_action :ensure_admin
|
before_action :ensure_admin
|
||||||
|
|
||||||
def index
|
def index
|
||||||
|
subcription = CustomWizard::Subscription.new
|
||||||
render_json_dump(
|
render_json_dump(
|
||||||
#TODO replace with appropriate static?
|
subscribed: subcription.subscribed?,
|
||||||
api_section: ["business"].include?(CustomWizard::Subscription.type),
|
subscription_type: subcription.type,
|
||||||
active_notice_count: CustomWizard::Notice.active_count,
|
subscription_attributes: CustomWizard::Subscription.attributes,
|
||||||
featured_notices: ActiveModel::ArraySerializer.new(
|
subscription_client_installed: subcription.client_installed?
|
||||||
CustomWizard::Notice.list(
|
|
||||||
type: CustomWizard::Notice.types[:info],
|
|
||||||
archetype: CustomWizard::Notice.archetypes[:subscription_message]
|
|
||||||
),
|
|
||||||
each_serializer: CustomWizard::NoticeSerializer
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -2,9 +2,7 @@
|
||||||
class CustomWizard::AdminCustomFieldsController < CustomWizard::AdminController
|
class CustomWizard::AdminCustomFieldsController < CustomWizard::AdminController
|
||||||
def index
|
def index
|
||||||
render_json_dump(
|
render_json_dump(
|
||||||
custom_fields: custom_field_list,
|
custom_fields: custom_field_list
|
||||||
subscribed: CustomWizard::Subscription.subscribed?,
|
|
||||||
subscription: CustomWizard::Subscription.type
|
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -10,9 +10,7 @@ class CustomWizard::AdminWizardController < CustomWizard::AdminController
|
||||||
),
|
),
|
||||||
field_types: CustomWizard::Field.types,
|
field_types: CustomWizard::Field.types,
|
||||||
realtime_validations: CustomWizard::RealtimeValidation.types,
|
realtime_validations: CustomWizard::RealtimeValidation.types,
|
||||||
custom_fields: custom_field_list,
|
custom_fields: custom_field_list
|
||||||
subscribed: CustomWizard::Subscription.subscribed?,
|
|
||||||
subscription: CustomWizard::Subscription.type
|
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,6 @@ class CustomWizard::WizardController < ::ActionController::Base
|
||||||
|
|
||||||
before_action :preload_wizard_json
|
before_action :preload_wizard_json
|
||||||
before_action :ensure_plugin_enabled
|
before_action :ensure_plugin_enabled
|
||||||
before_action :update_subscription, only: [:index]
|
|
||||||
before_action :ensure_logged_in, only: [:skip]
|
before_action :ensure_logged_in, only: [:skip]
|
||||||
|
|
||||||
helper_method :wizard_page_title
|
helper_method :wizard_page_title
|
||||||
|
@ -45,7 +44,7 @@ class CustomWizard::WizardController < ::ActionController::Base
|
||||||
return render json: { error: I18n.t('wizard.no_skip') }
|
return render json: { error: I18n.t('wizard.no_skip') }
|
||||||
end
|
end
|
||||||
|
|
||||||
result = success_json
|
result = { success: 'OK' }
|
||||||
|
|
||||||
if current_user && wizard.can_access?
|
if current_user && wizard.can_access?
|
||||||
if redirect_to = wizard.current_submission&.redirect_to
|
if redirect_to = wizard.current_submission&.redirect_to
|
||||||
|
@ -123,8 +122,4 @@ class CustomWizard::WizardController < ::ActionController::Base
|
||||||
redirect_to path("/")
|
redirect_to path("/")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_subscription
|
|
||||||
CustomWizard::Subscription.update
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
class Jobs::CustomWizardUpdateNotices < ::Jobs::Scheduled
|
|
||||||
every 5.minutes
|
|
||||||
|
|
||||||
def execute(args = {})
|
|
||||||
CustomWizard::Notice.update
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,9 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
class Jobs::CustomWizardUpdateSubscription < ::Jobs::Scheduled
|
|
||||||
every 1.hour
|
|
||||||
|
|
||||||
def execute(args = {})
|
|
||||||
CustomWizard::Subscription.update
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -1,10 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
class CustomWizard::Subscription::SubscriptionSerializer < ApplicationSerializer
|
|
||||||
attributes :type,
|
|
||||||
:active,
|
|
||||||
:updated_at
|
|
||||||
|
|
||||||
def active
|
|
||||||
object.active?
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -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
|
|
|
@ -3,29 +3,6 @@ import discourseComputed, { observes } from "discourse-common/utils/decorators";
|
||||||
import { alias, equal, or } from "@ember/object/computed";
|
import { alias, equal, or } from "@ember/object/computed";
|
||||||
import I18n from "I18n";
|
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({
|
export default Component.extend({
|
||||||
tagName: "tr",
|
tagName: "tr",
|
||||||
topicSerializers: ["topic_view", "topic_list_item"],
|
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")
|
@observes("field.klass")
|
||||||
clearSerializersWhenClassChanges() {
|
clearSerializersWhenClassChanges() {
|
||||||
this.set("field.serializers", null);
|
this.set("field.serializers", null);
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
import { default as discourseComputed } from "discourse-common/utils/decorators";
|
import { default as discourseComputed } from "discourse-common/utils/decorators";
|
||||||
import wizardSchema, {
|
import { subscriptionSelectKitContent } from "discourse/plugins/discourse-custom-wizard/discourse/lib/wizard-subscription";
|
||||||
requiringAdditionalSubscription,
|
|
||||||
subscriptionLevel,
|
|
||||||
} from "discourse/plugins/discourse-custom-wizard/discourse/lib/wizard-schema";
|
|
||||||
import { empty, equal, or } from "@ember/object/computed";
|
import { empty, equal, or } from "@ember/object/computed";
|
||||||
import { notificationLevels, selectKitContent } from "../lib/wizard";
|
import { notificationLevels, selectKitContent } from "../lib/wizard";
|
||||||
import { computed } from "@ember/object";
|
import { computed } from "@ember/object";
|
||||||
|
@ -97,22 +94,8 @@ export default Component.extend(UndoChanges, {
|
||||||
return apis.find((a) => a.name === api).endpoints;
|
return apis.find((a) => a.name === api).endpoints;
|
||||||
},
|
},
|
||||||
|
|
||||||
@discourseComputed("subscription")
|
@discourseComputed
|
||||||
actionTypes(subscription) {
|
actionTypes() {
|
||||||
let unsubscribedActions = requiringAdditionalSubscription(
|
return subscriptionSelectKitContent("action", "types");
|
||||||
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;
|
|
||||||
}, []);
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -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();
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -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);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -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);
|
||||||
|
},
|
||||||
|
});
|
|
@ -1,8 +1,9 @@
|
||||||
import Component from "@ember/component";
|
import Component from "@ember/component";
|
||||||
import discourseComputed from "discourse-common/utils/decorators";
|
import discourseComputed from "discourse-common/utils/decorators";
|
||||||
|
import Subscription from "../mixins/subscription";
|
||||||
|
|
||||||
export default Component.extend({
|
export default Component.extend(Subscription, {
|
||||||
classNameBindings: [":subscription-container", "subscribed"],
|
classNameBindings: [":wizard-subscription-container", "subscribed"],
|
||||||
|
|
||||||
@discourseComputed("subscribed")
|
@discourseComputed("subscribed")
|
||||||
subscribedIcon(subscribed) {
|
subscribedIcon(subscribed) {
|
|
@ -1,6 +1,22 @@
|
||||||
import SingleSelectComponent from "select-kit/components/single-select";
|
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"],
|
classNames: ["combo-box", "wizard-subscription-selector"],
|
||||||
|
|
||||||
selectKitOptions: {
|
selectKitOptions: {
|
||||||
|
@ -13,6 +29,47 @@ export default SingleSelectComponent.extend({
|
||||||
caretDownIcon: "caret-down",
|
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() {
|
modifyComponentForRow() {
|
||||||
return "wizard-subscription-selector/wizard-subscription-selector-row";
|
return "wizard-subscription-selector/wizard-subscription-selector-row";
|
||||||
},
|
},
|
||||||
|
|
|
@ -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);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -1,5 +0,0 @@
|
||||||
{{#if notices}}
|
|
||||||
{{#each notices as |notice|}}
|
|
||||||
{{wizard-notice notice=notice showPlugin=true}}
|
|
||||||
{{/each}}
|
|
||||||
{{/if}}
|
|
|
@ -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"));
|
|
||||||
});
|
|
||||||
},
|
|
||||||
};
|
|
|
@ -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)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -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);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -1,26 +1,7 @@
|
||||||
import Controller, { inject as controller } from "@ember/controller";
|
import Controller from "@ember/controller";
|
||||||
import { isPresent } from "@ember/utils";
|
import { equal } from "@ember/object/computed";
|
||||||
import { A } from "@ember/array";
|
|
||||||
|
|
||||||
export default Controller.extend({
|
export default Controller.extend({
|
||||||
adminWizardsNotices: controller(),
|
businessSubscription: equal("subscriptionType", "business"),
|
||||||
|
standardSubscription: equal("subscriptionType", "standard"),
|
||||||
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();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -58,16 +58,6 @@ export default {
|
||||||
path: "/manager",
|
path: "/manager",
|
||||||
resetNamespace: true,
|
resetNamespace: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.route("adminWizardsSubscription", {
|
|
||||||
path: "/subscription",
|
|
||||||
resetNamespace: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
this.route("adminWizardsNotices", {
|
|
||||||
path: "/notices",
|
|
||||||
resetNamespace: true,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
@ -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 += `<span>${I18n.t(attrs.label)}</span>`;
|
|
||||||
}
|
|
||||||
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 += `</${tag}>`;
|
|
||||||
return htmlSafe(html);
|
|
||||||
});
|
|
|
@ -1,8 +1,5 @@
|
||||||
import DiscourseURL from "discourse/lib/url";
|
import DiscourseURL from "discourse/lib/url";
|
||||||
import { withPluginApi } from "discourse/lib/plugin-api";
|
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";
|
import getUrl from "discourse-common/lib/get-url";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@ -23,50 +20,6 @@ export default {
|
||||||
};
|
};
|
||||||
|
|
||||||
withPluginApi("0.8.36", (api) => {
|
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", {
|
api.modifyClass("component:d-navigation", {
|
||||||
pluginId: "custom-wizard",
|
pluginId: "custom-wizard",
|
||||||
actions: {
|
actions: {
|
||||||
|
|
|
@ -203,23 +203,8 @@ const custom_field = {
|
||||||
type: ["string", "boolean", "integer", "json"],
|
type: ["string", "boolean", "integer", "json"],
|
||||||
};
|
};
|
||||||
|
|
||||||
const subscription_levels = {
|
field.type = Object.keys(field.types);
|
||||||
standard: {
|
action.type = Object.keys(action.types);
|
||||||
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: [],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const wizardSchema = {
|
const wizardSchema = {
|
||||||
wizard,
|
wizard,
|
||||||
|
@ -227,77 +212,8 @@ const wizardSchema = {
|
||||||
field,
|
field,
|
||||||
custom_field,
|
custom_field,
|
||||||
action,
|
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) {
|
export function buildFieldTypes(types) {
|
||||||
wizardSchema.field.types = types;
|
wizardSchema.field.types = types;
|
||||||
}
|
}
|
||||||
|
|
22
assets/javascripts/discourse/lib/wizard-subscription.js.es6
Normale Datei
22
assets/javascripts/discourse/lib/wizard-subscription.js.es6
Normale Datei
|
@ -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 };
|
|
@ -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);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
30
assets/javascripts/discourse/mixins/subscription.js.es6
Normale Datei
30
assets/javascripts/discourse/mixins/subscription.js.es6
Normale Datei
|
@ -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;
|
||||||
|
},
|
||||||
|
});
|
|
@ -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;
|
|
|
@ -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;
|
|
|
@ -9,13 +9,9 @@ export default DiscourseRoute.extend({
|
||||||
|
|
||||||
setupController(controller, model) {
|
setupController(controller, model) {
|
||||||
const customFields = A(model.custom_fields || []);
|
const customFields = A(model.custom_fields || []);
|
||||||
const subscribed = model.subscribed;
|
|
||||||
const subscription = model.subscription;
|
|
||||||
|
|
||||||
controller.setProperties({
|
controller.setProperties({
|
||||||
customFields,
|
customFields,
|
||||||
subscribed,
|
|
||||||
subscription,
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -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))
|
|
||||||
),
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -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();
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -39,8 +39,6 @@ export default DiscourseRoute.extend({
|
||||||
currentStep: wizard.steps[0],
|
currentStep: wizard.steps[0],
|
||||||
currentAction: wizard.actions[0],
|
currentAction: wizard.actions[0],
|
||||||
creating: model.create,
|
creating: model.create,
|
||||||
subscribed: parentModel.subscribed,
|
|
||||||
subscription: parentModel.subscription,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
controller.setProperties(props);
|
controller.setProperties(props);
|
||||||
|
|
|
@ -7,16 +7,12 @@ export default DiscourseRoute.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
setupController(controller, model) {
|
setupController(controller, model) {
|
||||||
controller.set("api_section", model.api_section);
|
controller.setProperties({
|
||||||
|
subscribed: model.subscribed,
|
||||||
if (model.active_notice_count) {
|
subscriptionType: model.subscription_type,
|
||||||
controller.set("activeNoticeCount", model.active_notice_count);
|
subscriptionAttributes: model.subscription_attributes,
|
||||||
}
|
subscriptionClientInstalled: model.subscription_client_installed,
|
||||||
if (model.featured_notices) {
|
});
|
||||||
controller.set("featuredNotices", model.featured_notices);
|
|
||||||
}
|
|
||||||
|
|
||||||
controller.subscribe();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
afterModel(model, transition) {
|
afterModel(model, transition) {
|
||||||
|
|
|
@ -32,9 +32,7 @@
|
||||||
{{custom-field-input
|
{{custom-field-input
|
||||||
field=field
|
field=field
|
||||||
removeField=(action "removeField")
|
removeField=(action "removeField")
|
||||||
saveField=(action "saveField")
|
saveField=(action "saveField")}}
|
||||||
subscribed=subscribed
|
|
||||||
subscription=subscription}}
|
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
|
@ -1,49 +0,0 @@
|
||||||
<div class="admin-wizard-controls">
|
|
||||||
<h3>{{i18n "admin.wizard.notices.title"}}</h3>
|
|
||||||
|
|
||||||
<div class="buttons">
|
|
||||||
{{d-button
|
|
||||||
label="admin.wizard.notice.dismiss_all.label"
|
|
||||||
title="admin.wizard.notice.dismiss_all.title"
|
|
||||||
action=(action "dismissAll")
|
|
||||||
disabled=allDismisssed
|
|
||||||
icon="check"}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{wizard-message
|
|
||||||
key=messageKey
|
|
||||||
url=messageUrl
|
|
||||||
type=messageType
|
|
||||||
opts=messageOpts
|
|
||||||
items=messageItems
|
|
||||||
loading=loading
|
|
||||||
component="notices"}}
|
|
||||||
|
|
||||||
<div class="wizard-table">
|
|
||||||
{{#load-more selector=".wizard-table tr" action=(action "loadMore")}}
|
|
||||||
{{#if hasNotices}}
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>{{I18n "admin.wizard.notice.time"}}</th>
|
|
||||||
<th>{{I18n "admin.wizard.notice.type.label"}}</th>
|
|
||||||
<th>{{I18n "admin.wizard.notice.title"}}</th>
|
|
||||||
<th>{{I18n "admin.wizard.notice.status"}}</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{{#each notices as |notice|}}
|
|
||||||
{{wizard-notice-row notice=notice}}
|
|
||||||
{{/each}}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
{{else}}
|
|
||||||
{{#unless loadingMore}}
|
|
||||||
<p>{{i18n "search.no_results"}}</p>
|
|
||||||
{{/unless}}
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{conditional-loading-spinner condition=loadingMore}}
|
|
||||||
{{/load-more}}
|
|
||||||
</div>
|
|
|
@ -1,35 +0,0 @@
|
||||||
<div class="admin-wizard-controls">
|
|
||||||
<h3>{{i18n "admin.wizard.subscription.title"}}</h3>
|
|
||||||
|
|
||||||
<div class="buttons">
|
|
||||||
{{#if model.authentication.active}}
|
|
||||||
{{conditional-loading-spinner size="small" condition=unauthorizing}}
|
|
||||||
<a {{action "unauthorize"}} title={{i18n "admin.wizard.subscription.unauthorize"}} role="button">
|
|
||||||
{{i18n "admin.wizard.subscription.unauthorize"}}
|
|
||||||
</a>
|
|
||||||
<label title={{i18n "admin.wizard.subscription.authorized"}}>
|
|
||||||
{{i18n "admin.wizard.subscription.authorized"}}
|
|
||||||
</label>
|
|
||||||
{{else}}
|
|
||||||
{{d-button
|
|
||||||
icon="id-card"
|
|
||||||
class="btn-primary"
|
|
||||||
label="admin.wizard.subscription.authorize"
|
|
||||||
title="admin.wizard.subscription.authorize"
|
|
||||||
action=(route-action "authorize")}}
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{wizard-message
|
|
||||||
key=messageKey
|
|
||||||
url=messageUrl
|
|
||||||
type=messageType
|
|
||||||
opts=messageOpts
|
|
||||||
component="subscription"}}
|
|
||||||
|
|
||||||
<div class="admin-wizard-container">
|
|
||||||
{{#if showSubscription}}
|
|
||||||
{{wizard-subscription subscription=model.subscription}}
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
|
@ -126,7 +126,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{#subscription-container subscribed=subscribed}}
|
{{#wizard-subscription-container}}
|
||||||
<div class="setting">
|
<div class="setting">
|
||||||
<div class="setting-label">
|
<div class="setting-label">
|
||||||
<label>{{i18n "admin.wizard.save_submissions"}}</label>
|
<label>{{i18n "admin.wizard.save_submissions"}}</label>
|
||||||
|
@ -146,7 +146,7 @@
|
||||||
<span>{{i18n "admin.wizard.restart_on_revisit_label"}}</span>
|
<span>{{i18n "admin.wizard.restart_on_revisit_label"}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{/subscription-container}}
|
{{/wizard-subscription-container}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{wizard-links
|
{{wizard-links
|
||||||
|
@ -177,9 +177,7 @@
|
||||||
wizard=wizard
|
wizard=wizard
|
||||||
apis=apis
|
apis=apis
|
||||||
removeAction="removeAction"
|
removeAction="removeAction"
|
||||||
wizardFields=wizardFields
|
wizardFields=wizardFields}}
|
||||||
subscribed=subscribed
|
|
||||||
subscription=subscription}}
|
|
||||||
{{/each}}
|
{{/each}}
|
||||||
|
|
||||||
<div class="admin-wizard-buttons">
|
<div class="admin-wizard-buttons">
|
||||||
|
|
|
@ -2,20 +2,14 @@
|
||||||
{{nav-item route="adminWizardsWizard" label="admin.wizard.nav_label"}}
|
{{nav-item route="adminWizardsWizard" label="admin.wizard.nav_label"}}
|
||||||
{{nav-item route="adminWizardsCustomFields" label="admin.wizard.custom_field.nav_label"}}
|
{{nav-item route="adminWizardsCustomFields" label="admin.wizard.custom_field.nav_label"}}
|
||||||
{{nav-item route="adminWizardsSubmissions" label="admin.wizard.submissions.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"}}
|
{{nav-item route="adminWizardsApi" label="admin.wizard.api.nav_label"}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{nav-item route="adminWizardsLogs" label="admin.wizard.log.nav_label"}}
|
{{nav-item route="adminWizardsLogs" label="admin.wizard.log.nav_label"}}
|
||||||
{{nav-item route="adminWizardsManager" label="admin.wizard.manager.nav_label"}}
|
{{nav-item route="adminWizardsManager" label="admin.wizard.manager.nav_label"}}
|
||||||
{{nav-item route="adminWizardsSubscription" label="admin.wizard.subscription.nav_label"}}
|
|
||||||
|
|
||||||
<div class="admin-actions">
|
<div class="admin-actions">
|
||||||
<div class="wizard-notices-link">
|
{{wizard-subscription-badge}}
|
||||||
{{nav-item tagName="div" route="adminWizardsNotices" label="admin.wizard.notices.nav_label"}}
|
|
||||||
{{#if activeNoticeCount}}
|
|
||||||
<span class="active-notice-count">{{activeNoticeCount}}</span>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
<a target="_blank" class="btn btn-pavilion-support" rel="noreferrer noopener" href="https://thepavilion.io/w/support" title={{i18n "admin.wizard.support_button.title"}}>
|
<a target="_blank" class="btn btn-pavilion-support" rel="noreferrer noopener" href="https://thepavilion.io/w/support" title={{i18n "admin.wizard.support_button.title"}}>
|
||||||
{{d-icon "far-life-ring"}}{{i18n "admin.wizard.support_button.label"}}
|
{{d-icon "far-life-ring"}}{{i18n "admin.wizard.support_button.label"}}
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -2,16 +2,22 @@
|
||||||
<td>
|
<td>
|
||||||
{{wizard-subscription-selector
|
{{wizard-subscription-selector
|
||||||
value=field.klass
|
value=field.klass
|
||||||
content=customFieldKlasses
|
feature="custom_field"
|
||||||
|
attribute="klass"
|
||||||
|
onChange=(action (mut field.klass))
|
||||||
|
options=(hash
|
||||||
none="admin.wizard.custom_field.klass.select"
|
none="admin.wizard.custom_field.klass.select"
|
||||||
onChange=(action (mut field.klass))}}
|
)}}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{{wizard-subscription-selector
|
{{wizard-subscription-selector
|
||||||
value=field.type
|
value=field.type
|
||||||
content=customFieldTypes
|
feature="custom_field"
|
||||||
|
attribute="type"
|
||||||
|
onChange=(action (mut field.type))
|
||||||
|
options=(hash
|
||||||
none="admin.wizard.custom_field.type.select"
|
none="admin.wizard.custom_field.type.select"
|
||||||
onChange=(action (mut field.type))}}
|
)}}
|
||||||
</td>
|
</td>
|
||||||
<td class="input">
|
<td class="input">
|
||||||
{{input
|
{{input
|
||||||
|
@ -22,8 +28,10 @@
|
||||||
{{multi-select
|
{{multi-select
|
||||||
value=field.serializers
|
value=field.serializers
|
||||||
content=serializerContent
|
content=serializerContent
|
||||||
|
onChange=(action (mut field.serializers))
|
||||||
|
options=(hash
|
||||||
none="admin.wizard.custom_field.serializers.select"
|
none="admin.wizard.custom_field.serializers.select"
|
||||||
onChange=(action (mut field.serializers))}}
|
)}}
|
||||||
</td>
|
</td>
|
||||||
<td class="actions">
|
<td class="actions">
|
||||||
{{#if loading}}
|
{{#if loading}}
|
||||||
|
|
|
@ -14,7 +14,8 @@
|
||||||
<div class="setting-value">
|
<div class="setting-value">
|
||||||
{{wizard-subscription-selector
|
{{wizard-subscription-selector
|
||||||
value=action.type
|
value=action.type
|
||||||
content=actionTypes
|
feature="action"
|
||||||
|
attribute="type"
|
||||||
onChange=(action "changeType")
|
onChange=(action "changeType")
|
||||||
options=(hash
|
options=(hash
|
||||||
none="admin.wizard.select_type"
|
none="admin.wizard.select_type"
|
||||||
|
|
|
@ -222,7 +222,7 @@
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#subscription-container subscribed=subscribed}}
|
{{#wizard-subscription-container}}
|
||||||
<div class="setting full field-mapper-setting">
|
<div class="setting full field-mapper-setting">
|
||||||
<div class="setting-label">
|
<div class="setting-label">
|
||||||
<label>{{i18n "admin.wizard.condition"}}</label>
|
<label>{{i18n "admin.wizard.condition"}}</label>
|
||||||
|
@ -237,7 +237,7 @@
|
||||||
|
|
||||||
<div class="setting full field-mapper-setting">
|
<div class="setting full field-mapper-setting">
|
||||||
<div class="setting-label">
|
<div class="setting-label">
|
||||||
<label>{{i18n "admin.wizard.index"}}</label>>
|
<label>{{i18n "admin.wizard.index"}}</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="setting-value">
|
<div class="setting-value">
|
||||||
|
@ -248,10 +248,9 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{#if isCategory}}
|
{{#if isCategory}}
|
||||||
<div class="setting pro">
|
<div class="setting">
|
||||||
<div class="setting-label">
|
<div class="setting-label">
|
||||||
<label>{{i18n "admin.wizard.field.property"}}</label>
|
<label>{{i18n "admin.wizard.field.property"}}</label>
|
||||||
<span class="pro-label">{{i18n "admin.wizard.pro.label"}}</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="setting-value">
|
<div class="setting-value">
|
||||||
|
@ -269,4 +268,4 @@
|
||||||
{{#if validations}}
|
{{#if validations}}
|
||||||
{{wizard-realtime-validations field=field validations=validations}}
|
{{wizard-realtime-validations field=field validations=validations}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{/subscription-container}}
|
{{/wizard-subscription-container}}
|
||||||
|
|
|
@ -34,7 +34,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{#subscription-container subscribed=subscribed}}
|
{{#wizard-subscription-container}}
|
||||||
<div class="setting full field-mapper-setting">
|
<div class="setting full field-mapper-setting">
|
||||||
<div class="setting-label">
|
<div class="setting-label">
|
||||||
<label>{{i18n "admin.wizard.condition"}}</label>
|
<label>{{i18n "admin.wizard.condition"}}</label>
|
||||||
|
@ -56,11 +56,11 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="setting full field-mapper-setting pro">
|
<div class="setting full field-mapper-setting">
|
||||||
<div class="setting-label">
|
<div class="setting-label">
|
||||||
<label>{{i18n "admin.wizard.step.required_data.label"}}</label>
|
<label>{{i18n "admin.wizard.step.required_data.label"}}</label>
|
||||||
<span class="pro-label">{{i18n "admin.wizard.pro.label"}}</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="setting-value">
|
<div class="setting-value">
|
||||||
{{wizard-mapper
|
{{wizard-mapper
|
||||||
inputs=step.required_data
|
inputs=step.required_data
|
||||||
|
@ -99,7 +99,7 @@
|
||||||
)}}
|
)}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{/subscription-container}}
|
{{/wizard-subscription-container}}
|
||||||
|
|
||||||
{{wizard-links
|
{{wizard-links
|
||||||
itemType="field"
|
itemType="field"
|
||||||
|
|
|
@ -1,30 +0,0 @@
|
||||||
<td class="small">
|
|
||||||
{{#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}}
|
|
||||||
</td>
|
|
||||||
<td>{{notice.typeLabel}}</td>
|
|
||||||
<td class="notice-message">
|
|
||||||
<a role="button" {{action "toggleCookedMessage"}} class="show-notice-message">{{notice.title}}</a>
|
|
||||||
{{#if showCookedMessage}}
|
|
||||||
<span class="cooked-notice-message">{{cookedMessage}}</span>
|
|
||||||
{{/if}}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{{#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}}
|
|
||||||
<span>{{i18n "admin.wizard.notice.dismissed_at"}} {{format-date notice.dismissed_at leaveAgo="true"}}</span>
|
|
||||||
{{else if notice.expired}}
|
|
||||||
<span>{{i18n "admin.wizard.notice.expired_at"}} {{format-date notice.expired_at leaveAgo="true"}}</span>
|
|
||||||
{{else}}
|
|
||||||
<span>{{i18n "admin.wizard.notice.active"}}</span>
|
|
||||||
{{/if}}
|
|
||||||
</td>
|
|
|
@ -1,39 +0,0 @@
|
||||||
<div class="notice-header">
|
|
||||||
<div class="notice-title notice-badge notice-message">
|
|
||||||
<a role="button" {{action "toggleCookedMessage"}} class="show-notice-message">{{notice.title}}</a>
|
|
||||||
{{#if showCookedMessage}}
|
|
||||||
<span class="cooked-notice-message">{{cookedMessage}}</span>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
<div class="notice-header-right">
|
|
||||||
{{#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}}
|
|
||||||
<div class="dismiss-notice-container">
|
|
||||||
{{#if dismissing}}
|
|
||||||
{{loading-spinner size="small"}}
|
|
||||||
{{else}}
|
|
||||||
<a {{action "dismiss"}} role="button" class="dismiss-notice">{{d-icon "times"}}</a>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
{{#if notice.canHide}}
|
|
||||||
<div class="hide-notice-container">
|
|
||||||
{{#if hiding}}
|
|
||||||
{{loading-spinner size="small"}}
|
|
||||||
{{else}}
|
|
||||||
<a {{action "hide"}} role="button" class="hide-notice">{{d-icon "far-eye-slash"}}</a>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
<svg width="300px" height="300px" viewBox="0 0 300 300" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<g id="pavilion-logo" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||||
|
<path id="Combined-Shape" stroke="currentColor" stroke-width="35" d="M41.1381822,291.00006 L40.5778853,130.009744 M258.850727,291.638415 L259.290397,130.37133 M36.0002279,140.721678 L139.995368,36.2122772 M263.350577,141.009083 L138.927245,16.2478517"></path>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
<span>{{label}}</span>
|
Nachher Breite: | Höhe: | Größe: 538 B |
|
@ -1,7 +1,7 @@
|
||||||
<div class="subscription-header">
|
<div class="subscription-header">
|
||||||
<h3>{{i18n "admin.wizard.subscription_container.title"}}</h3>
|
<h4>{{i18n "admin.wizard.subscription_container.title"}}</h4>
|
||||||
|
|
||||||
<a href="/admin/wizards/subscription" title={{i18n subscribedTitle}}>
|
<a href={{subscriptionLink}} title={{i18n subscribedTitle}}>
|
||||||
{{d-icon subscribedIcon}}
|
{{d-icon subscribedIcon}}
|
||||||
{{i18n subscribedLabel}}
|
{{i18n subscribedLabel}}
|
||||||
</a>
|
</a>
|
|
@ -7,8 +7,10 @@
|
||||||
shouldDisplayClearableButton=shouldDisplayClearableButton
|
shouldDisplayClearableButton=shouldDisplayClearableButton
|
||||||
}}
|
}}
|
||||||
|
|
||||||
{{#if selectedContent.subscription}}
|
{{#if selectedContent.subscriptionType}}
|
||||||
<span class="subscription-label">{{i18n "admin.wizard.subscription.label"}}</span>
|
<span class="subscription-label">
|
||||||
|
{{selectedContent.subscriptionType}}
|
||||||
|
</span>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{d-icon caretIcon class="caret-icon"}}
|
{{d-icon caretIcon class="caret-icon"}}
|
||||||
|
|
|
@ -9,9 +9,9 @@
|
||||||
|
|
||||||
<div class="texts">
|
<div class="texts">
|
||||||
<span class="name">{{html-safe label}}</span>
|
<span class="name">{{html-safe label}}</span>
|
||||||
{{#if item.subscription}}
|
{{#if item.subscriptionType}}
|
||||||
<span class="subscription-label">
|
<span class="subscription-label">
|
||||||
{{item.subscription}}
|
{{item.subscriptionType}}
|
||||||
</span>
|
</span>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,31 +0,0 @@
|
||||||
<div class="title-container">
|
|
||||||
<h3 class="subscription-title">{{title}}</h3>
|
|
||||||
|
|
||||||
<div class="buttons">
|
|
||||||
<span>
|
|
||||||
{{#if updating}}
|
|
||||||
{{loading-spinner size="small"}}
|
|
||||||
{{else if updateIcon}}
|
|
||||||
{{d-icon updateIcon}}
|
|
||||||
{{/if}}
|
|
||||||
</span>
|
|
||||||
{{d-button
|
|
||||||
icon="sync"
|
|
||||||
action=(action "update")
|
|
||||||
disabled=updating
|
|
||||||
title="admin.wizard.subscription.subscription.update"
|
|
||||||
label="admin.wizard.subscription.subscription.update"}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{#if subscribed}}
|
|
||||||
<div class="detail-container">
|
|
||||||
<div class="subscription-state {{stateClass}}" title={{stateLabel}}>{{stateLabel}}</div>
|
|
||||||
|
|
||||||
{{#if subscription.updated_at}}
|
|
||||||
<div class="subscription-updated-at" title={{subscription.updated_at}}>
|
|
||||||
{{i18n "admin.wizard.subscription.subscription.last_updated"}} {{format-date subscription.updated_at leaveAgo="true"}}
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
|
@ -1,5 +1,6 @@
|
||||||
// discourse-skip-module
|
// discourse-skip-module
|
||||||
|
|
||||||
|
if (window.location.pathname.indexOf("/w/") > -1 && Ember.testing) {
|
||||||
document.addEventListener("DOMContentLoaded", function () {
|
document.addEventListener("DOMContentLoaded", function () {
|
||||||
document.body.insertAdjacentHTML(
|
document.body.insertAdjacentHTML(
|
||||||
"afterbegin",
|
"afterbegin",
|
||||||
|
@ -15,3 +16,4 @@ Object.keys(requirejs.entries).forEach(function (entry) {
|
||||||
requirejs(entry);
|
requirejs(entry);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
|
@ -25,6 +25,14 @@ $error: #ef1700;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.admin-wizards .admin-actions {
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
.btn-pavilion-support {
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.wizard-message {
|
.wizard-message {
|
||||||
background-color: var(--primary-low);
|
background-color: var(--primary-low);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
@ -156,6 +164,16 @@ $error: #ef1700;
|
||||||
@extend .wizard-settings-group;
|
@extend .wizard-settings-group;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.admin-wizard-container.settings {
|
||||||
|
.wizard-settings {
|
||||||
|
.wizard-subscription-container {
|
||||||
|
[class~="setting"] {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.wizard-custom-field {
|
.wizard-custom-field {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
background-color: var(--primary-very-low);
|
background-color: var(--primary-very-low);
|
||||||
|
@ -802,84 +820,6 @@ $error: #ef1700;
|
||||||
vertical-align: middle;
|
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 {
|
.btn.btn-pavilion-support {
|
||||||
background: var(--pavilion-primary);
|
background: var(--pavilion-primary);
|
||||||
color: var(--pavilion-secondary);
|
color: var(--pavilion-secondary);
|
||||||
|
@ -899,7 +839,7 @@ $error: #ef1700;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.subscription-container {
|
.wizard-subscription-container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 1em;
|
padding: 1em;
|
||||||
background-color: rgba($pavilion_primary, 0.1);
|
background-color: rgba($pavilion_primary, 0.1);
|
||||||
|
@ -907,158 +847,79 @@ $error: #ef1700;
|
||||||
.subscription-header {
|
.subscription-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
margin-bottom: 1em;
|
margin-bottom: 0.25em;
|
||||||
|
|
||||||
h3 {
|
h3 {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: var(--pavilion-primary);
|
color: var(--primary);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&:not(.subscribed) .subscription-settings {
|
&:not(.subscribed) .subscription-settings {
|
||||||
filter: blur(1px);
|
filter: blur(1px);
|
||||||
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.admin-wizards-notices {
|
.wizard-subscription-badge {
|
||||||
.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 {
|
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
min-height: 25px;
|
max-height: 34px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
color: var(--primary) !important;
|
position: relative;
|
||||||
|
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;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: $primary-medium;
|
||||||
}
|
}
|
||||||
|
|
||||||
.admin-actions {
|
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;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
&.disabled {
|
||||||
.wizard-notices-link {
|
background: var(--primary-low);
|
||||||
position: relative;
|
cursor: unset;
|
||||||
margin-right: 10px;
|
|
||||||
|
|
||||||
div > a {
|
|
||||||
@include btn;
|
|
||||||
color: var(--secondary) !important;
|
|
||||||
background-color: var(--primary-medium);
|
|
||||||
|
|
||||||
&.active {
|
|
||||||
background-color: var(--tertiary) !important;
|
|
||||||
color: var(--secondary) !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.active-notice-count {
|
|
||||||
background-color: $danger;
|
|
||||||
color: $secondary;
|
|
||||||
border-radius: 50%;
|
|
||||||
width: 18px;
|
|
||||||
height: 18px;
|
|
||||||
line-height: 18px;
|
|
||||||
text-align: center;
|
|
||||||
position: absolute;
|
|
||||||
top: -8px;
|
|
||||||
right: -8px;
|
|
||||||
font-size: 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 {
|
.subscription-label {
|
||||||
position: relative;
|
margin-left: 0.75em;
|
||||||
|
padding-top: 0.25em;
|
||||||
.cooked-notice-message {
|
color: var(--pavilion-primary);
|
||||||
background-color: var(--secondary);
|
font-size: 0.75em;
|
||||||
padding: 1em;
|
|
||||||
z-index: 1;
|
|
||||||
box-shadow: shadow("dropdown");
|
|
||||||
border-top: 1px solid var(--primary-low);
|
|
||||||
|
|
||||||
p {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,13 @@ $pavilion_primary: #3c1c8c;
|
||||||
$pavilion_secondary: #ffffff;
|
$pavilion_secondary: #ffffff;
|
||||||
$pavilion_warning: rgb(243, 163, 61);
|
$pavilion_warning: rgb(243, 163, 61);
|
||||||
|
|
||||||
|
$subscription_standard: $pavilion_primary;
|
||||||
|
$subscription_business: #333;
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--pavilion-primary: #{$pavilion_primary};
|
--pavilion-primary: #{$pavilion_primary};
|
||||||
--pavilion-secondary: #{$pavilion_secondary};
|
--pavilion-secondary: #{$pavilion_secondary};
|
||||||
--pavilion-warning: #{$pavilion_warning};
|
--pavilion-warning: #{$pavilion_warning};
|
||||||
|
--subscription-standard: #{$subscription_standard};
|
||||||
|
--subscription-business: #{$subscription_business};
|
||||||
}
|
}
|
||||||
|
|
|
@ -474,53 +474,16 @@ en:
|
||||||
not_subscribed:
|
not_subscribed:
|
||||||
label: Not Subscribed
|
label: Not Subscribed
|
||||||
title: Subscribe to use these features
|
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:
|
type:
|
||||||
label: Type
|
none:
|
||||||
info: Information
|
label: Not Subscribed
|
||||||
warning: Warning
|
title: There is no Custom Wizard subscription active on this forum.
|
||||||
connection_error: Connection Error
|
business:
|
||||||
|
label: Business
|
||||||
notices:
|
title: There is a Custom Wizard Business subscription active on this forum.
|
||||||
nav_label: Notices
|
standard:
|
||||||
title: Plugin Notices
|
label: Standard
|
||||||
|
title: There is a Custom Wizard Standard subscription active on this forum.
|
||||||
|
|
||||||
wizard_js:
|
wizard_js:
|
||||||
group:
|
group:
|
||||||
|
|
|
@ -53,20 +53,7 @@ en:
|
||||||
after_signup_after_time: "You can't use 'after time' and 'after signup' on the same wizard."
|
after_signup_after_time: "You can't use 'after time' and 'after signup' on the same wizard."
|
||||||
after_time: "After time setting is invalid."
|
after_time: "After time setting is invalid."
|
||||||
liquid_syntax_error: "Liquid syntax error in %{attribute}: %{message}"
|
liquid_syntax_error: "Liquid syntax error in %{attribute}: %{message}"
|
||||||
|
subscription: "%{type} %{property} is subscription only"
|
||||||
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 <a href="mailto:support@thepavilion.io">support@thepavilion.io</a> for further assistance.
|
|
||||||
subscription_message:
|
|
||||||
connection_error:
|
|
||||||
title: Unable to connect to the Custom Wizard Plugin subscription server
|
|
||||||
message: If this issue persists contact <a href="mailto:support@thepavilion.io">support@thepavilion.io</a> for further assistance.
|
|
||||||
|
|
||||||
site_settings:
|
site_settings:
|
||||||
custom_wizard_enabled: "Enable custom wizards."
|
custom_wizard_enabled: "Enable custom wizards."
|
||||||
|
|
|
@ -45,17 +45,5 @@ Discourse::Application.routes.append do
|
||||||
get 'admin/wizards/manager/export' => 'admin_manager#export'
|
get 'admin/wizards/manager/export' => 'admin_manager#export'
|
||||||
post 'admin/wizards/manager/import' => 'admin_manager#import'
|
post 'admin/wizards/manager/import' => 'admin_manager#import'
|
||||||
delete 'admin/wizards/manager/destroy' => 'admin_manager#destroy'
|
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
|
||||||
end
|
end
|
||||||
|
|
|
@ -84,7 +84,7 @@ class ::CustomWizard::CustomField
|
||||||
next
|
next
|
||||||
end
|
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))
|
add_error(I18n.t("wizard.custom_field.error.subscription_type", type: value))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -99,7 +99,7 @@ class ::CustomWizard::CustomField
|
||||||
add_error(I18n.t("#{i18n_key}.unsupported_type", type: value))
|
add_error(I18n.t("#{i18n_key}.unsupported_type", type: value))
|
||||||
end
|
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))
|
add_error(I18n.t("wizard.custom_field.error.subscription_type", type: value))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -47,7 +47,6 @@ class CustomWizard::Mapper
|
||||||
@data = params[:data] || {}
|
@data = params[:data] || {}
|
||||||
@user = params[:user]
|
@user = params[:user]
|
||||||
@opts = params[:opts] || {}
|
@opts = params[:opts] || {}
|
||||||
@subscription = CustomWizard::Subscription.new
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def perform
|
def perform
|
||||||
|
@ -252,7 +251,7 @@ class CustomWizard::Mapper
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if opts[:template] && @subscription.subscribed?
|
if opts[:template] && CustomWizard::Subscription.subscribed?
|
||||||
template = Liquid::Template.parse(string)
|
template = Liquid::Template.parse(string)
|
||||||
string = template.render(data)
|
string = template.render(data)
|
||||||
end
|
end
|
||||||
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -1,240 +1,170 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class CustomWizard::Subscription
|
class CustomWizard::Subscription
|
||||||
include ActiveModel::Serialization
|
STANDARD_PRODUCT_ID = 'prod_LNAGVAaIqDsHmB'
|
||||||
|
BUSINESS_PRODUCT_ID = 'prod_LNABQ50maBQ1pY'
|
||||||
|
|
||||||
attr_accessor :authentication,
|
def self.attributes
|
||||||
:subscription
|
{
|
||||||
|
wizard: {
|
||||||
SUBSCRIPTION_LEVELS = {
|
required: {
|
||||||
standard: {
|
none: [],
|
||||||
actions: ["send_message", "add_to_group", "watch_categories"],
|
standard: ['*'],
|
||||||
custom_fields: {
|
business: ['*']
|
||||||
klass: [],
|
|
||||||
type: ["json"],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
business: {
|
|
||||||
actions: ["create_category", "create_group", "send_to_api"],
|
|
||||||
custom_fields: {
|
|
||||||
klass: ["group", "category"],
|
|
||||||
type: [],
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
permitted: {
|
||||||
|
none: [],
|
||||||
|
standard: ['*'],
|
||||||
|
business: ['*']
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
step: {
|
||||||
|
condition: {
|
||||||
|
none: [],
|
||||||
|
standard: ['*'],
|
||||||
|
business: ['*']
|
||||||
|
},
|
||||||
|
index: {
|
||||||
|
none: [],
|
||||||
|
standard: ['*'],
|
||||||
|
business: ['*']
|
||||||
|
},
|
||||||
|
required_data: {
|
||||||
|
none: [],
|
||||||
|
standard: ['*'],
|
||||||
|
business: ['*']
|
||||||
|
},
|
||||||
|
permitted_params: {
|
||||||
|
none: [],
|
||||||
|
standard: ['*'],
|
||||||
|
business: ['*']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
field: {
|
||||||
|
condition: {
|
||||||
|
none: [],
|
||||||
|
standard: ['*'],
|
||||||
|
business: ['*']
|
||||||
|
},
|
||||||
|
index: {
|
||||||
|
none: [],
|
||||||
|
standard: ['*'],
|
||||||
|
business: ['*']
|
||||||
|
},
|
||||||
|
type: {
|
||||||
|
none: ['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
|
def initialize
|
||||||
@authentication = CustomWizard::Subscription::Authentication.new(get_authentication)
|
@subscription = find_subscription
|
||||||
@subscription = CustomWizard::Subscription::Subscription.new(get_subscription)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def authorized?
|
def includes?(feature, attribute, value)
|
||||||
@authentication.active?
|
attributes = self.class.attributes[feature]
|
||||||
end
|
|
||||||
|
|
||||||
def subscribed?
|
## Attribute is not part of a subscription
|
||||||
@subscription.active?
|
return true unless attributes.present? && attributes.key?(attribute)
|
||||||
end
|
|
||||||
|
|
||||||
def server
|
values = attributes[attribute][type]
|
||||||
"test.thepavilion.io"
|
|
||||||
end
|
|
||||||
|
|
||||||
def subscription_type
|
## Subscription type does not support the attribute.
|
||||||
"stripe"
|
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
|
end
|
||||||
|
|
||||||
def type
|
def type
|
||||||
@subscription.type
|
return :none unless subscribed?
|
||||||
|
return :standard if standard?
|
||||||
|
return :business if business?
|
||||||
end
|
end
|
||||||
|
|
||||||
def client_name
|
def subscribed?
|
||||||
"custom-wizard"
|
standard? || business?
|
||||||
end
|
end
|
||||||
|
|
||||||
def scope
|
def standard?
|
||||||
"discourse-subscription-server:user_subscription"
|
@subscription.product_id === STANDARD_PRODUCT_ID
|
||||||
end
|
end
|
||||||
|
|
||||||
def requires_additional_subscription(kategory, sub_kategory)
|
def business?
|
||||||
case kategory
|
@subscription.product_id === BUSINESS_PRODUCT_ID
|
||||||
when "actions"
|
|
||||||
case self.type
|
|
||||||
when "business"
|
|
||||||
[]
|
|
||||||
when "standard"
|
|
||||||
SUBSCRIPTION_LEVELS[:business][kategory.to_sym]
|
|
||||||
else
|
|
||||||
SUBSCRIPTION_LEVELS[:standard][kategory.to_sym] + SUBSCRIPTION_LEVELS[:business][kategory.to_sym]
|
|
||||||
end
|
|
||||||
when "custom_fields"
|
|
||||||
case self.type
|
|
||||||
when "business"
|
|
||||||
[]
|
|
||||||
when "standard"
|
|
||||||
SUBSCRIPTION_LEVELS[:business][kategory.to_sym][sub_kategory.to_sym]
|
|
||||||
else
|
|
||||||
SUBSCRIPTION_LEVELS[:standard][kategory.to_sym][sub_kategory.to_sym] + SUBSCRIPTION_LEVELS[:business][kategory.to_sym][sub_kategory.to_sym]
|
|
||||||
end
|
|
||||||
else
|
|
||||||
[]
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def update
|
def client_installed?
|
||||||
if @authentication.active?
|
defined?(SubscriptionClient) == 'constant' && SubscriptionClient.class == Module
|
||||||
response = Excon.get(
|
|
||||||
"https://#{server}/subscription-server/user-subscriptions/#{subscription_type}/#{client_name}",
|
|
||||||
headers: {
|
|
||||||
"User-Api-Key" => @authentication.api_key
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
if response.status == 200
|
|
||||||
begin
|
|
||||||
data = JSON.parse(response.body).deep_symbolize_keys
|
|
||||||
rescue JSON::ParserError
|
|
||||||
return false
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return false unless data && data.is_a?(Hash)
|
def find_subscription
|
||||||
subscriptions = data[:subscriptions]
|
subscription = nil
|
||||||
|
|
||||||
if subscriptions.present? && type = subscriptions.first[:price_nickname]
|
if client_installed?
|
||||||
@subscription = set_subscription(type)
|
subscription = SubscriptionClientSubscription.active
|
||||||
return true
|
.where(product_id: [STANDARD_PRODUCT_ID, BUSINESS_PRODUCT_ID])
|
||||||
end
|
.order("product_id = '#{BUSINESS_PRODUCT_ID}' DESC")
|
||||||
end
|
.first
|
||||||
end
|
end
|
||||||
|
|
||||||
destroy_subscription
|
unless subscription
|
||||||
false
|
subscription = OpenStruct.new(product_id: nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
def destroy_subscription
|
subscription
|
||||||
if remove_subscription
|
|
||||||
@subscription = CustomWizard::Subscription::Subscription.new(get_subscription)
|
|
||||||
!@subscription.active?
|
|
||||||
else
|
|
||||||
false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def authentication_url(user_id, request_id)
|
|
||||||
keys = @authentication.generate_keys(user_id, request_id)
|
|
||||||
params = {
|
|
||||||
public_key: keys.public_key,
|
|
||||||
nonce: keys.nonce,
|
|
||||||
client_id: @authentication.client_id,
|
|
||||||
auth_redirect: "#{Discourse.base_url}/admin/wizards/subscription/authorize/callback",
|
|
||||||
application_name: SiteSetting.title,
|
|
||||||
scopes: scope
|
|
||||||
}
|
|
||||||
|
|
||||||
uri = URI.parse("https://#{server}/user-api-key/new")
|
|
||||||
uri.query = URI.encode_www_form(params)
|
|
||||||
uri.to_s
|
|
||||||
end
|
|
||||||
|
|
||||||
def authentication_response(request_id, payload)
|
|
||||||
data = @authentication.decrypt_payload(request_id, payload)
|
|
||||||
return false unless data.is_a?(Hash) && data[:key] && data[:user_id]
|
|
||||||
|
|
||||||
api_key = data[:key]
|
|
||||||
user_id = data[:user_id]
|
|
||||||
user = User.find(user_id)
|
|
||||||
|
|
||||||
if user&.admin
|
|
||||||
@authentication = set_authentication(api_key, user.id)
|
|
||||||
true
|
|
||||||
else
|
|
||||||
false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def destroy_authentication
|
|
||||||
if remove_authentication
|
|
||||||
@authentication = CustomWizard::Subscription::Authentication.new(get_authentication)
|
|
||||||
!@authentication.active?
|
|
||||||
else
|
|
||||||
false
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.subscribed?
|
def self.subscribed?
|
||||||
self.new.subscribed?
|
new.subscribed?
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.business?
|
||||||
|
new.business?
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.standard?
|
||||||
|
new.standard?
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.type
|
def self.type
|
||||||
self.new.type
|
new.type
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.requires_additional_subscription(kategory, sub_kategory)
|
def self.client_installed?
|
||||||
self.new.requires_additional_subscription(kategory, sub_kategory)
|
new.client_installed?
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.authorized?
|
def self.includes?(feature, attribute, value)
|
||||||
self.new.authorized?
|
new.includes?(feature, attribute, value)
|
||||||
end
|
|
||||||
|
|
||||||
def self.update
|
|
||||||
self.new.update
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.namespace
|
|
||||||
"custom_wizard_subscription"
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def subscription_db_key
|
|
||||||
"subscription"
|
|
||||||
end
|
|
||||||
|
|
||||||
def authentication_db_key
|
|
||||||
"authentication"
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_subscription
|
|
||||||
raw = PluginStore.get(self.class.namespace, subscription_db_key)
|
|
||||||
|
|
||||||
if raw.present?
|
|
||||||
OpenStruct.new(
|
|
||||||
type: raw['type'],
|
|
||||||
updated_at: raw['updated_at']
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def remove_subscription
|
|
||||||
PluginStore.remove(self.class.namespace, subscription_db_key)
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_subscription(type)
|
|
||||||
PluginStore.set(CustomWizard::Subscription.namespace, subscription_db_key, type: type, updated_at: Time.now)
|
|
||||||
CustomWizard::Subscription::Subscription.new(get_subscription)
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_authentication
|
|
||||||
raw = PluginStore.get(self.class.namespace, authentication_db_key)
|
|
||||||
OpenStruct.new(
|
|
||||||
key: raw && raw['key'],
|
|
||||||
auth_by: raw && raw['auth_by'],
|
|
||||||
auth_at: raw && raw['auth_at']
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_authentication(key, user_id)
|
|
||||||
PluginStore.set(self.class.namespace, authentication_db_key,
|
|
||||||
key: key,
|
|
||||||
auth_by: user_id,
|
|
||||||
auth_at: Time.now
|
|
||||||
)
|
|
||||||
CustomWizard::Subscription::Authentication.new(get_authentication)
|
|
||||||
end
|
|
||||||
|
|
||||||
def remove_authentication
|
|
||||||
PluginStore.remove(self.class.namespace, authentication_db_key)
|
|
||||||
get_authentication
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -54,34 +54,6 @@ class CustomWizard::TemplateValidator
|
||||||
}
|
}
|
||||||
end
|
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
|
private
|
||||||
|
|
||||||
def check_required(object, type)
|
def check_required(object, type)
|
||||||
|
@ -93,16 +65,10 @@ class CustomWizard::TemplateValidator
|
||||||
end
|
end
|
||||||
|
|
||||||
def validate_subscription(object, type)
|
def validate_subscription(object, type)
|
||||||
self.class.subscription[type].each do |property, subscription_type|
|
object.keys.each do |property|
|
||||||
val = object[property.to_s]
|
value = object[property]
|
||||||
is_subscription = (val != nil) && (
|
|
||||||
subscription_type === 'present' && val.present? ||
|
|
||||||
(['true', 'false'].include?(subscription_type) && cast_bool(val) == cast_bool(subscription_type)) ||
|
|
||||||
(subscription_type === 'conditional' && val.is_a?(Hash)) ||
|
|
||||||
(subscription_type.is_a?(Array) && subscription_type.include?(val))
|
|
||||||
)
|
|
||||||
|
|
||||||
if is_subscription && !@subscription.subscribed?
|
if !@subscription.includes?(type, property.to_sym, value)
|
||||||
errors.add :base, I18n.t("wizard.validation.subscription", type: type.to_s, property: property)
|
errors.add :base, I18n.t("wizard.validation.subscription", type: type.to_s, property: property)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -148,10 +114,6 @@ class CustomWizard::TemplateValidator
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def cast_bool(val)
|
|
||||||
ActiveRecord::Type::Boolean.new.cast(val)
|
|
||||||
end
|
|
||||||
|
|
||||||
def validate_liquid_template(object, type)
|
def validate_liquid_template(object, type)
|
||||||
%w[
|
%w[
|
||||||
description
|
description
|
||||||
|
|
46
plugin.rb
46
plugin.rb
|
@ -5,17 +5,13 @@
|
||||||
# authors: Angus McLeod, Faizaan Gagan, Robert Barrow, Keegan George
|
# authors: Angus McLeod, Faizaan Gagan, Robert Barrow, Keegan George
|
||||||
# contact_emails: support@thepavilion.io
|
# contact_emails: support@thepavilion.io
|
||||||
# url: https://github.com/paviliondev/discourse-custom-wizard
|
# url: https://github.com/paviliondev/discourse-custom-wizard
|
||||||
|
# subscription_url: https://coop.pavilion.tech
|
||||||
|
|
||||||
gem 'liquid', '5.0.1', require: true
|
gem 'liquid', '5.0.1', require: true
|
||||||
register_asset 'stylesheets/admin/admin.scss', :desktop
|
register_asset 'stylesheets/admin/admin.scss', :desktop
|
||||||
|
|
||||||
enabled_site_setting :custom_wizard_enabled
|
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?
|
if Rails.env.production?
|
||||||
config.assets.precompile += %w{
|
config.assets.precompile += %w{
|
||||||
wizard-custom-guest.js
|
wizard-custom-guest.js
|
||||||
|
@ -59,6 +55,24 @@ class ::Sprockets::DirectiveProcessor
|
||||||
end
|
end
|
||||||
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
|
after_initialize do
|
||||||
%w[
|
%w[
|
||||||
../lib/custom_wizard/engine.rb
|
../lib/custom_wizard/engine.rb
|
||||||
|
@ -70,15 +84,11 @@ after_initialize do
|
||||||
../app/controllers/custom_wizard/admin/logs.rb
|
../app/controllers/custom_wizard/admin/logs.rb
|
||||||
../app/controllers/custom_wizard/admin/manager.rb
|
../app/controllers/custom_wizard/admin/manager.rb
|
||||||
../app/controllers/custom_wizard/admin/custom_fields.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/wizard.rb
|
||||||
../app/controllers/custom_wizard/steps.rb
|
../app/controllers/custom_wizard/steps.rb
|
||||||
../app/controllers/custom_wizard/realtime_validations.rb
|
../app/controllers/custom_wizard/realtime_validations.rb
|
||||||
../app/jobs/regular/refresh_api_access_token.rb
|
../app/jobs/regular/refresh_api_access_token.rb
|
||||||
../app/jobs/regular/set_after_time_wizard.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/template.rb
|
||||||
../lib/custom_wizard/validators/update.rb
|
../lib/custom_wizard/validators/update.rb
|
||||||
../lib/custom_wizard/action_result.rb
|
../lib/custom_wizard/action_result.rb
|
||||||
|
@ -95,13 +105,9 @@ after_initialize do
|
||||||
../lib/custom_wizard/step_updater.rb
|
../lib/custom_wizard/step_updater.rb
|
||||||
../lib/custom_wizard/step.rb
|
../lib/custom_wizard/step.rb
|
||||||
../lib/custom_wizard/submission.rb
|
../lib/custom_wizard/submission.rb
|
||||||
|
../lib/custom_wizard/subscription.rb
|
||||||
../lib/custom_wizard/template.rb
|
../lib/custom_wizard/template.rb
|
||||||
../lib/custom_wizard/wizard.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/api.rb
|
||||||
../lib/custom_wizard/api/authorization.rb
|
../lib/custom_wizard/api/authorization.rb
|
||||||
../lib/custom_wizard/api/endpoint.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/log_serializer.rb
|
||||||
../app/serializers/custom_wizard/submission_serializer.rb
|
../app/serializers/custom_wizard/submission_serializer.rb
|
||||||
../app/serializers/custom_wizard/realtime_validation/similar_topics_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/extra_locales_controller.rb
|
||||||
../lib/custom_wizard/extensions/invites_controller.rb
|
../lib/custom_wizard/extensions/invites_controller.rb
|
||||||
../lib/custom_wizard/extensions/users_controller.rb
|
../lib/custom_wizard/extensions/users_controller.rb
|
||||||
|
@ -271,14 +273,6 @@ after_initialize do
|
||||||
"#{serializer_klass}_serializer".classify.constantize.prepend CustomWizardCustomFieldSerializer
|
"#{serializer_klass}_serializer".classify.constantize.prepend CustomWizardCustomFieldSerializer
|
||||||
end
|
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|
|
reloadable_patch do |plugin|
|
||||||
::TagsController.prepend CustomWizardTagsController
|
::TagsController.prepend CustomWizardTagsController
|
||||||
::DiscourseTagging.singleton_class.prepend CustomWizardDiscourseTagging
|
::DiscourseTagging.singleton_class.prepend CustomWizardDiscourseTagging
|
||||||
|
|
|
@ -8,6 +8,7 @@ describe CustomWizard::Action do
|
||||||
let(:wizard_template) { get_wizard_fixture("wizard") }
|
let(:wizard_template) { get_wizard_fixture("wizard") }
|
||||||
let(:open_composer) { get_wizard_fixture("actions/open_composer") }
|
let(:open_composer) { get_wizard_fixture("actions/open_composer") }
|
||||||
let(:create_category) { get_wizard_fixture("actions/create_category") }
|
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(:create_group) { get_wizard_fixture("actions/create_group") }
|
||||||
let(:add_to_group) { get_wizard_fixture("actions/add_to_group") }
|
let(:add_to_group) { get_wizard_fixture("actions/add_to_group") }
|
||||||
let(:send_message) { get_wizard_fixture("actions/send_message") }
|
let(:send_message) { get_wizard_fixture("actions/send_message") }
|
||||||
|
@ -158,17 +159,6 @@ describe CustomWizard::Action do
|
||||||
end
|
end
|
||||||
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
|
it 're-routes a user' do
|
||||||
wizard = CustomWizard::Builder.new(@template[:id], user).build
|
wizard = CustomWizard::Builder.new(@template[:id], user).build
|
||||||
updater = wizard.create_updater(wizard.steps.last.id, {})
|
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")
|
expect(updater.result[:redirect_on_next]).to eq("https://google.com")
|
||||||
end
|
end
|
||||||
|
|
||||||
context "subscription actions" do
|
context "standard subscription actions" do
|
||||||
before do
|
before do
|
||||||
enable_subscription("standard")
|
enable_subscription("standard")
|
||||||
end
|
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
|
it '#send_message' do
|
||||||
wizard_template['actions'] << send_message
|
wizard_template['actions'] << send_message
|
||||||
update_template(wizard_template)
|
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(topic.first.allowed_groups.map(&:name)).to include('cool_group', 'cool_group_1')
|
||||||
expect(post.exists?).to eq(true)
|
expect(post.exists?).to eq(true)
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "business subscription actions" do
|
||||||
|
before do
|
||||||
|
enable_subscription("business")
|
||||||
|
end
|
||||||
|
|
||||||
it '#create_category' do
|
it '#create_category' do
|
||||||
wizard_template['actions'] << create_category
|
wizard_template['actions'] << create_category
|
||||||
|
wizard_template['actions'] << create_group
|
||||||
update_template(wizard_template)
|
update_template(wizard_template)
|
||||||
|
|
||||||
wizard = CustomWizard::Builder.new(@template[:id], user).build
|
wizard = CustomWizard::Builder.new(@template[:id], user).build
|
||||||
|
|
|
@ -100,6 +100,7 @@ describe CustomWizard::Builder do
|
||||||
|
|
||||||
context "with restricted permissions" do
|
context "with restricted permissions" do
|
||||||
before do
|
before do
|
||||||
|
enable_subscription("standard")
|
||||||
@template[:permitted] = permitted_json["permitted"]
|
@template[:permitted] = permitted_json["permitted"]
|
||||||
CustomWizard::Template.save(@template.as_json)
|
CustomWizard::Template.save(@template.as_json)
|
||||||
end
|
end
|
||||||
|
@ -304,7 +305,7 @@ describe CustomWizard::Builder do
|
||||||
before do
|
before do
|
||||||
enable_subscription("standard")
|
enable_subscription("standard")
|
||||||
@template[:steps][0][:fields][0][:condition] = user_condition_json['condition']
|
@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)
|
CustomWizard::Template.save(@template.as_json)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -325,7 +326,7 @@ describe CustomWizard::Builder do
|
||||||
|
|
||||||
builder = CustomWizard::Builder.new(@template[:id], user)
|
builder = CustomWizard::Builder.new(@template[:id], user)
|
||||||
wizard = builder.build
|
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
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -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
|
|
|
@ -1,125 +1,104 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require_relative '../../plugin_helper'
|
|
||||||
|
|
||||||
describe CustomWizard::Subscription do
|
describe CustomWizard::Subscription do
|
||||||
fab!(:user) { Fabricate(:user) }
|
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 "initializes subscription authentication and subscription" do
|
def define_client_classes
|
||||||
|
load File.expand_path("#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/subscription_client.rb", __FILE__)
|
||||||
|
end
|
||||||
|
|
||||||
|
def stub_client_methods
|
||||||
|
[:active, :where, :order, :first].each do |method|
|
||||||
|
SubscriptionClientSubscription.stubs(method)
|
||||||
|
.returns(SubscriptionClientSubscription)
|
||||||
|
end
|
||||||
|
SubscriptionClientSubscription.stubs(:product_id).returns(SecureRandom.hex(8))
|
||||||
|
end
|
||||||
|
|
||||||
|
after do
|
||||||
|
undefine_client_classes
|
||||||
|
end
|
||||||
|
|
||||||
|
it "detects the subscription client" do
|
||||||
|
expect(described_class.client_installed?).to eq(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
context "without a subscription client" do
|
||||||
|
it "is not subscribed" do
|
||||||
|
expect(described_class.subscribed?).to eq(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "has none type" do
|
||||||
subscription = described_class.new
|
subscription = described_class.new
|
||||||
expect(subscription.authentication.class).to eq(CustomWizard::Subscription::Authentication)
|
expect(subscription.type).to eq(:none)
|
||||||
expect(subscription.subscription.class).to eq(CustomWizard::Subscription::Subscription)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it "returns authorized and subscribed states" do
|
it "non subscriber features are included" do
|
||||||
subscription = described_class.new
|
expect(described_class.includes?(:wizard, :after_signup, true)).to eq(true)
|
||||||
expect(subscription.authorized?).to eq(false)
|
|
||||||
expect(subscription.subscribed?).to eq(false)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context "subscription" do
|
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
|
before do
|
||||||
@subscription = described_class.new
|
define_client_classes
|
||||||
|
stub_client_methods
|
||||||
end
|
end
|
||||||
|
|
||||||
it "updates valid subscriptions" do
|
it "detects the subscription client" do
|
||||||
stub_subscription_request(200, valid_subscription)
|
expect(described_class.client_installed?).to eq(true)
|
||||||
expect(@subscription.update).to eq(true)
|
|
||||||
expect(@subscription.subscribed?).to eq(true)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it "handles invalid subscriptions" do
|
context "without a subscription" do
|
||||||
stub_subscription_request(200, invalid_subscription)
|
it "has none type" do
|
||||||
expect(@subscription.update).to eq(false)
|
expect(described_class.type).to eq(:none)
|
||||||
expect(@subscription.subscribed?).to eq(false)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it "handles subscription http errors" do
|
it "non subscriber features are included" do
|
||||||
stub_subscription_request(404, {})
|
expect(described_class.includes?(:wizard, :after_signup, true)).to eq(true)
|
||||||
expect(@subscription.update).to eq(false)
|
|
||||||
expect(@subscription.subscribed?).to eq(false)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it "destroys subscriptions" do
|
it "subscriber features are not included" do
|
||||||
stub_subscription_request(200, valid_subscription)
|
expect(described_class.includes?(:wizard, :permitted, {})).to eq(false)
|
||||||
expect(@subscription.update).to eq(true)
|
|
||||||
expect(@subscription.destroy_subscription).to eq(true)
|
|
||||||
expect(@subscription.subscribed?).to eq(false)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "has class aliases" do
|
|
||||||
authenticate_subscription
|
|
||||||
stub_subscription_request(200, valid_subscription)
|
|
||||||
expect(described_class.update).to eq(true)
|
|
||||||
expect(described_class.subscribed?).to eq(true)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context "authentication" do
|
context "with standard subscription" do
|
||||||
before do
|
before do
|
||||||
@subscription = described_class.new
|
SubscriptionClientSubscription.stubs(:product_id).returns(CustomWizard::Subscription::STANDARD_PRODUCT_ID)
|
||||||
user.update!(admin: true)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it "generates a valid authentication request url" do
|
it "detects standard type" do
|
||||||
request_id = SecureRandom.hex(32)
|
expect(described_class.type).to eq(:standard)
|
||||||
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
|
end
|
||||||
|
|
||||||
def generate_payload(request_id, user_id)
|
it "standard features are included" do
|
||||||
uri = URI(@subscription.authentication_url(user_id, request_id))
|
expect(described_class.includes?(:wizard, :type, 'send_message')).to eq(true)
|
||||||
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
|
end
|
||||||
|
|
||||||
it "handles authentication response if request and response is valid" do
|
it "business features are not included" do
|
||||||
request_id = SecureRandom.hex(32)
|
expect(described_class.includes?(:action, :type, 'create_category')).to eq(false)
|
||||||
payload = generate_payload(request_id, user.id)
|
end
|
||||||
|
|
||||||
expect(@subscription.authentication_response(request_id, payload)).to eq(true)
|
|
||||||
expect(@subscription.authorized?).to eq(true)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it "discards authentication response if user who made request as not an admin" do
|
context "with business subscription" do
|
||||||
user.update!(admin: false)
|
before do
|
||||||
|
SubscriptionClientSubscription.stubs(:product_id).returns(CustomWizard::Subscription::BUSINESS_PRODUCT_ID)
|
||||||
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
|
end
|
||||||
|
|
||||||
it "discards authentication response if request_id is invalid" do
|
it "detects business type" do
|
||||||
payload = generate_payload(SecureRandom.hex(32), user.id)
|
expect(described_class.type).to eq(:business)
|
||||||
|
|
||||||
expect(@subscription.authentication_response(SecureRandom.hex(32), payload)).to eq(false)
|
|
||||||
expect(@subscription.authorized?).to eq(false)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it "destroys authentication" do
|
it "business are included" do
|
||||||
request_id = SecureRandom.hex(32)
|
expect(described_class.includes?(:action, :type, 'create_category')).to eq(true)
|
||||||
payload = generate_payload(request_id, user.id)
|
end
|
||||||
@subscription.authentication_response(request_id, payload)
|
|
||||||
|
|
||||||
expect(@subscription.destroy_authentication).to eq(true)
|
|
||||||
expect(@subscription.authorized?).to eq(false)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -47,6 +47,8 @@ describe CustomWizard::Template do
|
||||||
|
|
||||||
context "wizard template list" do
|
context "wizard template list" do
|
||||||
before do
|
before do
|
||||||
|
enable_subscription('standard')
|
||||||
|
|
||||||
template_json_2 = template_json.dup
|
template_json_2 = template_json.dup
|
||||||
template_json_2["id"] = 'super_mega_fun_wizard_2'
|
template_json_2["id"] = 'super_mega_fun_wizard_2'
|
||||||
template_json_2["permitted"] = permitted_json['permitted']
|
template_json_2["permitted"] = permitted_json['permitted']
|
||||||
|
|
|
@ -5,6 +5,8 @@ describe CustomWizard::TemplateValidator do
|
||||||
let(:template) { get_wizard_fixture("wizard") }
|
let(:template) { get_wizard_fixture("wizard") }
|
||||||
let(:create_category) { get_wizard_fixture("actions/create_category") }
|
let(:create_category) { get_wizard_fixture("actions/create_category") }
|
||||||
let(:user_condition) { get_wizard_fixture("condition/user_condition") }
|
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) {
|
let(:valid_liquid_template) {
|
||||||
<<-LIQUID.strip
|
<<-LIQUID.strip
|
||||||
|
@ -104,7 +106,7 @@ describe CustomWizard::TemplateValidator do
|
||||||
|
|
||||||
context "without subscription" do
|
context "without subscription" do
|
||||||
it "invalidates subscription wizard attributes" do
|
it "invalidates subscription wizard attributes" do
|
||||||
template[:save_submissions] = false
|
template[:permitted] = permitted_json['permitted']
|
||||||
expect(
|
expect(
|
||||||
CustomWizard::TemplateValidator.new(template).perform
|
CustomWizard::TemplateValidator.new(template).perform
|
||||||
).to eq(false)
|
).to eq(false)
|
||||||
|
@ -134,11 +136,11 @@ describe CustomWizard::TemplateValidator do
|
||||||
|
|
||||||
context "with subscription" do
|
context "with subscription" do
|
||||||
before do
|
before do
|
||||||
enable_subscription("standard")
|
enable_subscription("business")
|
||||||
end
|
end
|
||||||
|
|
||||||
it "validates wizard attributes" do
|
it "validates wizard attributes" do
|
||||||
template[:save_submissions] = false
|
template[:permitted] = permitted_json['permitted']
|
||||||
expect(
|
expect(
|
||||||
CustomWizard::TemplateValidator.new(template).perform
|
CustomWizard::TemplateValidator.new(template).perform
|
||||||
).to eq(true)
|
).to eq(true)
|
||||||
|
@ -227,7 +229,9 @@ describe CustomWizard::TemplateValidator do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "validates preview templates" do
|
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)
|
expect_validation_failure("step_1_field_5.preview_template", liquid_syntax_error)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
describe CustomWizard::UpdateValidator do
|
describe CustomWizard::UpdateValidator do
|
||||||
fab!(:user) { Fabricate(:user) }
|
fab!(:user) { Fabricate(:user) }
|
||||||
let(:template) { get_wizard_fixture("wizard") }
|
let(:template) { get_wizard_fixture("wizard") }
|
||||||
|
let(:url_field) { get_wizard_fixture("field/url") }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
CustomWizard::Template.save(template, skip_jobs: true)
|
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][0][:min_length] = min_length
|
||||||
@template[:steps][0][:fields][1][: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)
|
CustomWizard::Template.save(@template)
|
||||||
|
|
||||||
|
@ -34,11 +34,6 @@ describe CustomWizard::UpdateValidator do
|
||||||
expect(
|
expect(
|
||||||
updater.errors.messages[:step_1_field_2].first
|
updater.errors.messages[:step_1_field_2].first
|
||||||
).to eq(I18n.t('wizard.field.too_short', label: 'Textarea', min: min_length))
|
).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
|
end
|
||||||
|
|
||||||
it 'prevents submission if the length is over the max length' do
|
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][0][:max_length] = max_length
|
||||||
@template[:steps][0][:fields][1][: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)
|
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. "
|
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(
|
expect(
|
||||||
updater.errors.messages[:step_1_field_2].first
|
updater.errors.messages[:step_1_field_2].first
|
||||||
).to eq(I18n.t('wizard.field.too_long', label: 'Textarea', max: max_length))
|
).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
|
end
|
||||||
|
|
||||||
it "allows submission if the length is under or equal to the max length" do
|
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][0][:max_length] = max_length
|
||||||
@template[:steps][0][:fields][1][: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)
|
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."
|
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(
|
expect(
|
||||||
updater.errors.messages[:step_1_field_2].first
|
updater.errors.messages[:step_1_field_2].first
|
||||||
).to eq(nil)
|
).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
|
end
|
||||||
|
|
||||||
it "applies min length only if the input is non-empty" do
|
it "applies min length only if the input is non-empty" do
|
||||||
|
@ -131,6 +114,11 @@ describe CustomWizard::UpdateValidator do
|
||||||
).to eq(I18n.t('wizard.field.required', label: 'Textarea'))
|
).to eq(I18n.t('wizard.field.required', label: 'Textarea'))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context "subscription fields" do
|
||||||
|
before do
|
||||||
|
enable_subscription("standard")
|
||||||
|
end
|
||||||
|
|
||||||
it 'validates url fields' do
|
it 'validates url fields' do
|
||||||
updater = perform_validation('step_2', step_2_field_6: 'https://discourse.com')
|
updater = perform_validation('step_2', step_2_field_6: 'https://discourse.com')
|
||||||
expect(
|
expect(
|
||||||
|
@ -139,7 +127,9 @@ describe CustomWizard::UpdateValidator do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'does not validate url fields with non-url inputs' do
|
it 'does not validate url fields with non-url inputs' do
|
||||||
updater = perform_validation('step_2', step_2_field_6: 'discourse')
|
template[:steps][0][:fields] << url_field
|
||||||
|
CustomWizard::Template.save(template)
|
||||||
|
updater = perform_validation('step_1', step_2_field_6: 'discourse')
|
||||||
expect(
|
expect(
|
||||||
updater.errors.messages[:step_2_field_6].first
|
updater.errors.messages[:step_2_field_6].first
|
||||||
).to eq(I18n.t('wizard.field.not_url', label: 'Url'))
|
).to eq(I18n.t('wizard.field.not_url', label: 'Url'))
|
||||||
|
@ -151,6 +141,7 @@ describe CustomWizard::UpdateValidator do
|
||||||
updater.errors.messages[:step_2_field_6].first
|
updater.errors.messages[:step_2_field_6].first
|
||||||
).to eq(nil)
|
).to eq(nil)
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
it 'validates date fields' do
|
it 'validates date fields' do
|
||||||
@template[:steps][1][:fields][0][:format] = "DD-MM-YYYY"
|
@template[:steps][1][:fields][0][:format] = "DD-MM-YYYY"
|
||||||
|
|
|
@ -113,6 +113,11 @@ describe CustomWizard::Wizard do
|
||||||
expect(wizard.completed?).to eq(false)
|
expect(wizard.completed?).to eq(false)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context "with subscription" do
|
||||||
|
before do
|
||||||
|
enable_subscription("standard")
|
||||||
|
end
|
||||||
|
|
||||||
it "permits admins" do
|
it "permits admins" do
|
||||||
expect(
|
expect(
|
||||||
CustomWizard::Wizard.new(@permitted_template, admin_user).permitted?
|
CustomWizard::Wizard.new(@permitted_template, admin_user).permitted?
|
||||||
|
@ -178,6 +183,23 @@ describe CustomWizard::Wizard do
|
||||||
).to eq(false)
|
).to eq(false)
|
||||||
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
|
||||||
|
|
||||||
it "lists the site groups" do
|
it "lists the site groups" do
|
||||||
expect(@wizard.groups.length).to eq(8)
|
expect(@wizard.groups.length).to eq(8)
|
||||||
end
|
end
|
||||||
|
@ -203,6 +225,7 @@ describe CustomWizard::Wizard do
|
||||||
|
|
||||||
context "class methods" do
|
context "class methods" do
|
||||||
before do
|
before do
|
||||||
|
enable_subscription("standard")
|
||||||
CustomWizard::Template.save(@permitted_template, skip_jobs: true)
|
CustomWizard::Template.save(@permitted_template, skip_jobs: true)
|
||||||
|
|
||||||
template_json_2 = template_json.dup
|
template_json_2 = template_json.dup
|
||||||
|
@ -238,20 +261,4 @@ describe CustomWizard::Wizard do
|
||||||
expect(CustomWizard::Wizard.prompt_completion(user).length).to eq(1)
|
expect(CustomWizard::Wizard.prompt_completion(user).length).to eq(1)
|
||||||
end
|
end
|
||||||
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
|
end
|
||||||
|
|
23
spec/fixtures/actions/watch_categories.json
gevendort
Normale Datei
23
spec/fixtures/actions/watch_categories.json
gevendort
Normale Datei
|
@ -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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
27
spec/fixtures/field/advanced_types.json
gevendort
Normale Datei
27
spec/fixtures/field/advanced_types.json
gevendort
Normale Datei
|
@ -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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
7
spec/fixtures/field/composer_preview.json
gevendort
Normale Datei
7
spec/fixtures/field/composer_preview.json
gevendort
Normale Datei
|
@ -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}"
|
||||||
|
}
|
5
spec/fixtures/field/url.json
gevendort
Normale Datei
5
spec/fixtures/field/url.json
gevendort
Normale Datei
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"id": "step_2_field_6",
|
||||||
|
"label": "Url",
|
||||||
|
"type": "url"
|
||||||
|
}
|
4
spec/fixtures/subscription_client.rb
gevendort
Normale Datei
4
spec/fixtures/subscription_client.rb
gevendort
Normale Datei
|
@ -0,0 +1,4 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module SubscriptionClient; end
|
||||||
|
class SubscriptionClientSubscription; end
|
69
spec/fixtures/wizard.json
gevendort
69
spec/fixtures/wizard.json
gevendort
|
@ -33,23 +33,11 @@
|
||||||
"type": "textarea",
|
"type": "textarea",
|
||||||
"min_length": ""
|
"min_length": ""
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"id": "step_1_field_3",
|
|
||||||
"label": "Composer",
|
|
||||||
"type": "composer"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"id": "step_1_field_4",
|
"id": "step_1_field_4",
|
||||||
"label": "I'm only text",
|
"label": "I'm only text",
|
||||||
"description": "",
|
"description": "",
|
||||||
"type": "text_only"
|
"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!"
|
"description": "Text inputs!"
|
||||||
|
@ -87,11 +75,6 @@
|
||||||
"label": "Checkbox",
|
"label": "Checkbox",
|
||||||
"type": "checkbox"
|
"type": "checkbox"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"id": "step_2_field_6",
|
|
||||||
"label": "Url",
|
|
||||||
"type": "url"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"id": "step_2_field_7",
|
"id": "step_2_field_7",
|
||||||
"label": "Upload",
|
"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: "
|
"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",
|
"id": "action_4",
|
||||||
"run_after": "step_2",
|
"run_after": "step_2",
|
||||||
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -8,37 +8,8 @@ def get_wizard_fixture(path)
|
||||||
).with_indifferent_access
|
).with_indifferent_access
|
||||||
end
|
end
|
||||||
|
|
||||||
def authenticate_subscription
|
|
||||||
CustomWizard::Subscription::Authentication.any_instance.stubs(:active?).returns(true)
|
|
||||||
end
|
|
||||||
|
|
||||||
def enable_subscription(type)
|
def enable_subscription(type)
|
||||||
# CustomWizard::Subscription.new
|
CustomWizard::Subscription.stubs(:client_installed?).returns(true)
|
||||||
CustomWizard::Subscription.any_instance.stubs(:subscribed?).returns(true)
|
CustomWizard::Subscription.stubs("#{type}?".to_sym).returns(true)
|
||||||
CustomWizard::Subscription.any_instance.stubs(:type).returns(type)
|
CustomWizard::Subscription.any_instance.stubs("#{type}?".to_sym).returns(true)
|
||||||
end
|
|
||||||
|
|
||||||
def disable_subscription
|
|
||||||
CustomWizard::Subscription.any_instance.stubs(:subscribed?).returns(false)
|
|
||||||
end
|
|
||||||
|
|
||||||
def valid_subscription
|
|
||||||
{
|
|
||||||
product_id: "prod_CBTNpi3fqWWkq0",
|
|
||||||
price_id: "price_id",
|
|
||||||
price_nickname: "business"
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
def invalid_subscription
|
|
||||||
{
|
|
||||||
product_id: "prod_CBTNpi3fqWWkq0",
|
|
||||||
price_id: "price_id"
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
def stub_subscription_request(status, subscription)
|
|
||||||
authenticate_subscription
|
|
||||||
sub = CustomWizard::Subscription.new
|
|
||||||
stub_request(:get, "https://#{sub.server}/subscription-server/user-subscriptions/#{sub.subscription_type}/#{sub.client_name}").to_return(status: status, body: { subscriptions: [subscription] }.to_json)
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -8,6 +8,7 @@ describe CustomWizard::AdminWizardController do
|
||||||
|
|
||||||
before do
|
before do
|
||||||
CustomWizard::Template.save(template, skip_jobs: true)
|
CustomWizard::Template.save(template, skip_jobs: true)
|
||||||
|
enable_subscription("standard")
|
||||||
|
|
||||||
template_2 = template.dup
|
template_2 = template.dup
|
||||||
template_2["id"] = 'super_mega_fun_wizard_2'
|
template_2["id"] = 'super_mega_fun_wizard_2'
|
||||||
|
|
|
@ -34,6 +34,7 @@ describe CustomWizard::StepsController do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "when the user cant access the wizard" do
|
it "when the user cant access the wizard" do
|
||||||
|
enable_subscription("standard")
|
||||||
new_template = wizard_template.dup
|
new_template = wizard_template.dup
|
||||||
new_template["permitted"] = permitted_json["permitted"]
|
new_template["permitted"] = permitted_json["permitted"]
|
||||||
CustomWizard::Template.save(new_template, skip_jobs: true)
|
CustomWizard::Template.save(new_template, skip_jobs: true)
|
||||||
|
|
|
@ -40,14 +40,15 @@ describe CustomWizard::WizardController do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'lets user skip if user cant access wizard' do
|
it 'lets user skip if user cant access wizard' do
|
||||||
|
enable_subscription("standard")
|
||||||
@template["permitted"] = permitted_json["permitted"]
|
@template["permitted"] = permitted_json["permitted"]
|
||||||
CustomWizard::Template.save(@template, skip_jobs: true)
|
CustomWizard::Template.save(@template, skip_jobs: true)
|
||||||
|
|
||||||
put '/w/super-mega-fun-wizard/skip.json'
|
put '/w/super-mega-fun-wizard/skip.json'
|
||||||
expect(response.status).to eq(200)
|
expect(response.status).to eq(200)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns a no skip message if user is not allowed to skip' do
|
it 'returns a no skip message if user is not allowed to skip' do
|
||||||
|
enable_subscription("standard")
|
||||||
@template['required'] = 'true'
|
@template['required'] = 'true'
|
||||||
CustomWizard::Template.save(@template)
|
CustomWizard::Template.save(@template)
|
||||||
put '/w/super-mega-fun-wizard/skip.json'
|
put '/w/super-mega-fun-wizard/skip.json'
|
||||||
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -19,7 +19,7 @@ describe CustomWizard::FieldSerializer do
|
||||||
expect(json_array.size).to eq(@wizard.steps.first.fields.size)
|
expect(json_array.size).to eq(@wizard.steps.first.fields.size)
|
||||||
expect(json_array[0][:label]).to eq("<p>Text</p>")
|
expect(json_array[0][:label]).to eq("<p>Text</p>")
|
||||||
expect(json_array[0][:description]).to eq("Text field description.")
|
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
|
end
|
||||||
|
|
||||||
it "should return optional field attributes" do
|
it "should return optional field attributes" do
|
||||||
|
@ -29,6 +29,6 @@ describe CustomWizard::FieldSerializer do
|
||||||
scope: Guardian.new(user)
|
scope: Guardian.new(user)
|
||||||
).as_json
|
).as_json
|
||||||
expect(json_array[0][:format]).to eq("YYYY-MM-DD")
|
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
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,6 +5,7 @@ describe CustomWizard::WizardSerializer do
|
||||||
fab!(:category) { Fabricate(:category) }
|
fab!(:category) { Fabricate(:category) }
|
||||||
let(:template) { get_wizard_fixture("wizard") }
|
let(:template) { get_wizard_fixture("wizard") }
|
||||||
let(:similar_topics_validation) { get_wizard_fixture("field/validation/similar_topics") }
|
let(:similar_topics_validation) { get_wizard_fixture("field/validation/similar_topics") }
|
||||||
|
let(:advanced_fields) { get_wizard_fixture("field/advanced_types") }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
CustomWizard::Template.save(template, skip_jobs: true)
|
CustomWizard::Template.save(template, skip_jobs: true)
|
||||||
|
@ -52,6 +53,13 @@ describe CustomWizard::WizardSerializer do
|
||||||
expect(json[:wizard][:uncategorized_category_id].present?).to eq(false)
|
expect(json[:wizard][:uncategorized_category_id].present?).to eq(false)
|
||||||
end
|
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 category selector field" do
|
it "should return categories if there is a category selector field" do
|
||||||
json = CustomWizard::WizardSerializer.new(
|
json = CustomWizard::WizardSerializer.new(
|
||||||
CustomWizard::Builder.new(@template[:id], user).build,
|
CustomWizard::Builder.new(@template[:id], user).build,
|
||||||
|
@ -82,7 +90,7 @@ describe CustomWizard::WizardSerializer do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should not return groups if there is not a group selector field' do
|
it 'should not return groups if there is not a group selector field' do
|
||||||
@template[:steps][2][:fields].delete_at(3)
|
@template[:steps][0][:fields].reject! { |f| f["type"] === "group" }
|
||||||
CustomWizard::Template.save(@template)
|
CustomWizard::Template.save(@template)
|
||||||
|
|
||||||
json = CustomWizard::WizardSerializer.new(
|
json = CustomWizard::WizardSerializer.new(
|
||||||
|
@ -92,3 +100,4 @@ describe CustomWizard::WizardSerializer do
|
||||||
expect(json[:wizard][:groups].present?).to eq(false)
|
expect(json[:wizard][:groups].present?).to eq(false)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
Laden …
In neuem Issue referenzieren