Spiegel von
https://github.com/paviliondev/discourse-custom-wizard.git
synchronisiert 2024-11-22 01:10:28 +01:00
Improve PRO feature approach
Dieser Commit ist enthalten in:
Ursprung
a810155f91
Commit
6b1e7568c1
22 geänderte Dateien mit 331 neuen und 314 gelöschten Zeilen
|
@ -6,7 +6,8 @@ import discourseComputed from "discourse-common/utils/decorators";
|
|||
import I18n from "I18n";
|
||||
|
||||
export default Component.extend({
|
||||
classNames: ["realtime-validations"],
|
||||
classNames: ["realtime-validations", "setting", "full", "pro"],
|
||||
|
||||
@discourseComputed
|
||||
timeUnits() {
|
||||
return ["days", "weeks", "months", "years"].map((unit) => {
|
||||
|
|
|
@ -77,7 +77,12 @@ export default Controller.extend({
|
|||
wizard
|
||||
.save(opts)
|
||||
.then((result) => {
|
||||
this.send("afterSave", result.wizard_id);
|
||||
console.log(result)
|
||||
if (result.wizard_id) {
|
||||
this.send("afterSave", result.wizard_id);
|
||||
} else if (result.errors) {
|
||||
this.set('error', result.errors.join(', '));
|
||||
}
|
||||
})
|
||||
.catch((result) => {
|
||||
let errorType = "failed";
|
||||
|
|
|
@ -220,60 +220,54 @@
|
|||
options=fieldConditionOptions}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<div class="setting full field-mapper-setting pro">
|
||||
<div class="setting-label">
|
||||
<label>{{i18n "admin.wizard.index"}}</label>
|
||||
<span class="pro-label">{{i18n "admin.wizard.pro.label"}}</span>
|
||||
</div>
|
||||
|
||||
{{#if showAdvanced}}
|
||||
{{wizard-advanced-toggle showAdvanced=field.showAdvanced}}
|
||||
<div class="setting-value">
|
||||
{{wizard-mapper
|
||||
inputs=field.index
|
||||
options=fieldIndexOptions}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{#if field.showAdvanced}}
|
||||
<div class="advanced-settings">
|
||||
|
||||
<div class="setting full field-mapper-setting">
|
||||
<div class="setting-label">
|
||||
<label>{{i18n "admin.wizard.index"}}</label>
|
||||
</div>
|
||||
|
||||
<div class="setting-value">
|
||||
{{wizard-mapper
|
||||
inputs=field.index
|
||||
options=fieldIndexOptions}}
|
||||
</div>
|
||||
{{#if isCategory}}
|
||||
<div class="setting pro">
|
||||
<div class="setting-label">
|
||||
<label>{{i18n "admin.wizard.field.property"}}</label>
|
||||
<span class="pro-label">{{i18n "admin.wizard.pro.label"}}</span>
|
||||
</div>
|
||||
|
||||
{{#if isCategory}}
|
||||
<div class="setting">
|
||||
<div class="setting-label">
|
||||
<label>{{i18n "admin.wizard.field.property"}}</label>
|
||||
</div>
|
||||
|
||||
<div class="setting-value">
|
||||
{{combo-box
|
||||
value=field.property
|
||||
content=categoryPropertyTypes
|
||||
onChange=(action (mut field.property))
|
||||
options=(hash
|
||||
none="admin.wizard.selector.placeholder.property"
|
||||
)}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<div class="setting">
|
||||
<div class="setting-label">
|
||||
<label>{{i18n "admin.wizard.translation"}}</label>
|
||||
</div>
|
||||
<div class="setting-value medium">
|
||||
{{input
|
||||
name="key"
|
||||
value=field.key
|
||||
class="medium"
|
||||
placeholderKey="admin.wizard.translation_placeholder"}}
|
||||
</div>
|
||||
|
||||
<div class="setting-value">
|
||||
{{combo-box
|
||||
value=field.property
|
||||
content=categoryPropertyTypes
|
||||
onChange=(action (mut field.property))
|
||||
options=(hash
|
||||
none="admin.wizard.selector.placeholder.property"
|
||||
)}}
|
||||
</div>
|
||||
|
||||
{{#if validations}}
|
||||
{{wizard-realtime-validations field=field validations=validations}}
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<div class="setting pro">
|
||||
<div class="setting-label">
|
||||
<label>{{i18n "admin.wizard.translation"}}</label>
|
||||
<span class="pro-label">{{i18n "admin.wizard.pro.label"}}</span>
|
||||
</div>
|
||||
<div class="setting-value medium">
|
||||
{{input
|
||||
name="key"
|
||||
value=field.key
|
||||
class="medium"
|
||||
placeholderKey="admin.wizard.translation_placeholder"}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{#if validations}}
|
||||
{{wizard-realtime-validations field=field validations=validations}}
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
|
|
@ -21,8 +21,11 @@
|
|||
{{#if subscribed}}
|
||||
<div class="detail-container">
|
||||
<div class="subscription-state {{stateClass}}" title={{stateLabel}}>{{stateLabel}}</div>
|
||||
<div class="subscription-updated-at" title={{subscription.updated_at}}>
|
||||
{{{i18n 'admin.wizard.pro.subscription.last_updated' updated_at=(format-date subscription.updated_at leaveAgo="true")}}}
|
||||
</div>
|
||||
|
||||
{{#if subscription.updated_at}}
|
||||
<div class="subscription-updated-at" title={{subscription.updated_at}}>
|
||||
{{{i18n 'admin.wizard.pro.subscription.last_updated' updated_at=(format-date subscription.updated_at leaveAgo="true")}}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/if}}
|
|
@ -1,50 +1,54 @@
|
|||
<h3>{{i18n "admin.wizard.field.validations.header"}}</h3>
|
||||
|
||||
<ul>
|
||||
{{#each-in field.validations as |type props|}}
|
||||
<li>
|
||||
<span class="setting-title">
|
||||
<h4>{{i18n (concat "admin.wizard.field.validations." type)}}</h4>
|
||||
{{input type="checkbox" checked=props.status}}
|
||||
{{i18n "admin.wizard.field.validations.enabled"}}
|
||||
</span>
|
||||
<div class="validation-container">
|
||||
<div class="validation-section">
|
||||
<div class="setting-label">
|
||||
<label>{{i18n "admin.wizard.field.validations.categories"}}</label>
|
||||
<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>
|
||||
{{#each-in field.validations as |type props|}}
|
||||
<li>
|
||||
<span class="setting-title">
|
||||
<h4>{{i18n (concat "admin.wizard.field.validations." type)}}</h4>
|
||||
{{input type="checkbox" checked=props.status}}
|
||||
{{i18n "admin.wizard.field.validations.enabled"}}
|
||||
</span>
|
||||
<div class="validation-container">
|
||||
<div class="validation-section">
|
||||
<div class="setting-label">
|
||||
<label>{{i18n "admin.wizard.field.validations.categories"}}</label>
|
||||
</div>
|
||||
<div class="setting-value">
|
||||
{{category-selector
|
||||
categories=(get this (concat "validationBuffer." type ".categories"))
|
||||
onChange=(action "updateValidationCategories" type props)
|
||||
class="wizard"}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="setting-value">
|
||||
{{category-selector
|
||||
categories=(get this (concat "validationBuffer." type ".categories"))
|
||||
onChange=(action "updateValidationCategories" type props)
|
||||
class="wizard"}}
|
||||
<div class="validation-section">
|
||||
<div class="setting-label">
|
||||
<label>{{i18n "admin.wizard.field.validations.max_topic_age"}}</label>
|
||||
</div>
|
||||
<div class="setting-value">
|
||||
{{input type="number" class="time-n-value" value=props.time_n_value}}
|
||||
{{combo-box
|
||||
value=(readonly props.time_unit)
|
||||
content=timeUnits
|
||||
class="time-unit-selector"
|
||||
onChange=(action (mut props.time_unit))}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="validation-section">
|
||||
<div class="setting-label">
|
||||
<label>{{i18n "admin.wizard.field.validations.position"}}</label>
|
||||
</div>
|
||||
<div class="setting-value">
|
||||
{{radio-button name=(concat type field.id) value="above" selection=props.position}}
|
||||
<span>{{i18n "admin.wizard.field.validations.above"}}</span>
|
||||
{{radio-button name=(concat type field.id) value="below" selection=props.position}}
|
||||
<span>{{i18n "admin.wizard.field.validations.below"}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="validation-section">
|
||||
<div class="setting-label">
|
||||
<label>{{i18n "admin.wizard.field.validations.max_topic_age"}}</label>
|
||||
</div>
|
||||
<div class="setting-value">
|
||||
{{input type="number" class="time-n-value" value=props.time_n_value}}
|
||||
{{combo-box
|
||||
value=(readonly props.time_unit)
|
||||
content=timeUnits
|
||||
class="time-unit-selector"
|
||||
onChange=(action (mut props.time_unit))}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="validation-section">
|
||||
<div class="setting-label">
|
||||
<label>{{i18n "admin.wizard.field.validations.position"}}</label>
|
||||
</div>
|
||||
<div class="setting-value">
|
||||
{{radio-button name=(concat type field.id) value="above" selection=props.position}}
|
||||
{{i18n "admin.wizard.field.validations.above"}}
|
||||
{{radio-button name=(concat type field.id) value="below" selection=props.position}}
|
||||
{{i18n "admin.wizard.field.validations.below"}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
{{/each-in}}
|
||||
</ul>
|
||||
</li>
|
||||
{{/each-in}}
|
||||
</ul>
|
||||
</div>
|
|
@ -694,27 +694,59 @@
|
|||
}
|
||||
}
|
||||
|
||||
.realtime-validations > ul {
|
||||
.admin-wizard-container.settings .realtime-validations .setting-value > ul {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
> li {
|
||||
background-color: var(--primary-low);
|
||||
padding: 1em;
|
||||
margin: 0 0 1em 0;
|
||||
|
||||
.setting-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
input {
|
||||
margin-bottom: 0;
|
||||
h4 {
|
||||
margin: 0 15px 0 0;
|
||||
}
|
||||
|
||||
input[type="checkbox"] {
|
||||
margin: 0 5px 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
.setting-label {
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.setting-value {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.input .select-kit,
|
||||
> .select-kit {
|
||||
max-width: unset !important;
|
||||
}
|
||||
|
||||
> span {
|
||||
margin-right: 1em;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.validation-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 1em 0;
|
||||
|
||||
.validation-section {
|
||||
width: 250px;
|
||||
min-width: 250px;
|
||||
margin: .5em 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -192,7 +192,7 @@ en:
|
|||
label: "Format"
|
||||
instructions: "<a href='https://momentjs.com/docs/#/displaying/format/' target='_blank'>Moment.js format</a>"
|
||||
validations:
|
||||
header: "Realtime Validations"
|
||||
header: "Validations"
|
||||
enabled: "Enabled"
|
||||
similar_topics: "Similar Topics"
|
||||
position: "Position"
|
||||
|
|
|
@ -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_required: "PRO Actions require a PRO Subscription"
|
||||
pro_type: "%{type} custom fields require PRO Subscription"
|
||||
|
||||
field:
|
||||
too_short: "%{label} must be at least %{min} characters"
|
||||
|
@ -50,7 +50,7 @@ en:
|
|||
required: "%{property} is required"
|
||||
conflict: "Wizard with id '%{wizard_id}' already exists"
|
||||
after_time: "After time setting is invalid"
|
||||
pro: "%{property} is PRO only"
|
||||
pro: "%{type} %{property} is PRO only"
|
||||
|
||||
site_settings:
|
||||
custom_wizard_enabled: "Enable custom wizards."
|
||||
|
|
|
@ -47,7 +47,7 @@ Discourse::Application.routes.append do
|
|||
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'
|
||||
delete 'admin/wizards/pro/authorize' => 'admin_pro#destroy_authentication'
|
||||
post 'admin/wizards/pro/subscription' => 'admin_pro#update_subscription'
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,27 +4,27 @@ class CustomWizard::AdminProController < CustomWizard::AdminController
|
|||
skip_before_action :check_xhr, :preload_json, :verify_authenticity_token, only: [:authorize, :authorize_callback]
|
||||
|
||||
def index
|
||||
render_serialized(CustomWizard::Pro.new, CustomWizard::ProSerializer, root: false)
|
||||
render_serialized(pro, CustomWizard::ProSerializer, root: false)
|
||||
end
|
||||
|
||||
def authorize
|
||||
request_id = SecureRandom.hex(32)
|
||||
cookies[:user_api_request_id] = request_id
|
||||
redirect_to CustomWizard::Pro.auth_request(current_user.id, request_id).to_s
|
||||
redirect_to pro.authentication_request(current_user.id, request_id).to_s
|
||||
end
|
||||
|
||||
def authorize_callback
|
||||
payload = params[:payload]
|
||||
request_id = cookies[:user_api_request_id]
|
||||
|
||||
CustomWizard::Pro.auth_response(request_id, payload)
|
||||
CustomWizard::Pro.update_subscription
|
||||
pro.authentication_response(request_id, payload)
|
||||
pro.update_subscription
|
||||
|
||||
redirect_to '/admin/wizards/pro'
|
||||
end
|
||||
|
||||
def destroy
|
||||
if CustomWizard::ProAuthentication.destroy
|
||||
def destroy_authentication
|
||||
if pro.destroy_authentication
|
||||
render json: success_json
|
||||
else
|
||||
render json: failed_json
|
||||
|
@ -32,15 +32,17 @@ class CustomWizard::AdminProController < CustomWizard::AdminController
|
|||
end
|
||||
|
||||
def update_subscription
|
||||
if CustomWizard::Pro.update
|
||||
render json: success_json.merge(
|
||||
subscription: CustomWizard::ProSubscriptionSerializer.new(
|
||||
CustomWizard::ProSubscription.new,
|
||||
root: false
|
||||
)
|
||||
)
|
||||
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
|
|
@ -5,6 +5,7 @@ class CustomWizard::WizardController < ::ApplicationController
|
|||
layout 'wizard'
|
||||
|
||||
before_action :ensure_plugin_enabled
|
||||
before_action :update_pro_subscription
|
||||
helper_method :wizard_page_title
|
||||
helper_method :wizard_theme_id
|
||||
helper_method :wizard_theme_lookup
|
||||
|
@ -81,4 +82,8 @@ class CustomWizard::WizardController < ::ApplicationController
|
|||
redirect_to path("/")
|
||||
end
|
||||
end
|
||||
|
||||
def update_pro_subscription
|
||||
CustomWizard::Pro.update_subscription
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class CustomWizard::UpdateProSubscription < ::Jobs::Scheduled
|
||||
every 10.minutes
|
||||
every 1.hour
|
||||
|
||||
def execute(args)
|
||||
CustomWizard::Pro.update_subscription
|
|
@ -14,15 +14,9 @@ class CustomWizard::Action
|
|||
@submission = opts[:submission]
|
||||
@log = []
|
||||
@result = CustomWizard::ActionResult.new
|
||||
@pro = CustomWizard::Pro.new
|
||||
end
|
||||
|
||||
def perform
|
||||
if pro_actions.include?(action['type']) && !@pro.subscribed?
|
||||
log_error(I18n.t("wizard.custom_field.error.pro_required"))
|
||||
return
|
||||
end
|
||||
|
||||
ActiveRecord::Base.transaction do
|
||||
self.send(action['type'].to_sym)
|
||||
end
|
||||
|
|
|
@ -6,7 +6,6 @@ class CustomWizard::Builder
|
|||
@template = CustomWizard::Template.create(wizard_id)
|
||||
return nil if @template.nil?
|
||||
@wizard = CustomWizard::Wizard.new(template.data, user)
|
||||
@pro = CustomWizard::Pro.new
|
||||
end
|
||||
|
||||
def self.sorted_handlers
|
||||
|
@ -226,11 +225,6 @@ class CustomWizard::Builder
|
|||
end
|
||||
|
||||
def check_condition(template)
|
||||
unless @pro.subscribed?
|
||||
CustomWizard::Log.create(I18n.t("wizard.custom_field.error.pro_required"))
|
||||
return false
|
||||
end
|
||||
|
||||
if template['condition'].present?
|
||||
result = CustomWizard::Mapper.new(
|
||||
inputs: template['condition'],
|
||||
|
|
|
@ -37,6 +37,8 @@ class ::CustomWizard::CustomField
|
|||
send("#{attr}=", value)
|
||||
end
|
||||
end
|
||||
|
||||
@pro = CustomWizard::Pro.new
|
||||
end
|
||||
|
||||
def save
|
||||
|
@ -92,6 +94,10 @@ class ::CustomWizard::CustomField
|
|||
add_error(I18n.t("#{i18n_key}.unsupported_type", type: value))
|
||||
end
|
||||
|
||||
if attr == 'type' && value == 'json' && !@pro.subscribed?
|
||||
add_error(I18n.t("wizard.custom_field.error.pro_type", type: value))
|
||||
end
|
||||
|
||||
if attr == 'name'
|
||||
unless value.is_a?(String)
|
||||
add_error(I18n.t("#{i18n_key}.name_invalid", name: value))
|
||||
|
|
|
@ -47,6 +47,7 @@ class CustomWizard::Mapper
|
|||
@data = params[:data] || {}
|
||||
@user = params[:user]
|
||||
@opts = params[:opts] || {}
|
||||
@pro = CustomWizard::Pro.new
|
||||
end
|
||||
|
||||
def perform
|
||||
|
@ -251,7 +252,7 @@ class CustomWizard::Mapper
|
|||
end
|
||||
end
|
||||
|
||||
if opts[:template]
|
||||
if @pro.subscribed? && opts[:template]
|
||||
template = Liquid::Template.parse(string)
|
||||
string = template.render(data)
|
||||
end
|
||||
|
|
|
@ -1,12 +1,22 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class CustomWizard::Pro
|
||||
attr_reader :authentication,
|
||||
:subscription
|
||||
include ActiveModel::Serialization
|
||||
|
||||
attr_accessor :authentication,
|
||||
:subscription
|
||||
|
||||
def initialize
|
||||
@authentication = CustomWizard::ProAuthentication.new
|
||||
@subscription = CustomWizard::ProSubscription.new
|
||||
@authentication = CustomWizard::ProAuthentication.new(get_authentication)
|
||||
@subscription = CustomWizard::ProSubscription.new(get_subscription)
|
||||
end
|
||||
|
||||
def authorized?
|
||||
@authentication.active?
|
||||
end
|
||||
|
||||
def subscribed?
|
||||
@subscription.active?
|
||||
end
|
||||
|
||||
def server
|
||||
|
@ -21,12 +31,8 @@ class CustomWizard::Pro
|
|||
"custom-wizard"
|
||||
end
|
||||
|
||||
def authorized?
|
||||
@authentication.active?
|
||||
end
|
||||
|
||||
def subscribed?
|
||||
@subscription.active?
|
||||
def scope
|
||||
"discourse-subscription-server:user_subscription"
|
||||
end
|
||||
|
||||
def update_subscription
|
||||
|
@ -45,28 +51,35 @@ class CustomWizard::Pro
|
|||
return false
|
||||
end
|
||||
|
||||
return @subscription.update(data)
|
||||
return false unless data && data.is_a?(Hash)
|
||||
subscriptions = data[:subscriptions]
|
||||
|
||||
if subscriptions.present?
|
||||
subscription = subscriptions.first
|
||||
type = subscription[:price_nickname]
|
||||
|
||||
@subscription = set_subscription(type)
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@subscription.destroy
|
||||
false
|
||||
remove_subscription
|
||||
end
|
||||
|
||||
def destroy
|
||||
@authentication.destroy
|
||||
def destroy_subscription
|
||||
remove_subscription
|
||||
end
|
||||
|
||||
def auth_request(user_id, request_id)
|
||||
def authentication_request(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/pro/authorize/callback",
|
||||
application_name: SiteSetting.title,
|
||||
scopes: "discourse-subscription-server:user_subscription"
|
||||
scopes: scope
|
||||
}
|
||||
|
||||
uri = URI.parse("https://#{server}/user-api-key/new")
|
||||
|
@ -74,26 +87,24 @@ class CustomWizard::Pro
|
|||
uri.to_s
|
||||
end
|
||||
|
||||
def auth_response(request_id, payload)
|
||||
def authentication_response(request_id, payload)
|
||||
data = @authentication.decrypt_payload(request_id, payload)
|
||||
return unless data.is_a?(Hash) && data[:key] && data[:user_id]
|
||||
@authentication.update(data)
|
||||
|
||||
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 self.update
|
||||
self.new.update
|
||||
end
|
||||
|
||||
def self.destroy
|
||||
self.new.destroy
|
||||
end
|
||||
|
||||
def self.generate_request
|
||||
self.new.generate_request
|
||||
end
|
||||
|
||||
def self.handle_response
|
||||
self.new.handle_response
|
||||
def destroy_authentication
|
||||
remove_authentication
|
||||
end
|
||||
|
||||
def self.subscribed?
|
||||
|
@ -103,4 +114,56 @@ class CustomWizard::Pro
|
|||
def self.namespace
|
||||
"custom_wizard_pro"
|
||||
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::Pro.namespace, subscription_db_key, type: type, updated_at: Time.now)
|
||||
CustomWizard::ProSubscription.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::ProAuthentication.new(get_authentication)
|
||||
end
|
||||
|
||||
def remove_authentication
|
||||
PluginStore.remove(self.class.namespace, authentication_db_key)
|
||||
end
|
||||
end
|
|
@ -6,12 +6,13 @@ class CustomWizard::ProAuthentication
|
|||
:auth_at,
|
||||
:api_key
|
||||
|
||||
def initialize
|
||||
api = get_api_key
|
||||
def initialize(auth)
|
||||
if auth
|
||||
@api_key = auth.key
|
||||
@auth_at = auth.auth_at
|
||||
@auth_by = auth.auth_by
|
||||
end
|
||||
|
||||
@api_key = api.key
|
||||
@auth_at = api.auth_at
|
||||
@auth_by = api.auth_by
|
||||
@client_id = get_client_id || set_client_id
|
||||
end
|
||||
|
||||
|
@ -19,22 +20,6 @@ class CustomWizard::ProAuthentication
|
|||
@api_key.present?
|
||||
end
|
||||
|
||||
def update(data)
|
||||
api_key = data[:key]
|
||||
user_id = data[:user_id]
|
||||
user = User.find(user_id)
|
||||
|
||||
if user&.admin
|
||||
set_api_key(api_key, user.id)
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
remove
|
||||
end
|
||||
|
||||
def generate_keys(user_id, request_id)
|
||||
rsa = OpenSSL::PKey::RSA.generate(2048)
|
||||
nonce = SecureRandom.hex(32)
|
||||
|
@ -63,50 +48,15 @@ class CustomWizard::ProAuthentication
|
|||
|
||||
data
|
||||
end
|
||||
|
||||
|
||||
private
|
||||
|
||||
def api_key_db_key
|
||||
"api_key"
|
||||
end
|
||||
|
||||
def api_client_id_db_key
|
||||
"api_client_id"
|
||||
end
|
||||
|
||||
def keys_db_key
|
||||
"keys"
|
||||
end
|
||||
|
||||
def get_api_key
|
||||
raw = PluginStore.get(CustomWizard::Pro.namespace, api_key_db_key)
|
||||
OpenStruct.new(
|
||||
key: raw && raw['key'],
|
||||
auth_by: raw && raw['auth_by'],
|
||||
auth_at: raw && raw['auth_at']
|
||||
)
|
||||
end
|
||||
|
||||
def set_api_key(key, user_id)
|
||||
PluginStore.set(CustomWizard::Pro.namespace, api_key_db_key,
|
||||
key: key,
|
||||
auth_by: user_id,
|
||||
auth_at: Time.now
|
||||
)
|
||||
end
|
||||
|
||||
def remove
|
||||
PluginStore.remove(CustomWizard::Pro.namespace, api_key_db_key)
|
||||
end
|
||||
|
||||
def get_client_id
|
||||
PluginStore.get(CustomWizard::Pro.namespace, api_client_id_db_key)
|
||||
end
|
||||
|
||||
def set_client_id
|
||||
client_id = SecureRandom.hex(32)
|
||||
PluginStore.set(CustomWizard::Pro.namespace, api_client_id_db_key, client_id)
|
||||
client_id
|
||||
def client_id_db_key
|
||||
"client_id"
|
||||
end
|
||||
|
||||
def set_keys(request_id, user_id, rsa, nonce)
|
||||
|
@ -129,4 +79,14 @@ class CustomWizard::ProAuthentication
|
|||
def delete_keys(request_id)
|
||||
PluginStore.remove(CustomWizard::Pro.namespace, "#{keys_db_key}_#{request_id}")
|
||||
end
|
||||
|
||||
def get_client_id
|
||||
PluginStore.get(CustomWizard::Pro.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)
|
||||
client_id
|
||||
end
|
||||
end
|
|
@ -4,12 +4,10 @@ class CustomWizard::ProSubscription
|
|||
attr_reader :type,
|
||||
:updated_at
|
||||
|
||||
def initialize
|
||||
raw = get
|
||||
|
||||
if raw
|
||||
@type = raw['type']
|
||||
@updated_at = raw['updated_at']
|
||||
def initialize(subscription)
|
||||
if subscription
|
||||
@type = subscription.type
|
||||
@updated_at = subscription.updated_at
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -18,40 +16,6 @@ class CustomWizard::ProSubscription
|
|||
end
|
||||
|
||||
def active?
|
||||
types.include?(type) && updated_at.to_datetime > (Date.today - 15.minutes).to_datetime
|
||||
end
|
||||
|
||||
def update(data)
|
||||
return false unless data && data.is_a?(Hash)
|
||||
subscriptions = data[:subscriptions]
|
||||
|
||||
if subscriptions.present?
|
||||
subscription = subscriptions.first
|
||||
type = subscription[:price_nickname]
|
||||
|
||||
set(type)
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
remove
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def key
|
||||
"custom_wizard_pro_subscription"
|
||||
end
|
||||
|
||||
def set(type)
|
||||
PluginStore.set(CustomWizard::Pro.namespace, key, type: type, updated_at: Time.now)
|
||||
end
|
||||
|
||||
def get
|
||||
PluginStore.get(CustomWizard::Pro.namespace, key)
|
||||
end
|
||||
|
||||
def remove
|
||||
PluginStore.remove(CustomWizard::Pro.namespace, key)
|
||||
types.include?(type) && updated_at.to_datetime > (Time.zone.now - 2.hours).to_datetime
|
||||
end
|
||||
end
|
|
@ -31,6 +31,7 @@ class CustomWizard::TemplateValidator
|
|||
|
||||
if data[:actions].present?
|
||||
data[:actions].each do |action|
|
||||
validate_pro_action(action)
|
||||
check_required(action, :action)
|
||||
end
|
||||
end
|
||||
|
@ -53,15 +54,31 @@ class CustomWizard::TemplateValidator
|
|||
|
||||
def self.pro
|
||||
{
|
||||
step: ['condition'],
|
||||
field: ['conition']
|
||||
wizard: {},
|
||||
step: {
|
||||
condition: 'present',
|
||||
index: 'conditional'
|
||||
},
|
||||
field: {
|
||||
condition: 'present',
|
||||
index: 'conditional'
|
||||
},
|
||||
action: {
|
||||
type: %w[
|
||||
send_message
|
||||
create_category
|
||||
create_group
|
||||
watch_categories
|
||||
send_to_api
|
||||
]
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def check_required(object, type)
|
||||
CustomWizard::TemplateValidator.required[type].each do |property|
|
||||
self.class.required[type].each do |property|
|
||||
if object[property].blank?
|
||||
errors.add :base, I18n.t("wizard.validation.required", property: property)
|
||||
end
|
||||
|
@ -69,9 +86,13 @@ class CustomWizard::TemplateValidator
|
|||
end
|
||||
|
||||
def validate_pro(object, type)
|
||||
CustomWizard::TemplateValidator.required[type].each do |property|
|
||||
if object[property].present? && !@pro.subscribed?
|
||||
errors.add :base, I18n.t("wizard.validation.pro", property: property)
|
||||
self.class.pro[type].each do |property, pro_type|
|
||||
is_pro = (pro_type === 'present' && object[property].present?) ||
|
||||
(pro_type === 'conditional' && object[property].is_a?(Hash)) ||
|
||||
(pro_type.is_a?(Array) && pro_type.includes?(object[property]))
|
||||
|
||||
if is_pro && @pro.subscribed?
|
||||
errors.add :base, I18n.t("wizard.validation.pro", type: type.to_s, property: property)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
16
plugin.rb
16
plugin.rb
|
@ -69,7 +69,7 @@ after_initialize do
|
|||
../controllers/custom_wizard/realtime_validations.rb
|
||||
../jobs/regular/refresh_api_access_token.rb
|
||||
../jobs/regular/set_after_time_wizard.rb
|
||||
../jobs/scheduled/update_pro_status.rb
|
||||
../jobs/scheduled/update_pro_subscription.rb
|
||||
../lib/custom_wizard/validators/template.rb
|
||||
../lib/custom_wizard/validators/update.rb
|
||||
../lib/custom_wizard/action_result.rb
|
||||
|
@ -111,9 +111,9 @@ 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_serializer.rb
|
||||
../serializers/custom_wizard/pro/authentication_serializer.rb
|
||||
../serializers/custom_wizard/pro/subscription_serializer.rb
|
||||
../serializers/custom_wizard/pro_serializer.rb
|
||||
../extensions/extra_locales_controller.rb
|
||||
../extensions/invites_controller.rb
|
||||
../extensions/users_controller.rb
|
||||
|
@ -126,18 +126,6 @@ after_initialize do
|
|||
|
||||
Liquid::Template.register_filter(::CustomWizard::LiquidFilter::FirstNonEmpty)
|
||||
|
||||
class CustomWizard::UnpermittedOverride < StandardError; end
|
||||
|
||||
CustomWizard.constants.each do |class_name|
|
||||
klass = CustomWizard.const_get(class_name)
|
||||
next if !klass.is_a?(Class) || klass.superclass.name.to_s.split("::").first == 'CustomWizard'
|
||||
|
||||
klass.define_singleton_method(:prepend) { |klass| raise CustomWizard::UnpermittedOverride.new }
|
||||
klass.define_singleton_method(:include) { |klass| raise CustomWizard::UnpermittedOverride.new }
|
||||
klass.define_singleton_method(:define_method) { |name, &block| raise CustomWizard::UnpermittedOverride.new }
|
||||
klass.define_singleton_method(:define_singleton_method) { |name, &block| raise CustomWizard::UnpermittedOverride.new }
|
||||
end
|
||||
|
||||
add_class_method(:wizard, :user_requires_completion?) do |user|
|
||||
wizard_result = self.new(user).requires_completion?
|
||||
return wizard_result if wizard_result
|
||||
|
|
|
@ -1,26 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
class CustomWizard::ProSerializer < ApplicationSerializer
|
||||
attributes :server,
|
||||
:authentication,
|
||||
:subscription
|
||||
|
||||
def server
|
||||
CustomWizard::ProSubscription::SUBSCRIPTION_SERVER
|
||||
end
|
||||
|
||||
def authentication
|
||||
if object.authentication
|
||||
CustomWizard::ProAuthenticationSerializer.new(object.authentication, root: false)
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def subscription
|
||||
if object.subscription
|
||||
CustomWizard::ProSubscriptionSerializer.new(object.subscription, root: false)
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
attributes :server
|
||||
has_one :authentication, serializer: CustomWizard::ProAuthenticationSerializer, embed: :objects
|
||||
has_one :subscription, serializer: CustomWizard::ProSubscriptionSerializer, embed: :objects
|
||||
end
|
Laden …
In neuem Issue referenzieren