0
0
Fork 1
Spiegel von https://github.com/paviliondev/discourse-custom-wizard.git synchronisiert 2024-11-22 09:20:29 +01:00
Dieser Commit ist enthalten in:
angusmcleod 2021-09-24 17:58:42 +08:00
Ursprung 270d3bccf5
Commit 084c6f4a7a
94 geänderte Dateien mit 1228 neuen und 413 gelöschten Zeilen

Datei anzeigen

@ -6,20 +6,20 @@ import I18n from "I18n";
const klasses = ["topic", "post", "group", "category"];
const types = ["string", "boolean", "integer", "json"];
const proTypes = {
const subscriptionTypes = {
klass: ["group", "category"],
type: ["json"],
};
const generateContent = function (array, type, proSubscribed = false) {
const generateContent = function (array, type, subscribed = false) {
return array.reduce((result, key) => {
let proArr = proTypes[type];
let pro = proArr && proArr.includes(key);
if (!pro || proSubscribed) {
let subArr = subscriptionTypes[type];
let subscription = subArr && subArr.includes(key);
if (!subscription || subscribed) {
result.push({
id: key,
name: I18n.t(`admin.wizard.custom_field.${type}.${key}`),
pro,
subscription,
});
}
return result;
@ -32,11 +32,11 @@ export default Component.extend({
postSerializers: ["post"],
groupSerializers: ["basic_group"],
categorySerializers: ["basic_category"],
klassContent: computed("proSubscribed", function () {
return generateContent(klasses, "klass", this.proSubscribed);
klassContent: computed("subscribed", function () {
return generateContent(klasses, "klass", this.subscribed);
}),
typeContent: computed("proSubscribed", function () {
return generateContent(types, "type", this.proSubscribed);
typeContent: computed("subscribed", function () {
return generateContent(types, "type", this.subscribed);
}),
showInputs: or("field.new", "field.edit"),
classNames: ["custom-field-input"],
@ -54,7 +54,7 @@ export default Component.extend({
const serializers = this.get(`${klass}Serializers`);
if (serializers) {
return generateContent(serializers, "serializers", this.proSubscribed);
return generateContent(serializers, "serializers", this.subscribed);
} else {
return [];
}

Datei anzeigen

@ -0,0 +1,21 @@
import Component from "@ember/component";
import discourseComputed from "discourse-common/utils/decorators";
export default Component.extend({
classNameBindings: [":subscription-container", "subscribed"],
@discourseComputed('subscribed')
subscribedIcon(subscribed) {
return subscribed ? 'check' : 'dash';
},
@discourseComputed('subscribed')
subscribedLabel(subscribed) {
return `admin.wizard.subscription_container.${subscribed ? 'subscribed' : 'not_subscribed'}.label`;
},
@discourseComputed('subscribed')
subscribedTitle(subscribed) {
return `admin.wizard.subscription_container.${subscribed ? 'subscribed' : 'not_subscribed'}.title`;
}
})

Datei anzeigen

@ -94,15 +94,15 @@ export default Component.extend(UndoChanges, {
return apis.find((a) => a.name === api).endpoints;
},
@discourseComputed("proSubscribed")
actionTypes(proSubscribed) {
@discourseComputed("subscribed")
actionTypes(subscribed) {
return Object.keys(wizardSchema.action.types).reduce((result, type) => {
let pro = wizardSchema.action.proTypes.includes(type);
if (proSubscribed || !pro) {
let subscription = wizardSchema.action.subscriptionTypes.includes(type);
if (subscribed || !subscription) {
result.push({
id: type,
name: I18n.t(`admin.wizard.action.${type}.label`),
pro,
subscription,
});
}
return result;

Datei anzeigen

@ -0,0 +1,34 @@
import Component from "@ember/component";
import discourseComputed from "discourse-common/utils/decorators";
import { notEmpty, not } from "@ember/object/computed";
import I18n from "I18n";
export default Component.extend({
classNameBindings: [':wizard-notice', 'notice.type', 'dismissed', 'expired'],
showFull: false,
resolved: notEmpty('notice.expired_at'),
dismissed: notEmpty('notice.dismissed_at'),
canDismiss: not('dismissed'),
@discourseComputed('notice.type')
title(type) {
return I18n.t(`admin.wizard.notice.title.${type}`);
},
@discourseComputed('notice.type')
icon(type) {
return {
warning: 'exclamation-circle',
info: 'info-circle'
}[type];
},
actions: {
dismiss() {
this.set('dismissing', true)
this.notice.dismiss().then(() => {
this.set('dismissing', false);
});
}
}
});

Datei anzeigen

@ -6,7 +6,7 @@ import discourseComputed from "discourse-common/utils/decorators";
import I18n from "I18n";
export default Component.extend({
classNames: ["realtime-validations", "setting", "full", "pro"],
classNames: ["realtime-validations", "setting", "full", "subscription"],
@discourseComputed
timeUnits() {

Datei anzeigen

@ -1,18 +1,18 @@
import SingleSelectComponent from "select-kit/components/single-select";
export default SingleSelectComponent.extend({
classNames: ["combo-box", "wizard-pro-selector"],
classNames: ["combo-box", "wizard-subscription-selector"],
selectKitOptions: {
autoFilterable: false,
filterable: false,
showFullTitle: true,
headerComponent: "wizard-pro-selector/wizard-pro-selector-header",
headerComponent: "wizard-subscription-selector/wizard-subscription-selector-header",
caretUpIcon: "caret-up",
caretDownIcon: "caret-down",
},
modifyComponentForRow() {
return "wizard-pro-selector/wizard-pro-selector-row";
return "wizard-subscription-selector/wizard-subscription-selector-row";
},
});

Datei anzeigen

@ -3,7 +3,7 @@ import { computed } from "@ember/object";
import { reads } from "@ember/object/computed";
export default SingleSelectHeaderComponent.extend({
classNames: ["combo-box-header", "wizard-pro-selector-header"],
classNames: ["combo-box-header", "wizard-subscription-selector-header"],
caretUpIcon: reads("selectKit.options.caretUpIcon"),
caretDownIcon: reads("selectKit.options.caretDownIcon"),
caretIcon: computed(

Datei anzeigen

@ -1,12 +1,12 @@
import Component from "@ember/component";
import CustomWizardPro from "../models/custom-wizard-pro";
import CustomWizardSubscription from "../models/custom-wizard-subscription";
import { notEmpty } from "@ember/object/computed";
import discourseComputed from "discourse-common/utils/decorators";
import I18n from "I18n";
export default Component.extend({
classNameBindings: [
":custom-wizard-pro-subscription",
":custom-wizard-subscription",
"subscription.active:active:inactive",
],
subscribed: notEmpty("subscription"),
@ -14,8 +14,8 @@ export default Component.extend({
@discourseComputed("subscription.type")
title(type) {
return type
? I18n.t(`admin.wizard.pro.subscription.title.${type}`)
: I18n.t("admin.wizard.pro.not_subscribed");
? I18n.t(`admin.wizard.subscription.subscription.title.${type}`)
: I18n.t("admin.wizard.subscription.not_subscribed");
},
@discourseComputed("subscription.active")
@ -25,13 +25,13 @@ export default Component.extend({
@discourseComputed("stateClass")
stateLabel(stateClass) {
return I18n.t(`admin.wizard.pro.subscription.status.${stateClass}`);
return I18n.t(`admin.wizard.subscription.subscription.status.${stateClass}`);
},
actions: {
update() {
this.set("updating", true);
CustomWizardPro.update_subscription()
CustomWizardSubscription.update()
.then((result) => {
if (result.success) {
this.setProperties({

Datei anzeigen

@ -0,0 +1,3 @@
{{#if wizardWarningNotice}}
{{wizard-notice notice=wizardWarningNotice}}
{{/if}}

Datei anzeigen

@ -0,0 +1,12 @@
import { getOwner } from "discourse-common/lib/get-owner";
export default {
setupComponent() {
const controller = getOwner(this).lookup('controller:admin-dashboard')
const wizardWarningNotice = controller.get('wizardWarningNotice');
if (wizardWarningNotice) {
this.set('wizardWarningNotice', wizardWarningNotice);
}
}
}

Datei anzeigen

@ -1,3 +1,7 @@
{{#if currentUser.admin}}
{{nav-item route="adminWizards" label="admin.wizard.nav_label"}}
{{#if wizardErrorNotice}}
{{d-icon "exclaimation-circle"}}
{{/if}}
{{/if}}

Datei anzeigen

@ -1,6 +1,6 @@
import Controller from "@ember/controller";
import discourseComputed from "discourse-common/utils/decorators";
import CustomWizardPro from "../models/custom-wizard-pro";
import CustomWizardSubscription from "../models/custom-wizard-subscription";
import { alias } from "@ember/object/computed";
export default Controller.extend({

Datei anzeigen

@ -0,0 +1,20 @@
import Controller from "@ember/controller";
import { popupAjaxError } from "discourse/lib/ajax-error";
import { ajax } from "discourse/lib/ajax";
export default Controller.extend({
actions: {
dismissNotice(noticeId) {
ajax(`/admin/wizards/notice/${this.id}`, {
type: "DELETE",
})
.then(result => {
if (result.success) {
const notices = this.notices;
notices.removeObject(notices.findBy('id', noticeId));
}
})
.catch(popupAjaxError);
}
}
});

Datei anzeigen

@ -59,8 +59,8 @@ export default {
resetNamespace: true,
});
this.route("adminWizardsPro", {
path: "/pro",
this.route("adminWizardsSubscription", {
path: "/subscription",
resetNamespace: true,
});
}

Datei anzeigen

@ -1,4 +1,8 @@
import DiscourseURL from "discourse/lib/url";
import { withPluginApi } from "discourse/lib/plugin-api";
import { ajax } from "discourse/lib/ajax";
import CustomWizardNotice from "../models/custom-wizard-notice";
import { A } from "@ember/array";
export default {
name: "custom-wizard-edits",
@ -16,5 +20,29 @@ export default {
}
return existing.apply(this, [path, opts]);
};
withPluginApi("0.8.36", (api) => {
api.modifyClass('route:admin-dashboard', {
afterModel() {
return CustomWizardNotice.list().then(result => {
if (result && result.length) {
this.set('notices', A(result.map(n => CustomWizardNotice.create(n))));
}
});
},
setupController(controller, model) {
if (this.notices) {
let warningNotices = this.notices.filter(n => n.type === 'warning');
if (warningNotices.length) {
controller.set('wizardWarningNotice', warningNotices[0]);
}
}
this._super(...arguments);
}
});
});
},
};

Datei anzeigen

@ -193,6 +193,14 @@ const action = {
"members_visibility_level",
],
required: ["id", "type"],
subscriptionTypes: [
"send_message",
"add_to_group",
"create_category",
"create_group",
"send_to_api",
],
required: ["id", "type"],
proTypes: [
"send_message",
"add_to_group",

Datei anzeigen

@ -0,0 +1,23 @@
import EmberObject from "@ember/object";
import { ajax } from "discourse/lib/ajax";
import { popupAjaxError } from "discourse/lib/ajax-error";
const CustomWizardNotice = EmberObject.extend();
CustomWizardNotice.reopen({
dismiss() {
return ajax(`/admin/wizards/notice/${this.id}`, { type: 'PUT' }).then(result => {
if (result.success) {
this.set('dismissed_at', result.dismissed_at);
}
}).catch(popupAjaxError)
}
});
CustomWizardNotice.reopenClass({
list() {
return ajax('/admin/wizards/notice').catch(popupAjaxError)
}
});
export default CustomWizardNotice;

Datei anzeigen

@ -2,11 +2,11 @@ import { ajax } from "discourse/lib/ajax";
import { popupAjaxError } from "discourse/lib/ajax-error";
import EmberObject from "@ember/object";
const CustomWizardPro = EmberObject.extend();
const CustomWizardSubscription = EmberObject.extend();
const basePath = "/admin/wizards/pro";
const basePath = "/admin/wizards/subscription";
CustomWizardPro.reopenClass({
CustomWizardSubscription.reopenClass({
status() {
return ajax(basePath, {
type: "GET",
@ -23,11 +23,11 @@ CustomWizardPro.reopenClass({
}).catch(popupAjaxError);
},
update_subscription() {
return ajax(`${basePath}/subscription`, {
update() {
return ajax(basePath, {
type: "POST",
}).catch(popupAjaxError);
},
});
export default CustomWizardPro;
export default CustomWizardSubscription;

Datei anzeigen

@ -9,11 +9,11 @@ export default DiscourseRoute.extend({
setupController(controller, model) {
const customFields = A(model.custom_fields || []);
const proSubscribed = model.pro_subscribed;
const subscribed = model.subscribed;
controller.setProperties({
customFields,
proSubscribed,
subscribed,
});
},
});

Datei anzeigen

@ -1,9 +1,9 @@
import CustomWizardPro from "../models/custom-wizard-pro";
import CustomWizardSubscription from "../models/custom-wizard-subscription";
import DiscourseRoute from "discourse/routes/discourse";
export default DiscourseRoute.extend({
model() {
return CustomWizardPro.status();
return CustomWizardSubscription.status();
},
setupController(controller, model) {
@ -13,7 +13,7 @@ export default DiscourseRoute.extend({
actions: {
authorize() {
CustomWizardPro.authorize();
CustomWizardSubscription.authorize();
},
},
});

Datei anzeigen

@ -39,7 +39,7 @@ export default DiscourseRoute.extend({
currentStep: wizard.steps[0],
currentAction: wizard.actions[0],
creating: model.create,
proSubscribed: parentModel.pro_subscribed,
subscribed: parentModel.subscribed,
};
controller.setProperties(props);

Datei anzeigen

@ -1,9 +1,19 @@
import DiscourseRoute from "discourse/routes/discourse";
import { ajax } from "discourse/lib/ajax";
import { A } from "@ember/array";
export default DiscourseRoute.extend({
beforeModel(transition) {
model() {
return ajax('/admin/wizards');
},
setupController(controller, model) {
controller.set('notices', A(model.notices));
},
afterModel(model, transition) {
if (transition.targetName === "adminWizards.index") {
this.transitionTo("adminWizardsWizard");
}
},
}
});

Datei anzeigen

@ -33,7 +33,7 @@
field=field
removeField=(action "removeField")
saveField=(action "saveField")
proSubscribed=proSubscribed}}
subscribed=subscribed}}
{{/each}}
</tbody>
</table>

Datei anzeigen

@ -1,34 +0,0 @@
<div class="admin-wizard-controls">
<h3>{{i18n "admin.wizard.pro.title"}}</h3>
<div class="buttons">
{{#if model.authentication.active}}
{{conditional-loading-spinner size="small" condition=unauthorizing}}
<a {{action "unauthorize"}} title={{i18n "admin.wizard.pro.unauthorize"}} role="button">
{{i18n "admin.wizard.pro.unauthorize"}}
</a>
<label title={{i18n "admin.wizard.pro.authorized"}}>
{{i18n "admin.wizard.pro.authorized"}}
</label>
{{else}}
{{d-button
icon="id-card"
label="admin.wizard.pro.authorize"
title="admin.wizard.pro.authorize"
action=(route-action "authorize")}}
{{/if}}
</div>
</div>
{{wizard-message
key=messageKey
url=messageUrl
type=messageType
opts=messageOpts
component="pro"}}
<div class="admin-wizard-container">
{{#if showSubscription}}
{{wizard-pro-subscription subscription=model.subscription}}
{{/if}}
</div>

Datei anzeigen

@ -0,0 +1,35 @@
<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>

Datei anzeigen

@ -126,11 +126,10 @@
</div>
</div>
{{#if proSubscribed}}
<div class="setting pro">
{{#subscription-container subscribed=subscribed}}
<div class="setting">
<div class="setting-label">
<label>{{i18n "admin.wizard.save_submissions"}}</label>
<span class="pro-label">{{i18n "admin.wizard.pro.label"}}</span>
</div>
<div class="setting-value">
{{input type="checkbox" checked=wizard.save_submissions}}
@ -138,17 +137,16 @@
</div>
</div>
<div class="setting pro">
<div class="setting">
<div class="setting-label">
<label>{{i18n "admin.wizard.restart_on_revisit"}}</label>
<span class="pro-label">{{i18n "admin.wizard.pro.label"}}</span>
</div>
<div class="setting-value">
{{input type="checkbox" checked=wizard.restart_on_revisit}}
<span>{{i18n "admin.wizard.restart_on_revisit_label"}}</span>
</div>
</div>
{{/if}}
{{/subscription-container}}
</div>
{{wizard-links
@ -163,7 +161,7 @@
currentField=currentField
wizardFields=wizardFields
fieldTypes=fieldTypes
proSubscribed=proSubscribed}}
subscribed=subscribed}}
{{/if}}
{{wizard-links
@ -180,7 +178,7 @@
apis=apis
removeAction="removeAction"
wizardFields=wizardFields
proSubscribed=proSubscribed}}
subscribed=subscribed}}
{{/each}}
<div class="admin-wizard-buttons">

Datei anzeigen

@ -7,13 +7,18 @@
{{/if}}
{{nav-item route="adminWizardsLogs" label="admin.wizard.log.nav_label"}}
{{nav-item route="adminWizardsManager" label="admin.wizard.manager.nav_label"}}
{{nav-item route="adminWizardsPro" label="admin.wizard.pro.nav_label"}}
{{nav-item route="adminWizardsSubscription" label="admin.wizard.subscription.nav_label"}}
<div class="admin-actions">
<a target="_blank" class="btn btn-pavilion-pro" rel="noreferrer noopener" href="https://thepavilion.io/w/support" title={{i18n "admin.wizard.pro_support_button.title"}}>{{d-icon "far-life-ring"}}{{i18n "admin.wizard.pro_support_button.label"}}</a>
<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"}}
</a>
</div>
{{/admin-nav}}
<div class="admin-container">
{{#each notices as |notice|}}
{{wizard-notice notice=notice}}
{{/each}}
{{outlet}}
</div>

Datei anzeigen

@ -1,13 +1,13 @@
{{#if showInputs}}
<td>
{{wizard-pro-selector
{{wizard-subscription-selector
value=field.klass
content=klassContent
none="admin.wizard.custom_field.klass.select"
onChange=(action (mut field.klass))}}
</td>
<td>
{{wizard-pro-selector
{{wizard-subscription-selector
value=field.type
content=typeContent
none="admin.wizard.custom_field.type.select"

Datei anzeigen

@ -0,0 +1,12 @@
<div class=subscription-header>
<h3>{{i18n 'admin.wizard.subscription_container.title'}}</h3>
<a href="/admin/wizards/subscription" title={{i18n subscribedTitle}}>
{{d-icon subscribedIcon}}
{{i18n subscribedLabel}}
</a>
</div>
<div class="subscription-settings">
{{yield}}
</div>

Datei anzeigen

@ -12,7 +12,7 @@
</div>
<div class="setting-value">
{{wizard-pro-selector
{{wizard-subscription-selector
value=action.type
content=actionTypes
onChange=(action "changeType")

Datei anzeigen

@ -207,11 +207,10 @@
</div>
{{/if}}
{{#if proSubscribed}}
<div class="setting full field-mapper-setting pro">
{{#subscription-container subscribed=subscribed}}
<div class="setting full field-mapper-setting">
<div class="setting-label">
<label>{{i18n "admin.wizard.condition"}}</label>
<span class="pro-label">{{i18n "admin.wizard.pro.label"}}</span>
</div>
<div class="setting-value">
@ -221,10 +220,9 @@
</div>
</div>
<div class="setting full field-mapper-setting pro">
<div class="setting full field-mapper-setting">
<div class="setting-label">
<label>{{i18n "admin.wizard.index"}}</label>
<span class="pro-label">{{i18n "admin.wizard.pro.label"}}</span>
<label>{{i18n "admin.wizard.index"}}</label>>
</div>
<div class="setting-value">
@ -256,4 +254,4 @@
{{#if validations}}
{{wizard-realtime-validations field=field validations=validations}}
{{/if}}
{{/if}}
{{/subscription-container}}

Datei anzeigen

@ -33,11 +33,10 @@
</div>
</div>
{{#if proSubscribed}}
<div class="setting full field-mapper-setting pro">
{{#subscription-container subscribed=subscribed}}
<div class="setting full field-mapper-setting">
<div class="setting-label">
<label>{{i18n "admin.wizard.condition"}}</label>
<span class="pro-label">{{i18n "admin.wizard.pro.label"}}</span>
</div>
<div class="setting-value">
@ -83,10 +82,9 @@
</div>
</div>
<div class="setting full field-mapper-setting pro">
<div class="setting full field-mapper-setting">
<div class="setting-label">
<label>{{i18n "admin.wizard.step.permitted_params.label"}}</label>
<span class="pro-label">{{i18n "admin.wizard.pro.label"}}</span>
</div>
<div class="setting-value">
{{wizard-mapper
@ -100,7 +98,7 @@
)}}
</div>
</div>
{{/if}}
{{/subscription-container}}
{{wizard-links
itemType="field"
@ -116,5 +114,5 @@
fieldTypes=fieldTypes
removeField="removeField"
wizardFields=wizardFields
proSubscribed=proSubscribed}}
subscribed=subscribed}}
{{/each}}

Datei anzeigen

@ -0,0 +1,37 @@
<div class="notice-header">
{{#if resolved}}
<div class="notice-expired-at notice-badge" title={{notice.expired_at}}>
{{d-icon "check"}}
<span class="notice-resolved">{{i18n "admin.wizard.notice.resolved"}}</span>
{{format-date notice.expired_at leaveAgo="true"}}
</div>
{{/if}}
<div class="notice-title notice-badge" title={{title}}>
{{d-icon icon}}
<span>{{title}}</span>
</div>
<div class="notice-created-at notice-badge" title={{notice.created_at}}>
{{d-icon "calendar-alt"}}
<span class="notice-issued">{{i18n "admin.wizard.notice.issued"}}</span>
{{format-date notice.created_at leaveAgo="true"}}
</div>
<div class="notice-plugin notice-badge" title={{i18n "admin.wizard.notice.plugin"}}>
{{d-icon "plug"}}
<span>{{i18n "admin.wizard.notice.plugin"}}</span>
</div>
</div>
<div class="notice-message">
{{{notice.message}}}
</div>
{{#if canDismiss}}
{{#if dismissing}}
{{loading-spinner size="small"}}
{{else}}
<a {{action "dismiss"}} role="button" class="dismiss-notice">{{d-icon "times"}}</a>
{{/if}}
{{/if}}

Datei anzeigen

@ -1,6 +1,5 @@
<div class="setting-label">
<label>{{i18n "admin.wizard.field.validations.header"}}</label>
<span class="pro-label">{{i18n "admin.wizard.pro.label"}}</span>
</div>
<div class="setting-value full">
<ul>

Datei anzeigen

@ -7,8 +7,8 @@
shouldDisplayClearableButton=shouldDisplayClearableButton
}}
{{#if selectedContent.pro}}
<span class="pro-label">{{i18n "admin.wizard.pro.label"}}</span>
{{#if selectedContent.subscription}}
<span class="subscription-label">{{i18n "admin.wizard.subscription.label"}}</span>
{{/if}}
{{d-icon caretIcon class="caret-icon"}}

Datei anzeigen

@ -9,7 +9,7 @@
<div class="texts">
<span class="name">{{html-safe label}}</span>
{{#if item.pro}}
<span class="pro-label">{{i18n "admin.wizard.pro.label"}}</span>
{{#if item.subscription}}
<span class="subscription-label">{{i18n "admin.wizard.subscription.label"}}</span>
{{/if}}
</div>

Datei anzeigen

@ -13,8 +13,8 @@
icon="sync"
action=(action "update")
disabled=updating
title="admin.wizard.pro.subscription.update"
label="admin.wizard.pro.subscription.update"}}
title="admin.wizard.subscription.subscription.update"
label="admin.wizard.subscription.subscription.update"}}
</div>
</div>
@ -24,7 +24,7 @@
{{#if subscription.updated_at}}
<div class="subscription-updated-at" title={{subscription.updated_at}}>
{{i18n "admin.wizard.pro.subscription.last_updated"}} {{format-date subscription.updated_at leaveAgo="true"}}
{{i18n "admin.wizard.subscription.subscription.last_updated"}} {{format-date subscription.updated_at leaveAgo="true"}}
</div>
{{/if}}
</div>

Datei anzeigen

@ -1,8 +1,8 @@
@import "wizard-mapper";
@import "wizard-manager";
@import "wizard-api";
@import "wizard/mapper";
@import "wizard/manager";
@import "wizard/api";
@import "common/components/buttons";
@import "wizard-variables";
@import "wizard/variables";
.admin-wizard-controls {
display: flex;
@ -121,7 +121,7 @@
}
.wizard-settings-parent {
padding: 20px;
padding: 1em;
border: 1px solid var(--primary-low);
}
@ -142,7 +142,7 @@
.wizard-basic-details,
.wizard-custom-field,
.advanced-settings {
.subscription-settings {
@extend .wizard-settings-group;
}
@ -411,7 +411,7 @@
margin-top: 5px;
}
&.pro {
&.subscription {
.setting-label {
display: flex;
flex-direction: column;
@ -423,15 +423,6 @@
}
}
.advanced-settings {
width: 100%;
margin-top: 30px;
[class~="setting"]:first-of-type {
border-top: none;
}
}
.wizard-custom-action > [class~="setting"]:first-of-type {
margin-bottom: 0;
}
@ -735,7 +726,7 @@
flex-wrap: wrap;
> li {
background-color: var(--primary-low);
border: 1px solid var(--primary);
padding: 1em;
margin: 0 0 1em 0;
@ -797,12 +788,12 @@
vertical-align: middle;
}
.pro-label {
.subscription-label {
color: var(--tertiary);
font-size: 0.75em;
}
.admin-wizards-pro {
.admin-wizards-subscription {
.admin-wizard-controls {
h3,
label {
@ -826,7 +817,7 @@
}
}
.custom-wizard-pro-subscription {
.custom-wizard-subscription {
.title-container {
display: flex;
justify-content: space-between;
@ -861,19 +852,19 @@
}
}
.wizard-pro-selector.select-kit.single-select {
.wizard-subscription-selector.select-kit.single-select {
.select-kit-row .texts {
display: flex;
align-items: center;
}
.pro-label {
.subscription-label {
margin-left: 0.75em;
padding-top: 0.25em;
}
}
.btn.btn-pavilion-pro {
.btn.btn-pavilion-support {
background: var(--pavilion-primary);
color: var(--pavilion-secondary);
@ -883,11 +874,108 @@
&:hover,
&:focus {
background: darken($pavilionPrimary, 5%);
background: darken($pavilion_primary, 5%);
&[href],
svg.d-icon {
color: darken($pavilionSecondary, 10%);
color: darken($pavilion_secondary, 10%);
}
}
}
.subscription-container {
width: 100%;
padding: 1em;
background-color: rgba($pavilion_primary, 0.1);
.subscription-header {
display: flex;
justify-content: space-between;
margin-bottom: 1em;
h3 {
margin: 0;
}
a {
color: var(--pavilion-primary);
}
}
&:not(.subscribed) .subscription-settings {
filter: blur(1px);
}
}
.wizard-notice {
padding: 1em;
margin-bottom: 1em;
border: 1px solid var(--primary);
border-radius: 4px;
position: relative;
&.dismissed {
display: none;
}
.d-icon {
margin-right: .4em;
}
.notice-header {
display: flex;
}
.notice-badge {
border: 1px solid var(--primary);
display: inline-flex;
align-items: center;
padding: 0 .5em;
border-radius: 4px;
margin-right: 1em;
font-size: .9em;
line-height: 25px;
min-height: 25px;
box-sizing: border-box;
&:last-of-type {
margin-right: 0;
}
}
&.warning {
.notice-expired-at {
border: 1px solid var(--success);
background-color: rgba($success, 0.1);
color: var(--success);
}
.notice-title {
border: 1px solid var(--pavilion-warning);
background-color: rgba($pavilion_warning, 0.1);
color: var(--pavilion-warning);
}
}
.notice-issued {
margin-right: .3em;
}
.notice-message {
p {
margin: .5em 0;
}
p:last-of-type {
margin-bottom: 0;
}
}
.dismiss-notice,
.spinner {
position: absolute;
top: 1em;
right: 1em;
color: var(--primary);
}
}

Datei anzeigen

@ -0,0 +1,9 @@
$pavilion_primary: #3c1c8c;
$pavilion_secondary: #ffffff;
$pavilion_warning: rgb(243, 163, 61);
:root {
--pavilion-primary: #{$pavilion_primary};
--pavilion-secondary: #{$pavilion_secondary};
--pavilion-warning: #{$pavilion_warning};
}

Datei anzeigen

@ -1,7 +0,0 @@
$pavilionPrimary: #3c1c8c;
$pavilionSecondary: #ffffff;
:root {
--pavilion-primary: #3c1c8c;
--pavilion-secondary: #ffffff;
}

Datei anzeigen

@ -60,9 +60,9 @@ en:
expand_text: "Read More"
collapse_text: "Show Less"
pro_support_button:
title: "Request Pro Support"
label: "Pro Support"
support_button:
title: "Request Support"
label: "Support"
message:
wizard:
@ -95,10 +95,10 @@ en:
destroying: Destroying wizards...
import_complete: Import complete
destroy_complete: Destruction complete
pro:
documentation: Check out the PRO documentation
authorize: "Authorize this forum to use your PRO subscription plan on %{server}."
not_subscribed: "You've authorized, but are not currently subscribed to a PRO plan on %{server}."
subscription:
documentation: Check out the subscription documentation
authorize: "Authorize this forum to use your Custom Wizard subscription plan on %{server}."
not_subscribed: "You've authorized, but are not currently subscribed to a Custom Wizard plan on %{server}."
subscription_expiring: "Your subscription is active, but will expire in the next 48 hours."
subscription_active: "Your subscription is active."
subscription_inactive: "Your subscription is inactive on this forum. Read more in <a href='https://thepavilion.io/t/3652'>the documentation</a>."
@ -455,10 +455,19 @@ en:
destroy: Destroy
destroyed: destroyed
pro:
nav_label: PRO
label: PRO
title: Custom Wizard PRO
subscription_container:
title: Subscriber Features
subscribed:
label: Subscribed
title: You're subscribed and can use these features
not_subscribed:
label: Not Subscribed
title: Subscribe to use these features
subscription:
nav_label: Subscription
label: Subscription
title: Custom Wizard Subscription
authorize: Authorize
authorized: Authorized
unauthorize: cancel
@ -473,6 +482,13 @@ en:
update: Update
last_updated: Last updated
notice:
plugin: Custom Wizard Plugin
issued: Issued
resolved: Resolved
title:
warning: Warning Notice
wizard_js:
group:
select: "Select a group"

Datei anzeigen

@ -17,7 +17,7 @@ en:
name_too_short: "'%{name}' is too short for a custom field name (min length is #{min_length})"
name_already_taken: "'%{name}' is already taken as a custom field name"
save_default: "Failed to save custom field '%{name}'"
pro_type: "%{type} custom fields require PRO Subscription"
subscription_type: "%{type} custom fields require a subscription"
field:
too_short: "%{label} must be at least %{min} characters"
@ -50,7 +50,16 @@ en:
required: "%{property} is required"
conflict: "Wizard with id '%{wizard_id}' already exists"
after_time: "After time setting is invalid"
pro: "%{type} %{property} is PRO only"
subscription: "%{type} %{property} is subscription only"
notice:
connection_error: "Failed to connect to [%{server}](http://%{server})"
compatibility_issue: >
The Custom Wizard Plugin may have a compatibility issue with the latest version of Discourse.
Please check the Custom Wizard Plugin status on [%{server}](http://%{server}) before updating Discourse.
plugin_status_connection_error_limit: >
We're unable to connect to the plugin status server to determine whether there are any compatibility issues with the latest version of Discourse.
Please contact <a href="mailto:support@thepavilion.io">support@thepavilion.io</a> for further assistance.
site_settings:
custom_wizard_enabled: "Enable custom wizards."

Datei anzeigen

@ -45,10 +45,13 @@ Discourse::Application.routes.append do
post 'admin/wizards/manager/import' => 'admin_manager#import'
delete 'admin/wizards/manager/destroy' => 'admin_manager#destroy'
get 'admin/wizards/pro' => 'admin_pro#index'
get 'admin/wizards/pro/authorize' => 'admin_pro#authorize'
get 'admin/wizards/pro/authorize/callback' => 'admin_pro#authorize_callback'
delete 'admin/wizards/pro/authorize' => 'admin_pro#destroy_authentication'
post 'admin/wizards/pro/subscription' => 'admin_pro#update_subscription'
get 'admin/wizards/subscription' => 'admin_subscription#index'
post 'admin/wizards/subscription' => 'admin_subscription#update_subscription'
get 'admin/wizards/subscription/authorize' => 'admin_subscription#authorize'
get 'admin/wizards/subscription/authorize/callback' => 'admin_subscription#authorize_callback'
delete 'admin/wizards/subscription/authorize' => 'admin_subscription#destroy_authentication'
get 'admin/wizards/notice' => 'admin_notice#index'
put 'admin/wizards/notice/:notice_id' => 'admin_notice#dismiss'
end
end

Datei anzeigen

@ -3,6 +3,12 @@ class CustomWizard::AdminController < ::Admin::AdminController
before_action :ensure_admin
def index
render_json_dump(
notices: ActiveModel::ArraySerializer.new(
CustomWizard::Notice.list,
each_serializer: CustomWizard::NoticeSerializer
)
)
end
private

Datei anzeigen

@ -3,7 +3,7 @@ class CustomWizard::AdminCustomFieldsController < CustomWizard::AdminController
def index
render_json_dump(
custom_fields: custom_field_list,
pro_subscribed: CustomWizard::Pro.subscribed?
subscribed: CustomWizard::Subscription.subscribed?
)
end

Datei anzeigen

@ -0,0 +1,22 @@
# frozen_string_literal: true
class CustomWizard::AdminNoticeController < CustomWizard::AdminController
before_action :find_notice, only: [:dismiss]
def index
render_serialized(CustomWizard::Notice.list, CustomWizard::NoticeSerializer)
end
def dismiss
if @notice.dismissable? && @notice.dismiss
render json: success_json.merge(dismissed_at: @notice.dismissed_at)
else
render json: failed_json
end
end
def find_notice
@notice = CustomWizard::Notice.find(params[:notice_id])
raise Discourse::InvalidParameters.new(:notice_id) unless @notice
end
end

Datei anzeigen

@ -1,48 +0,0 @@
# frozen_string_literal: true
class CustomWizard::AdminProController < CustomWizard::AdminController
skip_before_action :check_xhr, :preload_json, :verify_authenticity_token, only: [:authorize, :authorize_callback]
def index
render_serialized(pro, CustomWizard::ProSerializer, root: false)
end
def authorize
request_id = SecureRandom.hex(32)
cookies[:user_api_request_id] = request_id
redirect_to pro.authentication_url(current_user.id, request_id).to_s
end
def authorize_callback
payload = params[:payload]
request_id = cookies[:user_api_request_id]
pro.authentication_response(request_id, payload)
pro.update_subscription
redirect_to '/admin/wizards/pro'
end
def destroy_authentication
if pro.destroy_authentication
render json: success_json
else
render json: failed_json
end
end
def update_subscription
if pro.update_subscription
subscription = CustomWizard::ProSubscriptionSerializer.new(pro.subscription, root: false)
render json: success_json.merge(subscription: subscription)
else
render json: failed_json
end
end
protected
def pro
@pro ||= CustomWizard::Pro.new
end
end

Datei anzeigen

@ -0,0 +1,48 @@
# frozen_string_literal: true
class CustomWizard::AdminSubscriptionController < CustomWizard::AdminController
skip_before_action :check_xhr, :preload_json, :verify_authenticity_token, only: [:authorize, :authorize_callback]
def index
render_serialized(subscription, CustomWizard::SubscriptionSerializer, root: false)
end
def authorize
request_id = SecureRandom.hex(32)
cookies[:user_api_request_id] = request_id
redirect_to subscription.authentication_url(current_user.id, request_id).to_s
end
def authorize_callback
payload = params[:payload]
request_id = cookies[:user_api_request_id]
subscription.authentication_response(request_id, payload)
subscription.update
redirect_to '/admin/wizards/subscription'
end
def destroy_authentication
if subscription.destroy_authentication
render json: success_json
else
render json: failed_json
end
end
def update_subscription
if subscription.update
serialized_subscription = CustomWizard::Subscription::SubscriptionSerializer.new(subscription.subscription, root: false)
render json: success_json.merge(subscription: serialized_subscription)
else
render json: failed_json
end
end
protected
def subscription
@subscription ||= CustomWizard::Subscription.new
end
end

Datei anzeigen

@ -11,7 +11,7 @@ class CustomWizard::AdminWizardController < CustomWizard::AdminController
field_types: CustomWizard::Field.types,
realtime_validations: CustomWizard::RealtimeValidation.types,
custom_fields: custom_field_list,
pro_subscribed: CustomWizard::Pro.subscribed?
subscribed: CustomWizard::Subscription.subscribed?
)
end

Datei anzeigen

@ -5,7 +5,7 @@ class CustomWizard::WizardController < ::ApplicationController
layout 'wizard'
before_action :ensure_plugin_enabled
before_action :update_pro_subscription, only: [:index]
before_action :update_subscription, only: [:index]
helper_method :wizard_page_title
helper_method :wizard_theme_id
helper_method :wizard_theme_lookup
@ -84,7 +84,7 @@ class CustomWizard::WizardController < ::ApplicationController
end
end
def update_pro_subscription
CustomWizard::Pro.update_subscription
def update_subscription
CustomWizard::Subscription.update
end
end

Datei anzeigen

@ -1,5 +1,5 @@
{
"result": {
"line": 92.14
"line": 92.3
}
}

Datei anzeigen

@ -0,0 +1,9 @@
# frozen_string_literal: true
class Jobs::CustomWizardUpdateNotices < ::Jobs::Scheduled
every 5.minutes
def execute(args = {})
CustomWizard::Notice.update
end
end

Datei anzeigen

@ -0,0 +1,9 @@
# frozen_string_literal: true
class Jobs::CustomWizardUpdateSubscription < ::Jobs::Scheduled
every 1.hour
def execute(args = {})
CustomWizard::Subscription.update
end
end

Datei anzeigen

@ -1,9 +0,0 @@
# frozen_string_literal: true
class CustomWizard::UpdateProSubscription < ::Jobs::Scheduled
every 1.hour
def execute(args = {})
CustomWizard::Pro.update_subscription
end
end

Datei anzeigen

@ -749,8 +749,4 @@ class CustomWizard::Action
@log.join('; ')
)
end
def pro_actions
%w[send_message watch_categories send_to_api create_group create_category]
end
end

Datei anzeigen

@ -17,9 +17,9 @@ class ::CustomWizard::CustomField
category: ["basic_category"],
post: ["post"]
}
PRO_CLASSES ||= ['category', 'group']
SUBSCRIPTION_CLASSES ||= ['category', 'group']
TYPES ||= ["string", "boolean", "integer", "json"]
PRO_TYPES ||= ["json"]
SUBSCRIPTION_TYPES ||= ["json"]
LIST_CACHE_KEY ||= 'custom_field_list'
def self.serializers
@ -40,7 +40,7 @@ class ::CustomWizard::CustomField
end
end
@pro = CustomWizard::Pro.new
@subscription = CustomWizard::Subscription.new
end
def save
@ -85,8 +85,8 @@ class ::CustomWizard::CustomField
next
end
if attr == 'klass' && PRO_CLASSES.include?(value) && !@pro.subscribed?
add_error(I18n.t("wizard.custom_field.error.pro_type", type: value))
if attr == 'klass' && SUBSCRIPTION_CLASSES.include?(value) && !@subscription.subscribed?
add_error(I18n.t("wizard.custom_field.error.subscription_type", type: value))
end
if attr == 'serializers' && (unsupported = value - CLASSES[klass.to_sym]).length > 0
@ -100,8 +100,8 @@ class ::CustomWizard::CustomField
add_error(I18n.t("#{i18n_key}.unsupported_type", type: value))
end
if attr == 'type' && PRO_TYPES.include?(value) && !@pro.subscribed?
add_error(I18n.t("wizard.custom_field.error.pro_type", type: value))
if attr == 'type' && SUBSCRIPTION_TYPES.include?(value) && !@subscription.subscribed?
add_error(I18n.t("wizard.custom_field.error.subscription_type", type: value))
end
if attr == 'name'

Datei anzeigen

@ -47,7 +47,7 @@ class CustomWizard::Mapper
@data = params[:data] || {}
@user = params[:user]
@opts = params[:opts] || {}
@pro = CustomWizard::Pro.new
@subscription = CustomWizard::Subscription.new
end
def perform
@ -252,7 +252,7 @@ class CustomWizard::Mapper
end
end
if opts[:template] && @pro.subscribed?
if opts[:template] && @subscription.subscribed?
template = Liquid::Template.parse(string)
string = template.render(data)
end

234
lib/custom_wizard/notice.rb Normale Datei
Datei anzeigen

@ -0,0 +1,234 @@
# frozen_string_literal: true
class CustomWizard::Notice
include ActiveModel::Serialization
PLUGIN_STATUSES_TO_WARN = %w(incompatible tests_failing)
attr_reader :id,
:message,
:type,
:created_at
attr_accessor :retrieved_at,
:dismissed_at,
:expired_at
def initialize(attrs)
@id = Digest::SHA1.hexdigest(attrs[:message])
@message = attrs[:message]
@type = attrs[:type].to_i
@created_at = attrs[:created_at]
@retrieved_at = attrs[:retrieved_at]
@dismissed_at = attrs[:dismissed_at]
@expired_at = attrs[:expired_at]
end
def dismiss
if dismissable?
self.dismissed_at = Time.now
self.save
end
end
def expire
self.expired_at = Time.now
self.save
end
def expired?
expired_at.present?
end
def dismissed?
dismissed_at.present?
end
def dismissable?
true
end
def save
attrs = {
expired_at: expired_at,
created_at: created_at,
expired_at: expired_at,
message: message,
type: type
}
if current = self.class.find(self.id)
attrs[:dismissed_at] = current.dismissed_at || self.dismissed_at
end
self.class.store(id, attrs)
end
def self.types
@types ||= Enum.new(
info: 0,
warning: 1
)
end
def self.connection_types
@connection_types ||= Enum.new(
plugin_status: 0,
subscription: 1
)
end
def self.update(skip_subscription: false, skip_plugin: false)
notices = []
if !skip_subscription
subscription_messages = request(subscription_messages_url)
if subscription_messages.present?
subscription_notices = convert_subscription_messages_to_notices(subscription_messages[:messages])
notices.push(*subscription_notices)
end
end
if !skip_plugin && (Discourse.git_branch === 'tests-passed' || (Rails.env.test? || Rails.env.development?))
plugin_status = request(plugin_status_url)
if plugin_status.present? && plugin_status[:status].present? && plugin_status[:status].is_a?(Hash)
plugin_notice = convert_plugin_status_to_notice(plugin_status[:status])
notices.push(plugin_notice) if plugin_notice
expire_connection_errors(connection_types[:plugin_status])
else
create_connection_error(connection_types[:plugin_status])
end
end
notices.each do |notice_data|
notice = new(notice_data)
notice.retrieved_at = Time.now
notice.save
end
if reached_connection_error_limit(connection_types[:plugin_status])
new(
message: I18n.t("wizard.notice.plugin_status_connection_error_limit"),
type: types[:warning],
created_at: Time.now
)
end
end
def self.convert_subscription_messages_to_notices(messages)
messages.map do |message|
{
message: message[:message],
type: types[message[:type].to_sym],
created_at: message[:created_at],
expired_at: message[:expired_at]
}
end
end
def self.convert_plugin_status_to_notice(plugin_status)
notice = nil
if PLUGIN_STATUSES_TO_WARN.include?(plugin_status[:status])
notice = {
message: PrettyText.cook(I18n.t('wizard.notice.compatibility_issue', server: plugin_status_domain)),
type: types[:warning],
created_at: plugin_status[:status_changed_at]
}
else
list(types[:warning]).each(&:expire)
end
notice
end
def self.subscription_messages_domain
"localhost:3000"
end
def self.subscription_messages_url
"http://#{subscription_messages_domain}/subscription-server/messages.json"
end
def self.plugin_status_domain
"localhost:4200"
end
def self.plugin_status_url
"http://#{plugin_status_domain}/plugin-manager/status/discourse-custom-wizard"
end
def self.request(url)
response = Excon.get(url)
if response.status == 200
begin
data = JSON.parse(response.body).deep_symbolize_keys
rescue JSON::ParserError
return nil
end
data
else
nil
end
end
def self.namespace
"#{CustomWizard::PLUGIN_NAME}_notice"
end
def self.namespace_connection
"#{CustomWizard::PLUGIN_NAME}_notice_connection"
end
def self.find(id)
raw = PluginStore.get(namespace, id)
new(raw.symbolize_keys) if raw.present?
end
def self.store(id, raw_notice)
PluginStore.set(namespace, id, raw_notice)
end
def self.plugin_status_connection_error_limit
5
end
def self.list_connection_query(type)
query = PluginStoreRow.where(plugin_name: namespace_connection)
query.where("(value::json->>'type')::integer = ?", type)
end
def self.expire_connection_errors(type)
list_connection_query(type).update_all("value = jsonb_set(value::jsonb, '{ expired_at }', (to_char(current_timestamp, 'HH12:MI:SS'))::jsonb)")
end
def self.create_connection_error(type)
id = SecureRandom.hex(16)
attrs = {
message: I18n.t("wizard.notice.connection_error", domain: self.send("#{type}_domain")),
type: type,
created_at: Time.now
}
PluginStore.set(namespace_connection, id, attrs)
end
def self.reached_connection_error_limit(type)
list_connection_query(type).size >= self.send("#{connection_types.key(type)}_connection_error_limit")
end
def self.list_query(type = nil)
query = PluginStoreRow.where(plugin_name: namespace)
query = query.where("(value::json->>'expired_at') IS NULL OR (value::json->>'expired_at')::date > now()::date - 1")
query = query.where("(value::json->>'type')::integer = ?", type) if type
query.order("value::json->>'created_at' DESC")
end
def self.list(type = nil)
list_query(type)
.map { |r| self.new(JSON.parse(r.value).symbolize_keys) }
end
end

Datei anzeigen

@ -1,14 +1,14 @@
# frozen_string_literal: true
class CustomWizard::Pro
class CustomWizard::Subscription
include ActiveModel::Serialization
attr_accessor :authentication,
:subscription
def initialize
@authentication = CustomWizard::ProAuthentication.new(get_authentication)
@subscription = CustomWizard::ProSubscription.new(get_subscription)
@authentication = CustomWizard::Subscription::Authentication.new(get_authentication)
@subscription = CustomWizard::Subscription::Subscription.new(get_subscription)
end
def authorized?
@ -35,7 +35,7 @@ class CustomWizard::Pro
"discourse-subscription-server:user_subscription"
end
def update_subscription
def update
if @authentication.active?
response = Excon.get(
"https://#{server}/subscription-server/user-subscriptions/#{subscription_type}/#{client_name}",
@ -67,7 +67,7 @@ class CustomWizard::Pro
def destroy_subscription
if remove_subscription
@subscription = CustomWizard::ProSubscription.new(get_subscription)
@subscription = CustomWizard::Subscription::Subscription.new(get_subscription)
!@subscription.active?
else
false
@ -80,7 +80,7 @@ class CustomWizard::Pro
public_key: keys.public_key,
nonce: keys.nonce,
client_id: @authentication.client_id,
auth_redirect: "#{Discourse.base_url}/admin/wizards/pro/authorize/callback",
auth_redirect: "#{Discourse.base_url}/admin/wizards/subscription/authorize/callback",
application_name: SiteSetting.title,
scopes: scope
}
@ -108,7 +108,7 @@ class CustomWizard::Pro
def destroy_authentication
if remove_authentication
@authentication = CustomWizard::ProAuthentication.new(get_authentication)
@authentication = CustomWizard::Subscription::Authentication.new(get_authentication)
!@authentication.active?
else
false
@ -123,12 +123,12 @@ class CustomWizard::Pro
self.new.authorized?
end
def self.update_subscription
self.new.update_subscription
def self.update
self.new.update
end
def self.namespace
"custom_wizard_pro"
"custom_wizard_subscription"
end
private
@ -157,8 +157,8 @@ class CustomWizard::Pro
end
def set_subscription(type)
PluginStore.set(CustomWizard::Pro.namespace, subscription_db_key, type: type, updated_at: Time.now)
CustomWizard::ProSubscription.new(get_subscription)
PluginStore.set(CustomWizard::Subscription.namespace, subscription_db_key, type: type, updated_at: Time.now)
CustomWizard::Subscription::Subscription.new(get_subscription)
end
def get_authentication
@ -176,7 +176,7 @@ class CustomWizard::Pro
auth_by: user_id,
auth_at: Time.now
)
CustomWizard::ProAuthentication.new(get_authentication)
CustomWizard::Subscription::Authentication.new(get_authentication)
end
def remove_authentication

Datei anzeigen

@ -1,5 +1,5 @@
# frozen_string_literal: true
class CustomWizard::ProAuthentication
class CustomWizard::Subscription::Authentication
include ActiveModel::Serialization
attr_reader :client_id,
@ -53,7 +53,7 @@ class CustomWizard::ProAuthentication
end
def get_keys(request_id)
raw = PluginStore.get(CustomWizard::Pro.namespace, "#{keys_db_key}_#{request_id}")
raw = PluginStore.get(CustomWizard::Subscription.namespace, "#{keys_db_key}_#{request_id}")
OpenStruct.new(
user_id: raw && raw['user_id'],
pem: raw && raw['pem'],
@ -72,7 +72,7 @@ class CustomWizard::ProAuthentication
end
def set_keys(request_id, user_id, rsa, nonce)
PluginStore.set(CustomWizard::Pro.namespace, "#{keys_db_key}_#{request_id}",
PluginStore.set(CustomWizard::Subscription.namespace, "#{keys_db_key}_#{request_id}",
user_id: user_id,
pem: rsa.export,
nonce: nonce
@ -80,16 +80,16 @@ class CustomWizard::ProAuthentication
end
def delete_keys(request_id)
PluginStore.remove(CustomWizard::Pro.namespace, "#{keys_db_key}_#{request_id}")
PluginStore.remove(CustomWizard::Subscription.namespace, "#{keys_db_key}_#{request_id}")
end
def get_client_id
PluginStore.get(CustomWizard::Pro.namespace, client_id_db_key)
PluginStore.get(CustomWizard::Subscription.namespace, client_id_db_key)
end
def set_client_id
client_id = SecureRandom.hex(32)
PluginStore.set(CustomWizard::Pro.namespace, client_id_db_key, client_id)
PluginStore.set(CustomWizard::Subscription.namespace, client_id_db_key, client_id)
client_id
end
end

Datei anzeigen

@ -1,5 +1,5 @@
# frozen_string_literal: true
class CustomWizard::ProSubscription
class CustomWizard::Subscription::Subscription
include ActiveModel::Serialization
attr_reader :type,

Datei anzeigen

@ -6,7 +6,7 @@ class CustomWizard::TemplateValidator
def initialize(data, opts = {})
@data = data
@opts = opts
@pro = CustomWizard::Pro.new
@subscription = CustomWizard::Subscription.new
end
def perform
@ -15,15 +15,15 @@ class CustomWizard::TemplateValidator
check_id(data, :wizard)
check_required(data, :wizard)
validate_after_time
validate_pro(data, :wizard)
validate_subscription(data, :wizard)
data[:steps].each do |step|
check_required(step, :step)
validate_pro(step, :step)
validate_subscription(step, :step)
if step[:fields].present?
step[:fields].each do |field|
validate_pro(field, :field)
validate_subscription(field, :field)
check_required(field, :field)
end
end
@ -31,7 +31,7 @@ class CustomWizard::TemplateValidator
if data[:actions].present?
data[:actions].each do |action|
validate_pro(action, :action)
validate_subscription(action, :action)
check_required(action, :action)
end
end
@ -52,7 +52,7 @@ class CustomWizard::TemplateValidator
}
end
def self.pro
def self.subscription
{
wizard: {
save_submissions: 'false',
@ -90,18 +90,18 @@ class CustomWizard::TemplateValidator
end
end
def validate_pro(object, type)
self.class.pro[type].each do |property, pro_type|
def validate_subscription(object, type)
self.class.subscription[type].each do |property, subscription_type|
val = object[property.to_s]
is_pro = (val != nil) && (
pro_type === 'present' && val.present? ||
(['true', 'false'].include?(pro_type) && cast_bool(val) == cast_bool(pro_type)) ||
(pro_type === 'conditional' && val.is_a?(Hash)) ||
(pro_type.is_a?(Array) && pro_type.include?(val))
is_subscription = (val != nil) && (
subscription_type === 'present' && val.present? ||
(['true', 'false'].include?(subscription_type) && cast_bool(val) == cast_bool(subscription_type)) ||
(subscription_type === 'conditional' && val.is_a?(Hash)) ||
(subscription_type.is_a?(Array) && subscription_type.include?(val))
)
if is_pro && !@pro.subscribed?
errors.add :base, I18n.t("wizard.validation.pro", type: type.to_s, property: property)
if is_subscription && !@subscription.subscribed?
errors.add :base, I18n.t("wizard.validation.subscription", type: type.to_s, property: property)
end
end
end

Datei anzeigen

@ -7,9 +7,7 @@
# contact emails: angus@thepavilion.io
gem 'liquid', '5.0.1', require: true
register_asset 'stylesheets/common/wizard-admin.scss'
register_asset 'stylesheets/common/wizard-mapper.scss'
register_asset 'stylesheets/admin/admin.scss', :desktop
enabled_site_setting :custom_wizard_enabled
config = Rails.application.config
@ -42,6 +40,7 @@ if respond_to?(:register_svg_icon)
register_svg_icon "comment-alt"
register_svg_icon "far-life-ring"
register_svg_icon "arrow-right"
register_svg_icon "shield-virus"
end
class ::Sprockets::DirectiveProcessor
@ -71,13 +70,15 @@ after_initialize do
../controllers/custom_wizard/admin/logs.rb
../controllers/custom_wizard/admin/manager.rb
../controllers/custom_wizard/admin/custom_fields.rb
../controllers/custom_wizard/admin/pro.rb
../controllers/custom_wizard/admin/subscription.rb
../controllers/custom_wizard/admin/notice.rb
../controllers/custom_wizard/wizard.rb
../controllers/custom_wizard/steps.rb
../controllers/custom_wizard/realtime_validations.rb
../jobs/regular/refresh_api_access_token.rb
../jobs/regular/set_after_time_wizard.rb
../jobs/scheduled/update_pro_subscription.rb
../jobs/scheduled/custom_wizard/update_subscription.rb
../jobs/scheduled/custom_wizard/update_notices.rb
../lib/custom_wizard/validators/template.rb
../lib/custom_wizard/validators/update.rb
../lib/custom_wizard/action_result.rb
@ -96,9 +97,10 @@ after_initialize do
../lib/custom_wizard/submission.rb
../lib/custom_wizard/template.rb
../lib/custom_wizard/wizard.rb
../lib/custom_wizard/pro.rb
../lib/custom_wizard/pro/subscription.rb
../lib/custom_wizard/pro/authentication.rb
../lib/custom_wizard/notice.rb
../lib/custom_wizard/subscription.rb
../lib/custom_wizard/subscription/subscription.rb
../lib/custom_wizard/subscription/authentication.rb
../lib/custom_wizard/api/api.rb
../lib/custom_wizard/api/authorization.rb
../lib/custom_wizard/api/endpoint.rb
@ -119,9 +121,10 @@ after_initialize do
../serializers/custom_wizard/log_serializer.rb
../serializers/custom_wizard/submission_serializer.rb
../serializers/custom_wizard/realtime_validation/similar_topics_serializer.rb
../serializers/custom_wizard/pro/authentication_serializer.rb
../serializers/custom_wizard/pro/subscription_serializer.rb
../serializers/custom_wizard/pro_serializer.rb
../serializers/custom_wizard/subscription/authentication_serializer.rb
../serializers/custom_wizard/subscription/subscription_serializer.rb
../serializers/custom_wizard/subscription_serializer.rb
../serializers/custom_wizard/notice_serializer.rb
../extensions/extra_locales_controller.rb
../extensions/invites_controller.rb
../extensions/users_controller.rb
@ -238,5 +241,11 @@ after_initialize do
"#{serializer_klass}_serializer".classify.constantize.prepend CustomWizardCustomFieldSerializer
end
AdminDashboardData.add_problem_check do
warning_notices = CustomWizard::Notice.list(CustomWizard::Notice.types[:warning])
warning_notices.any? ? ActionView::Base.full_sanitizer.sanitize(warning_notices.first.message, tags: %w(a)) : nil
end
Jobs.enqueue(:custom_wizard_update_notices)
DiscourseEvent.trigger(:custom_wizard_ready)
end

Datei anzeigen

@ -0,0 +1,20 @@
# frozen_string_literal: true
class CustomWizard::NoticeSerializer < ApplicationSerializer
attributes :id,
:message,
:type,
:created_at,
:expired_at,
:dismissed_at,
:retrieved_at,
:dismissable
def dismissable
object.dismissable?
end
def type
CustomWizard::Notice.types.key(object.type)
end
end

Datei anzeigen

@ -1,6 +0,0 @@
# frozen_string_literal: true
class CustomWizard::ProSerializer < ApplicationSerializer
attributes :server
has_one :authentication, serializer: CustomWizard::ProAuthenticationSerializer, embed: :objects
has_one :subscription, serializer: CustomWizard::ProSubscriptionSerializer, embed: :objects
end

Datei anzeigen

@ -1,5 +1,5 @@
# frozen_string_literal: true
class CustomWizard::ProAuthenticationSerializer < ApplicationSerializer
class CustomWizard::Subscription::AuthenticationSerializer < ApplicationSerializer
attributes :active,
:client_id,
:auth_by,

Datei anzeigen

@ -1,5 +1,5 @@
# frozen_string_literal: true
class CustomWizard::ProSubscriptionSerializer < ApplicationSerializer
class CustomWizard::Subscription::SubscriptionSerializer < ApplicationSerializer
attributes :type,
:active,
:updated_at

Datei anzeigen

@ -0,0 +1,6 @@
# frozen_string_literal: true
class CustomWizard::SubscriptionSerializer < ApplicationSerializer
attributes :server
has_one :authentication, serializer: CustomWizard::Subscription::AuthenticationSerializer, embed: :objects
has_one :subscription, serializer: CustomWizard::Subscription::SubscriptionSerializer, embed: :objects
end

Datei anzeigen

@ -123,6 +123,6 @@ class CustomWizard::FieldSerializer < ::ApplicationSerializer
end
def subscribed?
@subscribed ||= CustomWizard::Pro.subscribed?
@subscribed ||= CustomWizard::Subscription.subscribed?
end
end

Datei anzeigen

@ -10,7 +10,7 @@ class CustomWizard::WizardSerializer < CustomWizard::BasicWizardSerializer
:permitted,
:uncategorized_category_id,
:categories,
:pro_subscribed
:subscribed
has_many :steps, serializer: ::CustomWizard::StepSerializer, embed: :objects
has_one :user, serializer: ::BasicUserSerializer, embed: :objects
@ -62,7 +62,7 @@ class CustomWizard::WizardSerializer < CustomWizard::BasicWizardSerializer
object.categories.map { |c| c.to_h }
end
def pro_subscribed
CustomWizard::Pro.subscribed?
def subscribed
CustomWizard::Subscription.subscribed?
end
end

Datei anzeigen

@ -80,8 +80,4 @@ class CustomWizard::StepSerializer < ::ApplicationSerializer
def i18n_key
@i18n_key ||= "#{object.wizard.id}.#{object.id}".underscore
end
def subscribed?
@subscribed ||= CustomWizard::Pro.subscribed?
end
end

Datei anzeigen

@ -174,7 +174,7 @@ describe CustomWizard::Action do
expect(updater.result[:redirect_on_next]).to eq("https://google.com")
end
context "pro actions" do
context "subscription actions" do
before do
enable_subscription
end

Datei anzeigen

@ -4,7 +4,7 @@ require_relative '../../plugin_helper'
describe CustomWizard::CustomField do
let(:custom_field_json) { get_wizard_fixture("custom_field/custom_fields") }
let(:custom_field_pro_json) { get_wizard_fixture("custom_field/pro_custom_fields") }
let(:custom_field_subscription_json) { get_wizard_fixture("custom_field/subscription_custom_fields") }
before do
CustomWizard::CustomField.invalidate_cache
@ -193,44 +193,44 @@ describe CustomWizard::CustomField do
).to eq(false)
end
it "does not save pro field types without a pro subscription" do
pro_field_json = custom_field_pro_json['custom_fields'].first
custom_field = CustomWizard::CustomField.new(nil, pro_field_json)
it "does not save subscription field types without a subscription" do
subscription_field_json = custom_field_subscription_json['custom_fields'].first
custom_field = CustomWizard::CustomField.new(nil, subscription_field_json)
expect(custom_field.save).to eq(false)
expect(custom_field.valid?).to eq(false)
expect(custom_field.errors.full_messages.first).to eq(
I18n.t("wizard.custom_field.error.pro_type", type: "json")
I18n.t("wizard.custom_field.error.subscription_type", type: "json")
)
end
it "does not save pro field classes without a pro subscription" do
pro_field_json = custom_field_pro_json['custom_fields'].second
custom_field = CustomWizard::CustomField.new(nil, pro_field_json)
it "does not save subscription field classes without a subscription" do
subscription_field_json = custom_field_subscription_json['custom_fields'].second
custom_field = CustomWizard::CustomField.new(nil, subscription_field_json)
expect(custom_field.save).to eq(false)
expect(custom_field.valid?).to eq(false)
expect(custom_field.errors.full_messages.first).to eq(
I18n.t("wizard.custom_field.error.pro_type", type: "category")
I18n.t("wizard.custom_field.error.subscription_type", type: "category")
)
end
context "with a pro subscription" do
context "with a subscription" do
before do
enable_subscription
end
it "saves pro field types" do
pro_field_json = custom_field_pro_json['custom_fields'].first
custom_field = CustomWizard::CustomField.new(nil, pro_field_json)
it "saves subscription field types" do
subscription_field_json = custom_field_subscription_json['custom_fields'].first
custom_field = CustomWizard::CustomField.new(nil, subscription_field_json)
expect(custom_field.save).to eq(true)
expect(custom_field.valid?).to eq(true)
end
it "saves pro field classes" do
pro_field_json = custom_field_pro_json['custom_fields'].second
custom_field = CustomWizard::CustomField.new(nil, pro_field_json)
it "saves subscription field classes" do
subscription_field_json = custom_field_subscription_json['custom_fields'].second
custom_field = CustomWizard::CustomField.new(nil, subscription_field_json)
expect(custom_field.save).to eq(true)
expect(custom_field.valid?).to eq(true)

Datei anzeigen

@ -344,7 +344,7 @@ describe CustomWizard::Mapper do
expect(result).to eq(template_params["step_1_field_1"])
end
it "requires a pro subscription" do
it "requires a subscription" do
template = '{{ "w{step_1_field_1}" | size }}'
mapper = create_template_mapper(template_params, user1)
result = mapper.interpolate(
@ -357,7 +357,7 @@ describe CustomWizard::Mapper do
expect(result).to eq("{{ \"#{template_params["step_1_field_1"]}\" | size }}")
end
context "with a pro subscription" do
context "with a subscription" do
before do
enable_subscription
end

Datei anzeigen

@ -0,0 +1,80 @@
# frozen_string_literal: true
require_relative '../../plugin_helper'
describe CustomWizard::Notice do
fab!(:user) { Fabricate(:user) }
let(:subscription_message) {
{
message: "Message about subscription",
type: "info",
created_at: Time.now - 3.day,
expired_at: nil
}
}
let(:plugin_status) {
{
name: 'discourse-custom-wizard',
status: 'incompatible',
status_changed_at: Time.now - 3.day
}
}
context "subscription message" do
before do
freeze_time
stub_request(:get, described_class.subscription_messages_url).to_return(status: 200, body: { messages: [subscription_message] }.to_json)
described_class.update(skip_plugin: true)
end
it "converts subscription messages into notices" do
notice = described_class.list.first
expect(notice.type).to eq(described_class.types[:info])
expect(notice.message).to eq(subscription_message[:message])
expect(notice.created_at.to_datetime).to be_within(1.second).of (subscription_message[:created_at].to_datetime)
end
it "expires notice if subscription message is expired" do
subscription_message[:expired_at] = Time.now
stub_request(:get, described_class.subscription_messages_url).to_return(status: 200, body: { messages: [subscription_message] }.to_json)
described_class.update(skip_plugin: true)
notice = described_class.list.first
expect(notice.expired?).to eq(true)
end
end
context "plugin status" do
before do
freeze_time
stub_request(:get, described_class.plugin_status_url).to_return(status: 200, body: { status: plugin_status }.to_json)
described_class.update(skip_subscription: true)
end
it "converts plugin statuses to warn into notices" do
notice = described_class.list.first
expect(notice.type).to eq(described_class.types[:warning])
expect(notice.message).to eq(PrettyText.cook(I18n.t("wizard.notice.compatibility_issue", server: described_class.plugin_status_domain)))
expect(notice.created_at.to_datetime).to be_within(1.second).of (plugin_status[:status_changed_at].to_datetime)
end
it "expires unexpired warning notices if status is recommended or compatible" do
plugin_status[:status] = 'compatible'
plugin_status[:status_changed_at] = Time.now
stub_request(:get, described_class.plugin_status_url).to_return(status: 200, body: { status: plugin_status }.to_json)
described_class.update(skip_subscription: true)
notice = described_class.list(described_class.types[:warning]).first
expect(notice.expired?).to eq(true)
end
end
it "lists notices not expired more than a day ago" do
subscription_message[:expired_at] = Time.now - 8.hours
stub_request(:get, described_class.subscription_messages_url).to_return(status: 200, body: { messages: [subscription_message] }.to_json)
stub_request(:get, described_class.plugin_status_url).to_return(status: 200, body: { status: plugin_status }.to_json)
described_class.update
expect(described_class.list.length).to eq(2)
end
end

Datei anzeigen

@ -2,69 +2,69 @@
require_relative '../../plugin_helper'
describe CustomWizard::Pro do
describe CustomWizard::Subscription do
fab!(:user) { Fabricate(:user) }
it "initializes pro authentication and subscription" do
pro = described_class.new
expect(pro.authentication.class).to eq(CustomWizard::ProAuthentication)
expect(pro.subscription.class).to eq(CustomWizard::ProSubscription)
it "initializes subscription authentication and subscription" do
subscription = described_class.new
expect(subscription.authentication.class).to eq(CustomWizard::Subscription::Authentication)
expect(subscription.subscription.class).to eq(CustomWizard::Subscription::Subscription)
end
it "returns authorized and subscribed states" do
pro = described_class.new
expect(pro.authorized?).to eq(false)
expect(pro.subscribed?).to eq(false)
subscription = described_class.new
expect(subscription.authorized?).to eq(false)
expect(subscription.subscribed?).to eq(false)
end
context "subscription" do
before do
@pro = described_class.new
@subscription = described_class.new
end
it "updates valid subscriptions" do
stub_subscription_request(200, valid_subscription)
expect(@pro.update_subscription).to eq(true)
expect(@pro.subscribed?).to eq(true)
expect(@subscription.update).to eq(true)
expect(@subscription.subscribed?).to eq(true)
end
it "handles invalid subscriptions" do
stub_subscription_request(200, invalid_subscription)
expect(@pro.update_subscription).to eq(false)
expect(@pro.subscribed?).to eq(false)
expect(@subscription.update).to eq(false)
expect(@subscription.subscribed?).to eq(false)
end
it "handles subscription http errors" do
stub_subscription_request(404, {})
expect(@pro.update_subscription).to eq(false)
expect(@pro.subscribed?).to eq(false)
expect(@subscription.update).to eq(false)
expect(@subscription.subscribed?).to eq(false)
end
it "destroys subscriptions" do
stub_subscription_request(200, valid_subscription)
expect(@pro.update_subscription).to eq(true)
expect(@pro.destroy_subscription).to eq(true)
expect(@pro.subscribed?).to eq(false)
expect(@subscription.update).to eq(true)
expect(@subscription.destroy_subscription).to eq(true)
expect(@subscription.subscribed?).to eq(false)
end
it "has class aliases" do
authenticate_pro
authenticate_subscription
stub_subscription_request(200, valid_subscription)
expect(described_class.update_subscription).to eq(true)
expect(described_class.update).to eq(true)
expect(described_class.subscribed?).to eq(true)
end
end
context "authentication" do
before do
@pro = described_class.new
@subscription = described_class.new
user.update!(admin: true)
end
it "generates a valid authentication request url" do
request_id = SecureRandom.hex(32)
uri = URI(@pro.authentication_url(user.id, request_id))
expect(uri.host).to eq(@pro.server)
uri = URI(@subscription.authentication_url(user.id, request_id))
expect(uri.host).to eq(@subscription.server)
parsed_query = Rack::Utils.parse_query uri.query
expect(parsed_query['public_key'].present?).to eq(true)
@ -72,12 +72,12 @@ describe CustomWizard::Pro do
expect(parsed_query['client_id'].present?).to eq(true)
expect(parsed_query['auth_redirect'].present?).to eq(true)
expect(parsed_query['application_name']).to eq(SiteSetting.title)
expect(parsed_query['scopes']).to eq(@pro.scope)
expect(parsed_query['scopes']).to eq(@subscription.scope)
end
def generate_payload(request_id, user_id)
uri = URI(@pro.authentication_url(user_id, request_id))
keys = @pro.authentication.get_keys(request_id)
uri = URI(@subscription.authentication_url(user_id, request_id))
keys = @subscription.authentication.get_keys(request_id)
raw_payload = {
key: "12345",
nonce: keys.nonce,
@ -92,8 +92,8 @@ describe CustomWizard::Pro do
request_id = SecureRandom.hex(32)
payload = generate_payload(request_id, user.id)
expect(@pro.authentication_response(request_id, payload)).to eq(true)
expect(@pro.authorized?).to eq(true)
expect(@subscription.authentication_response(request_id, payload)).to eq(true)
expect(@subscription.authorized?).to eq(true)
end
it "discards authentication response if user who made request as not an admin" do
@ -102,24 +102,24 @@ describe CustomWizard::Pro do
request_id = SecureRandom.hex(32)
payload = generate_payload(request_id, user.id)
expect(@pro.authentication_response(request_id, payload)).to eq(false)
expect(@pro.authorized?).to eq(false)
expect(@subscription.authentication_response(request_id, payload)).to eq(false)
expect(@subscription.authorized?).to eq(false)
end
it "discards authentication response if request_id is invalid" do
payload = generate_payload(SecureRandom.hex(32), user.id)
expect(@pro.authentication_response(SecureRandom.hex(32), payload)).to eq(false)
expect(@pro.authorized?).to eq(false)
expect(@subscription.authentication_response(SecureRandom.hex(32), payload)).to eq(false)
expect(@subscription.authorized?).to eq(false)
end
it "destroys authentication" do
request_id = SecureRandom.hex(32)
payload = generate_payload(request_id, user.id)
@pro.authentication_response(request_id, payload)
@subscription.authentication_response(request_id, payload)
expect(@pro.destroy_authentication).to eq(true)
expect(@pro.authorized?).to eq(false)
expect(@subscription.destroy_authentication).to eq(true)
expect(@subscription.authorized?).to eq(false)
end
end
end

Datei anzeigen

@ -10,7 +10,7 @@ describe "custom field extensions" do
fab!(:user) { Fabricate(:user) }
let(:custom_field_json) { get_wizard_fixture("custom_field/custom_fields") }
let(:pro_custom_field_json) { get_wizard_fixture("custom_field/pro_custom_fields") }
let(:subscription_custom_field_json) { get_wizard_fixture("custom_field/subscription_custom_fields") }
before do
custom_field_json['custom_fields'].each do |field_json|
@ -72,11 +72,11 @@ describe "custom field extensions" do
end
end
context "pro custom fields" do
context "subscription custom fields" do
before do
enable_subscription
pro_custom_field_json['custom_fields'].each do |field_json|
subscription_custom_field_json['custom_fields'].each do |field_json|
custom_field = CustomWizard::CustomField.new(nil, field_json)
custom_field.save
end

Datei anzeigen

@ -0,0 +1,29 @@
# frozen_string_literal: true
require_relative '../plugin_helper'
describe Jobs::CustomWizardUpdateNotices do
let(:subscription_message) {
{
message: "Message about subscription",
type: "info",
created_at: Time.now - 3.day,
expired_at: nil
}
}
let(:plugin_status) {
{
name: 'discourse-custom-wizard',
status: 'incompatible',
status_changed_at: Time.now - 3.day
}
}
it "updates the notices" do
stub_request(:get, CustomWizard::Notice.subscription_messages_url).to_return(status: 200, body: { messages: [subscription_message] }.to_json)
stub_request(:get, CustomWizard::Notice.plugin_status_url).to_return(status: 200, body: { status: plugin_status }.to_json)
described_class.new.execute
expect(CustomWizard::Notice.list.length).to eq(2)
end
end

Datei anzeigen

@ -2,10 +2,10 @@
require_relative '../plugin_helper'
describe CustomWizard::UpdateProSubscription do
it "updates the pro subscription" do
describe Jobs::CustomWizardUpdateSubscription do
it "updates the subscription" do
stub_subscription_request(200, valid_subscription)
described_class.new.execute
expect(CustomWizard::Pro.subscribed?).to eq(true)
expect(CustomWizard::Subscription.subscribed?).to eq(true)
end
end

Datei anzeigen

@ -24,16 +24,16 @@ def get_wizard_fixture(path)
).with_indifferent_access
end
def authenticate_pro
CustomWizard::ProAuthentication.any_instance.stubs(:active?).returns(true)
def authenticate_subscription
CustomWizard::Subscription::Authentication.any_instance.stubs(:active?).returns(true)
end
def enable_subscription
CustomWizard::Pro.any_instance.stubs(:subscribed?).returns(true)
CustomWizard::Subscription.any_instance.stubs(:subscribed?).returns(true)
end
def disable_pro
CustomWizard::Pro.any_instance.stubs(:subscribed?).returns(false)
def disable_subscription
CustomWizard::Subscription.any_instance.stubs(:subscribed?).returns(false)
end
def valid_subscription
@ -52,7 +52,7 @@ def invalid_subscription
end
def stub_subscription_request(status, subscription)
authenticate_pro
pro = CustomWizard::Pro.new
stub_request(:get, "https://#{pro.server}/subscription-server/user-subscriptions/#{pro.subscription_type}/#{pro.client_name}").to_return(status: status, body: { subscriptions: [subscription] }.to_json)
authenticate_subscription
sub = CustomWizard::Subscription.new
stub_request(:get, "https://#{sub.server}/subscription-server/user-subscriptions/#{sub.subscription_type}/#{sub.client_name}").to_return(status: status, body: { subscriptions: [subscription] }.to_json)
end

Datei anzeigen

@ -0,0 +1,31 @@
# frozen_string_literal: true
require_relative '../../../plugin_helper'
describe CustomWizard::AdminNoticeController do
fab!(:admin_user) { Fabricate(:user, admin: true) }
before do
sign_in(admin_user)
@notice = CustomWizard::Notice.new(
message: "Message about subscription",
type: "info",
created_at: Time.now - 3.day,
expired_at: nil
)
@notice.save
end
it "lists notices" do
get "/admin/wizards/notice.json"
expect(response.status).to eq(200)
expect(response.parsed_body.length).to eq(1)
end
it "dismisses notices" do
put "/admin/wizards/notice/#{@notice.id}.json"
expect(response.status).to eq(200)
updated = CustomWizard::Notice.find(@notice.id)
expect(updated.dismissed?).to eq(true)
end
end

Datei anzeigen

@ -1,12 +1,12 @@
# frozen_string_literal: true
require_relative '../../../plugin_helper'
describe CustomWizard::AdminProController do
describe CustomWizard::AdminSubscriptionController do
fab!(:admin_user) { Fabricate(:user, admin: true) }
def generate_payload(request_id, user_id)
uri = URI(@pro.authentication_url(user_id, request_id))
keys = @pro.authentication.get_keys(request_id)
uri = URI(@subscription.authentication_url(user_id, request_id))
keys = @subscription.authentication.get_keys(request_id)
raw_payload = {
key: "12345",
nonce: keys.nonce,
@ -18,19 +18,19 @@ describe CustomWizard::AdminProController do
end
before do
@pro = CustomWizard::Pro.new
@subscription = CustomWizard::Subscription.new
sign_in(admin_user)
end
it "#index" do
get "/admin/wizards/pro.json"
expect(response.parsed_body['server']).to eq(@pro.server)
expect(response.parsed_body['authentication'].deep_symbolize_keys).to eq(CustomWizard::ProAuthenticationSerializer.new(@pro.authentication, root: false).as_json)
expect(response.parsed_body['subscription'].deep_symbolize_keys).to eq(CustomWizard::ProSubscriptionSerializer.new(@pro.subscription, root: false).as_json)
get "/admin/wizards/subscription.json"
expect(response.parsed_body['server']).to eq(@subscription.server)
expect(response.parsed_body['authentication'].deep_symbolize_keys).to eq(CustomWizard::Subscription::AuthenticationSerializer.new(@subscription.authentication, root: false).as_json)
expect(response.parsed_body['subscription'].deep_symbolize_keys).to eq(CustomWizard::Subscription::SubscriptionSerializer.new(@subscription.subscription, root: false).as_json)
end
it "#authorize" do
get "/admin/wizards/pro/authorize"
get "/admin/wizards/subscription/authorize"
expect(response.status).to eq(302)
expect(cookies[:user_api_request_id].present?).to eq(true)
end
@ -38,12 +38,12 @@ describe CustomWizard::AdminProController do
it "#destroy_authentication" do
request_id = SecureRandom.hex(32)
payload = generate_payload(request_id, admin_user.id)
@pro.authentication_response(request_id, payload)
@subscription.authentication_response(request_id, payload)
delete "/admin/wizards/pro/authorize.json"
delete "/admin/wizards/subscription/authorize.json"
expect(response.status).to eq(200)
expect(CustomWizard::Pro.authorized?).to eq(false)
expect(CustomWizard::Subscription.authorized?).to eq(false)
end
context "subscription" do
@ -54,18 +54,18 @@ describe CustomWizard::AdminProController do
it "handles authentication response and the updates subscription" do
request_id = cookies[:user_api_request_id] = SecureRandom.hex(32)
payload = generate_payload(request_id, admin_user.id)
get "/admin/wizards/pro/authorize/callback", params: { payload: payload }
get "/admin/wizards/subscription/authorize/callback", params: { payload: payload }
expect(response).to redirect_to("/admin/wizards/pro")
expect(CustomWizard::Pro.subscribed?).to eq(true)
expect(response).to redirect_to("/admin/wizards/subscription")
expect(CustomWizard::Subscription.subscribed?).to eq(true)
end
it "updates the subscription" do
authenticate_pro
post "/admin/wizards/pro/subscription.json"
authenticate_subscription
post "/admin/wizards/subscription.json"
expect(response.status).to eq(200)
expect(CustomWizard::Pro.subscribed?).to eq(true)
expect(CustomWizard::Subscription.subscribed?).to eq(true)
end
end
end

Datei anzeigen

@ -9,7 +9,7 @@ describe "custom field extensions" do
let!(:user) { Fabricate(:user) }
let!(:group) { Fabricate(:group, users: [user]) }
let(:custom_field_json) { get_wizard_fixture("custom_field/custom_fields") }
let(:pro_custom_field_json) { get_wizard_fixture("custom_field/pro_custom_fields") }
let(:subscription_custom_field_json) { get_wizard_fixture("custom_field/subscription_custom_fields") }
before do
custom_field_json['custom_fields'].each do |field_json|
@ -38,11 +38,11 @@ describe "custom field extensions" do
expect(response.parsed_body['post_field_1']).to eq(7)
end
context "with a pro subscription" do
context "with a subscription" do
before do
enable_subscription
pro_custom_field_json['custom_fields'].each do |field_json|
subscription_custom_field_json['custom_fields'].each do |field_json|
custom_field = CustomWizard::CustomField.new(nil, field_json)
custom_field.save
end

Datei anzeigen

@ -119,7 +119,7 @@ describe CustomWizard::StepsController do
expect(response.parsed_body['final']).to eq(true)
end
context "pro" do
context "subscription" do
before do
enable_subscription
end
@ -149,6 +149,38 @@ describe CustomWizard::StepsController do
expect(response.parsed_body['wizard']['start']).to eq("step_3")
end
it "returns the correct final step when the conditional final step and last step are the same" do
new_template = wizard_template.dup
new_template['steps'][0]['condition'] = user_condition_template['condition']
new_template['steps'][2]['condition'] = wizard_field_condition_template['condition']
CustomWizard::Template.save(new_template, skip_jobs: true)
end
it "raises an error when user cant see the step due to conditions" do
sign_in(user2)
new_wizard_template = wizard_template.dup
new_wizard_template['steps'][0]['condition'] = user_condition_template['condition']
CustomWizard::Template.save(new_wizard_template, skip_jobs: true)
put '/w/super-mega-fun-wizard/steps/step_1.json'
expect(response.status).to eq(403)
end
it "returns an updated wizard when condition doesnt pass" do
new_template = wizard_template.dup
new_template['steps'][1]['condition'] = wizard_field_condition_template['condition']
CustomWizard::Template.save(new_template, skip_jobs: true)
put '/w/super-mega-fun-wizard/steps/step_1.json', params: {
fields: {
step_1_field_1: "Condition wont pass"
}
}
expect(response.status).to eq(200)
expect(response.parsed_body['wizard']['start']).to eq("step_3")
end
it "returns the correct final step when the conditional final step and last step are the same" do
new_template = wizard_template.dup
new_template['steps'][0]['condition'] = user_condition_template['condition']

Datei anzeigen

@ -0,0 +1,22 @@
# frozen_string_literal: true
require_relative '../../plugin_helper'
describe CustomWizard::NoticeSerializer do
before do
@notice = CustomWizard::Notice.new(
message: "Message about subscription",
type: "info",
created_at: Time.now - 3.day,
expired_at: nil
)
@notice.save
end
it 'should return notice attributes' do
serialized_notice = described_class.new(@notice)
expect(serialized_notice.message).to eq(@notice.message)
expect(serialized_notice.type).to eq(CustomWizard::Notice.types.key(@notice.type))
expect(serialized_notice.dismissable).to eq(true)
end
end

Datei anzeigen

@ -1,14 +0,0 @@
# frozen_string_literal: true
require_relative '../../plugin_helper'
describe CustomWizard::ProSerializer do
it 'should return pro attributes' do
pro = CustomWizard::Pro.new
serialized = described_class.new(pro, root: false)
expect(serialized.server).to eq(pro.server)
expect(serialized.authentication.class).to eq(CustomWizard::ProAuthentication)
expect(serialized.subscription.class).to eq(CustomWizard::ProSubscription)
end
end

Datei anzeigen

@ -2,11 +2,11 @@
require_relative '../../../plugin_helper'
describe CustomWizard::ProAuthenticationSerializer do
describe CustomWizard::Subscription::AuthenticationSerializer do
fab!(:user) { Fabricate(:user) }
it 'should return pro authentication attributes' do
auth = CustomWizard::ProAuthentication.new(OpenStruct.new(key: '1234', auth_at: Time.now, auth_by: user.id))
it 'should return subscription authentication attributes' do
auth = CustomWizard::Subscription::Authentication.new(OpenStruct.new(key: '1234', auth_at: Time.now, auth_by: user.id))
serialized = described_class.new(auth, root: false).as_json
expect(serialized[:active]).to eq(true)

Datei anzeigen

@ -2,9 +2,9 @@
require_relative '../../../plugin_helper'
describe CustomWizard::ProSubscriptionSerializer do
it 'should return pro subscription attributes' do
sub = CustomWizard::ProSubscription.new(OpenStruct.new(type: 'community', updated_at: Time.now))
describe CustomWizard::Subscription::SubscriptionSerializer do
it 'should return subscription attributes' do
sub = CustomWizard::Subscription::Subscription.new(OpenStruct.new(type: 'community', updated_at: Time.now))
serialized = described_class.new(sub, root: false).as_json
expect(serialized[:active]).to eq(true)

Datei anzeigen

@ -0,0 +1,14 @@
# frozen_string_literal: true
require_relative '../../plugin_helper'
describe CustomWizard::SubscriptionSerializer do
it 'should return subscription attributes' do
subscription = CustomWizard::Subscription.new
serialized = described_class.new(subscription, root: false)
expect(serialized.server).to eq(subscription.server)
expect(serialized.authentication.class).to eq(CustomWizard::Subscription::Authentication)
expect(serialized.subscription.class).to eq(CustomWizard::Subscription::Subscription)
end
end