Remove subs and notices files
Dieser Commit ist enthalten in:
Ursprung
c0b93fc166
Commit
5edfb4c41e
40 geänderte Dateien mit 17 neuen und 1826 gelöschten Zeilen
|
@ -4,16 +4,7 @@ class CustomWizard::AdminController < ::Admin::AdminController
|
||||||
|
|
||||||
def index
|
def index
|
||||||
render_json_dump(
|
render_json_dump(
|
||||||
#TODO replace with appropriate static?
|
api_section: ["business"].include?(CustomWizard::Subscription.type)
|
||||||
api_section: ["business"].include?(CustomWizard::Subscription.type),
|
|
||||||
active_notice_count: CustomWizard::Notice.active_count,
|
|
||||||
featured_notices: ActiveModel::ArraySerializer.new(
|
|
||||||
CustomWizard::Notice.list(
|
|
||||||
type: CustomWizard::Notice.types[:info],
|
|
||||||
archetype: CustomWizard::Notice.archetypes[:subscription_message]
|
|
||||||
),
|
|
||||||
each_serializer: CustomWizard::NoticeSerializer
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
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
|
|
|
@ -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
|
||||||
|
@ -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
|
|
|
@ -1,25 +0,0 @@
|
||||||
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`;
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -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);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -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,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,6 +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 { isPresent } from "@ember/utils";
|
||||||
import { A } from "@ember/array";
|
import { A } from "@ember/array";
|
||||||
import getUrl from "discourse-common/lib/get-url";
|
import getUrl from "discourse-common/lib/get-url";
|
||||||
|
@ -23,50 +22,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: {
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -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;
|
|
|
@ -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();
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -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>
|
|
|
@ -7,15 +7,8 @@
|
||||||
{{/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">
|
|
||||||
{{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>
|
||||||
|
|
|
@ -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>
|
|
|
@ -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,6 +1,7 @@
|
||||||
// discourse-skip-module
|
// discourse-skip-module
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", function () {
|
if (window.location.pathname.indexOf("/w/") > -1 && Ember.testing) {
|
||||||
|
document.addEventListener("DOMContentLoaded", function () {
|
||||||
document.body.insertAdjacentHTML(
|
document.body.insertAdjacentHTML(
|
||||||
"afterbegin",
|
"afterbegin",
|
||||||
`
|
`
|
||||||
|
@ -8,10 +9,11 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||||
<style>#ember-testing-container { position: absolute; background: white; bottom: 0; right: 0; width: 640px; height: 384px; overflow: auto; z-index: 9999; border: 1px solid #ccc; } #ember-testing { zoom: 50%; }</style>
|
<style>#ember-testing-container { position: absolute; background: white; bottom: 0; right: 0; width: 640px; height: 384px; overflow: auto; z-index: 9999; border: 1px solid #ccc; } #ember-testing { zoom: 50%; }</style>
|
||||||
`
|
`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
Object.keys(requirejs.entries).forEach(function (entry) {
|
Object.keys(requirejs.entries).forEach(function (entry) {
|
||||||
if (/\-test/.test(entry)) {
|
if (/\-test/.test(entry)) {
|
||||||
requirejs(entry);
|
requirejs(entry);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
|
@ -54,20 +54,6 @@ en:
|
||||||
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}"
|
||||||
|
|
||||||
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."
|
||||||
wizard_redirect_exclude_paths: "Routes excluded from wizard redirects."
|
wizard_redirect_exclude_paths: "Routes excluded from wizard redirects."
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,8 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
class CustomWizard::Subscription
|
class CustomWizard::Subscription
|
||||||
include ActiveModel::Serialization
|
|
||||||
|
|
||||||
attr_accessor :authentication,
|
|
||||||
:subscription
|
|
||||||
|
|
||||||
SUBSCRIPTION_LEVELS = {
|
|
||||||
standard: {
|
|
||||||
actions: ["send_message", "add_to_group", "watch_categories"],
|
|
||||||
custom_fields: {
|
|
||||||
klass: [],
|
|
||||||
type: ["json"],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
business: {
|
|
||||||
actions: ["create_category", "create_group", "send_to_api"],
|
|
||||||
custom_fields: {
|
|
||||||
klass: ["group", "category"],
|
|
||||||
type: [],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
def initialize
|
|
||||||
@authentication = CustomWizard::Subscription::Authentication.new(get_authentication)
|
|
||||||
@subscription = CustomWizard::Subscription::Subscription.new(get_subscription)
|
|
||||||
end
|
|
||||||
|
|
||||||
def authorized?
|
|
||||||
@authentication.active?
|
|
||||||
end
|
|
||||||
|
|
||||||
def subscribed?
|
|
||||||
@subscription.active?
|
|
||||||
end
|
|
||||||
|
|
||||||
def server
|
|
||||||
"test.thepavilion.io"
|
|
||||||
end
|
|
||||||
|
|
||||||
def subscription_type
|
|
||||||
"stripe"
|
|
||||||
end
|
|
||||||
|
|
||||||
def type
|
|
||||||
@subscription.type
|
|
||||||
end
|
|
||||||
|
|
||||||
def client_name
|
|
||||||
"custom-wizard"
|
|
||||||
end
|
|
||||||
|
|
||||||
def scope
|
|
||||||
"discourse-subscription-server:user_subscription"
|
|
||||||
end
|
|
||||||
|
|
||||||
def requires_additional_subscription(kategory, sub_kategory)
|
|
||||||
case kategory
|
|
||||||
when "actions"
|
|
||||||
case self.type
|
|
||||||
when "business"
|
|
||||||
[]
|
|
||||||
when "standard"
|
|
||||||
SUBSCRIPTION_LEVELS[:business][kategory.to_sym]
|
|
||||||
else
|
|
||||||
SUBSCRIPTION_LEVELS[:standard][kategory.to_sym] + SUBSCRIPTION_LEVELS[:business][kategory.to_sym]
|
|
||||||
end
|
|
||||||
when "custom_fields"
|
|
||||||
case self.type
|
|
||||||
when "business"
|
|
||||||
[]
|
|
||||||
when "standard"
|
|
||||||
SUBSCRIPTION_LEVELS[:business][kategory.to_sym][sub_kategory.to_sym]
|
|
||||||
else
|
|
||||||
SUBSCRIPTION_LEVELS[:standard][kategory.to_sym][sub_kategory.to_sym] + SUBSCRIPTION_LEVELS[:business][kategory.to_sym][sub_kategory.to_sym]
|
|
||||||
end
|
|
||||||
else
|
|
||||||
[]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def update
|
|
||||||
if @authentication.active?
|
|
||||||
response = Excon.get(
|
|
||||||
"https://#{server}/subscription-server/user-subscriptions/#{subscription_type}/#{client_name}",
|
|
||||||
headers: {
|
|
||||||
"User-Api-Key" => @authentication.api_key
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
if response.status == 200
|
|
||||||
begin
|
|
||||||
data = JSON.parse(response.body).deep_symbolize_keys
|
|
||||||
rescue JSON::ParserError
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
return false unless data && data.is_a?(Hash)
|
|
||||||
subscriptions = data[:subscriptions]
|
|
||||||
|
|
||||||
if subscriptions.present? && type = subscriptions.first[:price_nickname]
|
|
||||||
@subscription = set_subscription(type)
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
destroy_subscription
|
|
||||||
false
|
|
||||||
end
|
|
||||||
|
|
||||||
def destroy_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
|
|
||||||
|
|
||||||
def self.subscribed?
|
def self.subscribed?
|
||||||
self.new.subscribed?
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.type
|
def self.type
|
||||||
self.new.type
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.requires_additional_subscription(kategory, sub_kategory)
|
|
||||||
self.new.requires_additional_subscription(kategory, sub_kategory)
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.authorized?
|
|
||||||
self.new.authorized?
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.update
|
|
||||||
self.new.update
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.namespace
|
|
||||||
"custom_wizard_subscription"
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def subscription_db_key
|
|
||||||
"subscription"
|
|
||||||
end
|
|
||||||
|
|
||||||
def authentication_db_key
|
|
||||||
"authentication"
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_subscription
|
|
||||||
raw = PluginStore.get(self.class.namespace, subscription_db_key)
|
|
||||||
|
|
||||||
if raw.present?
|
|
||||||
OpenStruct.new(
|
|
||||||
type: raw['type'],
|
|
||||||
updated_at: raw['updated_at']
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def remove_subscription
|
|
||||||
PluginStore.remove(self.class.namespace, subscription_db_key)
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_subscription(type)
|
|
||||||
PluginStore.set(CustomWizard::Subscription.namespace, subscription_db_key, type: type, updated_at: Time.now)
|
|
||||||
CustomWizard::Subscription::Subscription.new(get_subscription)
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_authentication
|
|
||||||
raw = PluginStore.get(self.class.namespace, authentication_db_key)
|
|
||||||
OpenStruct.new(
|
|
||||||
key: raw && raw['key'],
|
|
||||||
auth_by: raw && raw['auth_by'],
|
|
||||||
auth_at: raw && raw['auth_at']
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_authentication(key, user_id)
|
|
||||||
PluginStore.set(self.class.namespace, authentication_db_key,
|
|
||||||
key: key,
|
|
||||||
auth_by: user_id,
|
|
||||||
auth_at: Time.now
|
|
||||||
)
|
|
||||||
CustomWizard::Subscription::Authentication.new(get_authentication)
|
|
||||||
end
|
|
||||||
|
|
||||||
def remove_authentication
|
|
||||||
PluginStore.remove(self.class.namespace, authentication_db_key)
|
|
||||||
get_authentication
|
|
||||||
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
|
|
21
plugin.rb
21
plugin.rb
|
@ -70,15 +70,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
|
||||||
|
@ -97,11 +93,6 @@ after_initialize do
|
||||||
../lib/custom_wizard/submission.rb
|
../lib/custom_wizard/submission.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 +113,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 +258,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
|
||||||
|
|
Laden …
In neuem Issue referenzieren