diff --git a/assets/javascripts/discourse/templates/admin-wizards.hbs b/assets/javascripts/discourse/templates/admin-wizards.hbs
index 5baa9f52..0578e01f 100644
--- a/assets/javascripts/discourse/templates/admin-wizards.hbs
+++ b/assets/javascripts/discourse/templates/admin-wizards.hbs
@@ -7,6 +7,7 @@
{{/if}}
{{nav-item route="adminWizardsLogs" label="admin.wizard.log.nav_label"}}
{{nav-item route="adminWizardsManager" label="admin.wizard.manager.nav_label"}}
+ {{nav-item route="adminWizardsPro" label="admin.wizard.pro.nav_label"}}
{{d-icon "far-life-ring"}}{{i18n "admin.wizard.pro_support_button.label"}}
diff --git a/assets/javascripts/discourse/templates/components/custom-field-input.hbs b/assets/javascripts/discourse/templates/components/custom-field-input.hbs
index 43a97be8..2bb9acce 100644
--- a/assets/javascripts/discourse/templates/components/custom-field-input.hbs
+++ b/assets/javascripts/discourse/templates/components/custom-field-input.hbs
@@ -1,13 +1,13 @@
{{#if showInputs}}
- {{combo-box
+ {{wizard-pro-selector
value=field.klass
content=klassContent
none="admin.wizard.custom_field.klass.select"
onChange=(action (mut field.klass))}}
|
- {{combo-box
+ {{wizard-pro-selector
value=field.type
content=typeContent
none="admin.wizard.custom_field.type.select"
diff --git a/assets/javascripts/discourse/templates/components/submission-field.hbs b/assets/javascripts/discourse/templates/components/submission-field.hbs
index 831dcc3a..c635ff47 100644
--- a/assets/javascripts/discourse/templates/components/submission-field.hbs
+++ b/assets/javascripts/discourse/templates/components/submission-field.hbs
@@ -160,4 +160,4 @@
{{d-icon "clock"}}{{format-date value format="tiny"}}
-{{/if}}
\ No newline at end of file
+{{/if}}
diff --git a/assets/javascripts/discourse/templates/components/wizard-advanced-toggle.hbs b/assets/javascripts/discourse/templates/components/wizard-advanced-toggle.hbs
deleted file mode 100644
index ec2bcb76..00000000
--- a/assets/javascripts/discourse/templates/components/wizard-advanced-toggle.hbs
+++ /dev/null
@@ -1,4 +0,0 @@
-{{d-button
- action="toggleAdvanced"
- label="admin.wizard.advanced"
- class=toggleClass}}
diff --git a/assets/javascripts/discourse/templates/components/wizard-custom-action.hbs b/assets/javascripts/discourse/templates/components/wizard-custom-action.hbs
index 4c645cf7..f5d53200 100644
--- a/assets/javascripts/discourse/templates/components/wizard-custom-action.hbs
+++ b/assets/javascripts/discourse/templates/components/wizard-custom-action.hbs
@@ -12,13 +12,14 @@
- {{combo-box
+ {{wizard-pro-selector
value=action.type
content=actionTypes
onChange=(action "changeType")
options=(hash
none="admin.wizard.select_type"
- )}}
+ )
+ }}
@@ -714,99 +715,90 @@
{{/if}}
-{{#if showAdvanced}}
- {{wizard-advanced-toggle showAdvanced=action.showAdvanced}}
-
- {{#if action.showAdvanced}}
-
-
- {{#if hasCustomFields}}
-
-
-
-
-
-
- {{wizard-mapper
- inputs=action.custom_fields
- property="custom_fields"
- onUpdate=(action "mappedFieldUpdated")
- options=(hash
- inputTypes="association"
- customFieldSelection="key"
- wizardFieldSelection="value"
- wizardActionSelection="value"
- userFieldSelection="value"
- keyPlaceholder="admin.wizard.action.custom_fields.key"
- context=customFieldsContext
- )}}
-
-
- {{/if}}
-
- {{#if sendMessage}}
-
-
-
-
-
-
- {{wizard-mapper
- inputs=action.required
- property="required"
- onUpdate=(action "mappedFieldUpdated")
- options=(hash
- textSelection="value"
- wizardFieldSelection=true
- userFieldSelection=true
- groupSelection=true
- context="action"
- )}}
-
-
- {{/if}}
-
- {{#if showPostAdvanced}}
-
-
-
-
-
-
- {{input type="checkbox" checked=action.skip_redirect}}
-
-
- {{i18n "admin.wizard.action.skip_redirect.description" type="topic"}}
-
-
-
-
-
-
-
-
-
-
- {{input type="checkbox" checked=action.suppress_notifications}}
-
-
- {{i18n "admin.wizard.action.suppress_notifications.description" type="topic"}}
-
-
-
- {{/if}}
-
- {{#if routeTo}}
-
-
-
-
-
-
- {{input value=action.code}}
-
-
- {{/if}}
+{{#if hasCustomFields}}
+
+
+
- {{/if}}
+
+
+ {{wizard-mapper
+ inputs=action.custom_fields
+ property="custom_fields"
+ onUpdate=(action "mappedFieldUpdated")
+ options=(hash
+ inputTypes="association"
+ customFieldSelection="key"
+ wizardFieldSelection="value"
+ wizardActionSelection="value"
+ userFieldSelection="value"
+ keyPlaceholder="admin.wizard.action.custom_fields.key"
+ context=customFieldsContext
+ )}}
+
+
+{{/if}}
+
+{{#if sendMessage}}
+
+
+
+
+
+
+ {{wizard-mapper
+ inputs=action.required
+ property="required"
+ onUpdate=(action "mappedFieldUpdated")
+ options=(hash
+ textSelection="value"
+ wizardFieldSelection=true
+ userFieldSelection=true
+ groupSelection=true
+ context="action"
+ )}}
+
+
+{{/if}}
+
+{{#if showPostAdvanced}}
+
+
+
+
+
+
+ {{input type="checkbox" checked=action.skip_redirect}}
+
+
+ {{i18n "admin.wizard.action.skip_redirect.description" type="topic"}}
+
+
+
+
+
+
+
+
+
+
+ {{input type="checkbox" checked=action.suppress_notifications}}
+
+
+ {{i18n "admin.wizard.action.suppress_notifications.description" type="topic"}}
+
+
+
+{{/if}}
+
+{{#if routeTo}}
+
+
+
+
+
+
+ {{input value=action.code}}
+
+
{{/if}}
diff --git a/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs b/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs
index 3b63cef7..8c8bb6d4 100644
--- a/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs
+++ b/assets/javascripts/discourse/templates/components/wizard-custom-field.hbs
@@ -207,70 +207,66 @@
{{/if}}
-{{#if showAdvanced}}
- {{wizard-advanced-toggle showAdvanced=field.showAdvanced}}
+{{#if proSubscribed}}
+
+
+
+ {{i18n "admin.wizard.pro.label"}}
+
- {{#if field.showAdvanced}}
-
+
+ {{wizard-mapper
+ inputs=field.condition
+ options=fieldConditionOptions}}
+
+
+
+
+
+
+ {{i18n "admin.wizard.pro.label"}}
+
-
-
-
-
+
+ {{wizard-mapper
+ inputs=field.index
+ options=fieldIndexOptions}}
+
+
-
- {{wizard-mapper
- inputs=field.condition
- options=fieldConditionOptions}}
-
+ {{#if isCategory}}
+
+
+
+ {{i18n "admin.wizard.pro.label"}}
-
-
-
-
-
-
-
- {{wizard-mapper
- inputs=field.index
- options=fieldIndexOptions}}
-
+
+
+ {{combo-box
+ value=field.property
+ content=categoryPropertyTypes
+ onChange=(action (mut field.property))
+ options=(hash
+ none="admin.wizard.selector.placeholder.property"
+ )}}
-
- {{#if isCategory}}
-
-
-
-
-
-
- {{combo-box
- value=field.property
- content=categoryPropertyTypes
- onChange=(action (mut field.property))
- options=(hash
- none="admin.wizard.selector.placeholder.property"
- )}}
-
-
- {{/if}}
-
-
-
-
-
-
- {{input
- name="key"
- value=field.key
- class="medium"
- placeholderKey="admin.wizard.translation_placeholder"}}
-
-
-
- {{#if validations}}
- {{wizard-realtime-validations field=field validations=validations}}
- {{/if}}
{{/if}}
+
+
+
+
+ {{i18n "admin.wizard.pro.label"}}
+
+
+ {{input
+ name="key"
+ value=field.key
+ placeholderKey="admin.wizard.translation_placeholder"}}
+
+
+
+ {{#if validations}}
+ {{wizard-realtime-validations field=field validations=validations}}
+ {{/if}}
{{/if}}
diff --git a/assets/javascripts/discourse/templates/components/wizard-custom-step.hbs b/assets/javascripts/discourse/templates/components/wizard-custom-step.hbs
index 85adfe8a..91476ae3 100644
--- a/assets/javascripts/discourse/templates/components/wizard-custom-step.hbs
+++ b/assets/javascripts/discourse/templates/components/wizard-custom-step.hbs
@@ -33,85 +33,84 @@
-{{wizard-advanced-toggle showAdvanced=step.showAdvanced}}
-
-{{#if step.showAdvanced}}
-
-
-
-
-
-
-
-
- {{wizard-mapper
- inputs=step.condition
- options=stepConditionOptions}}
-
+{{#if proSubscribed}}
+
+
+
+ {{i18n "admin.wizard.pro.label"}}
-
-
-
- {{i18n "admin.wizard.step.force_final.label"}}
- {{input type="checkbox" checked=step.force_final}}
- {{i18n "admin.wizard.step.force_final.description"}}
-
+
+ {{wizard-mapper
+ inputs=step.condition
+ options=stepConditionOptions}}
+
-
-
-
-
-
- {{wizard-mapper
- inputs=step.required_data
- options=(hash
- inputTypes="validation"
- inputConnector="and"
- wizardFieldSelection="value"
- userFieldSelection="value"
- keyPlaceholder="admin.wizard.submission_key"
- context="step"
- )}}
- {{#if step.required_data}}
-
-
- {{i18n "admin.wizard.step.required_data.not_permitted_message"}}
-
- {{input value=step.required_data_message}}
+
+
+
+ {{i18n "admin.wizard.step.force_final.label"}}
+ {{input type="checkbox" checked=step.force_final}}
+ {{i18n "admin.wizard.step.force_final.description"}}
+
+
+
+
+
+
+ {{i18n "admin.wizard.pro.label"}}
+
+
+ {{wizard-mapper
+ inputs=step.required_data
+ options=(hash
+ inputTypes="validation"
+ inputConnector="and"
+ wizardFieldSelection="value"
+ userFieldSelection="value"
+ keyPlaceholder="admin.wizard.submission_key"
+ context="step"
+ )}}
+ {{#if step.required_data}}
+
+
+ {{i18n "admin.wizard.step.required_data.not_permitted_message"}}
- {{/if}}
-
+ {{input value=step.required_data_message}}
+
+ {{/if}}
+
-
-
-
-
-
- {{wizard-mapper
- inputs=step.permitted_params
- options=(hash
- pairConnector="set"
- inputTypes="association"
- keyPlaceholder="admin.wizard.param_key"
- valuePlaceholder="admin.wizard.submission_key"
- context="step"
- )}}
-
+
+
+
+ {{i18n "admin.wizard.pro.label"}}
+
+ {{wizard-mapper
+ inputs=step.permitted_params
+ options=(hash
+ pairConnector="set"
+ inputTypes="association"
+ keyPlaceholder="admin.wizard.param_key"
+ valuePlaceholder="admin.wizard.submission_key"
+ context="step"
+ )}}
+
+
-
-
-
-
-
- {{input
- name="key"
- value=step.key
- placeholderKey="admin.wizard.translation_placeholder"}}
-
+
+
+
+ {{i18n "admin.wizard.pro.label"}}
+
+
+ {{input
+ name="key"
+ value=step.key
+ placeholderKey="admin.wizard.translation_placeholder"}}
{{/if}}
@@ -129,5 +128,6 @@
currentFieldId=currentField.id
fieldTypes=fieldTypes
removeField="removeField"
- wizardFields=wizardFields}}
+ wizardFields=wizardFields
+ proSubscribed=proSubscribed}}
{{/each}}
diff --git a/assets/javascripts/discourse/templates/components/wizard-pro-selector/wizard-pro-selector-header.hbs b/assets/javascripts/discourse/templates/components/wizard-pro-selector/wizard-pro-selector-header.hbs
new file mode 100644
index 00000000..db467b02
--- /dev/null
+++ b/assets/javascripts/discourse/templates/components/wizard-pro-selector/wizard-pro-selector-header.hbs
@@ -0,0 +1,15 @@
+
diff --git a/assets/javascripts/discourse/templates/components/wizard-pro-selector/wizard-pro-selector-row.hbs b/assets/javascripts/discourse/templates/components/wizard-pro-selector/wizard-pro-selector-row.hbs
new file mode 100644
index 00000000..077265ef
--- /dev/null
+++ b/assets/javascripts/discourse/templates/components/wizard-pro-selector/wizard-pro-selector-row.hbs
@@ -0,0 +1,15 @@
+{{#if icons}}
+
+
+ {{#each icons as |icon|}}
+ {{d-icon icon translatedtitle=(dasherize title)}}
+ {{/each}}
+
+{{/if}}
+
+
+ {{html-safe label}}
+ {{#if item.pro}}
+ {{i18n "admin.wizard.pro.label"}}
+ {{/if}}
+
diff --git a/assets/javascripts/discourse/templates/components/wizard-pro-subscription.hbs b/assets/javascripts/discourse/templates/components/wizard-pro-subscription.hbs
new file mode 100644
index 00000000..8eca5996
--- /dev/null
+++ b/assets/javascripts/discourse/templates/components/wizard-pro-subscription.hbs
@@ -0,0 +1,31 @@
+
+ {{title}}
+
+
+
+ {{#if updating}}
+ {{loading-spinner size="small"}}
+ {{else if updateIcon}}
+ {{d-icon updateIcon}}
+ {{/if}}
+
+ {{d-button
+ icon="sync"
+ action=(action "update")
+ disabled=updating
+ title="admin.wizard.pro.subscription.update"
+ label="admin.wizard.pro.subscription.update"}}
+
+
+
+{{#if subscribed}}
+
+ {{stateLabel}}
+
+ {{#if subscription.updated_at}}
+
+ {{i18n "admin.wizard.pro.subscription.last_updated"}} {{format-date subscription.updated_at leaveAgo="true"}}
+
+ {{/if}}
+
+{{/if}}
diff --git a/assets/javascripts/discourse/templates/components/wizard-realtime-validations.hbs b/assets/javascripts/discourse/templates/components/wizard-realtime-validations.hbs
index cd1298a9..1aa0893b 100644
--- a/assets/javascripts/discourse/templates/components/wizard-realtime-validations.hbs
+++ b/assets/javascripts/discourse/templates/components/wizard-realtime-validations.hbs
@@ -1,50 +1,54 @@
- {{i18n "admin.wizard.field.validations.header"}}
-
-
- {{#each-in field.validations as |type props|}}
- -
-
-
{{i18n (concat "admin.wizard.field.validations." type)}}
- {{input type="checkbox" checked=props.status}}
- {{i18n "admin.wizard.field.validations.enabled"}}
-
-
-
-
-
+
+
+ {{i18n "admin.wizard.pro.label"}}
+
+
+
+ {{#each-in field.validations as |type props|}}
+ -
+
+
{{i18n (concat "admin.wizard.field.validations." type)}}
+ {{input type="checkbox" checked=props.status}}
+ {{i18n "admin.wizard.field.validations.enabled"}}
+
+
+
+
+
+
+
+ {{category-selector
+ categories=(get this (concat "validationBuffer." type ".categories"))
+ onChange=(action "updateValidationCategories" type props)
+ class="wizard"}}
+
-
- {{category-selector
- categories=(get this (concat "validationBuffer." type ".categories"))
- onChange=(action "updateValidationCategories" type props)
- class="wizard"}}
+
+
+
+
+
+ {{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))}}
+
+
+
+
+
+
+
+ {{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"}}
+
-
-
-
-
-
- {{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))}}
-
-
-
-
-
-
-
- {{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"}}
-
-
-
-
- {{/each-in}}
-
+
+ {{/each-in}}
+
+
diff --git a/assets/javascripts/discourse/templates/modal/admin-wizards-submissions-columns.hbs b/assets/javascripts/discourse/templates/modal/admin-wizards-submissions-columns.hbs
index 24743a92..a167a954 100644
--- a/assets/javascripts/discourse/templates/modal/admin-wizards-submissions-columns.hbs
+++ b/assets/javascripts/discourse/templates/modal/admin-wizards-submissions-columns.hbs
@@ -29,4 +29,4 @@
label="directory.edit_columns.reset_to_default"
action=(action "resetToDefault")
}}
-
\ No newline at end of file
+
diff --git a/assets/stylesheets/common/wizard-admin.scss b/assets/stylesheets/common/wizard-admin.scss
index 02fab71b..7027950a 100644
--- a/assets/stylesheets/common/wizard-admin.scss
+++ b/assets/stylesheets/common/wizard-admin.scss
@@ -8,7 +8,7 @@
display: flex;
align-items: center;
justify-content: space-between;
- margin-bottom: 20px;
+ margin-bottom: 10px;
& + .wizard-message + div {
margin-top: 20px;
@@ -294,7 +294,7 @@
font-size: 0.85em;
}
- span {
+ > span {
font-size: 0.929em;
}
@@ -423,6 +423,17 @@
.setting-gutter {
margin-top: 5px;
}
+
+ &.pro {
+ .setting-label {
+ display: flex;
+ flex-direction: column;
+
+ label {
+ margin: 0;
+ }
+ }
+ }
}
.advanced-settings {
@@ -729,27 +740,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;
- input {
- margin-bottom: 0;
+ .setting-title {
+ display: flex;
+ align-items: center;
+
+ 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: 0.5em 0;
}
}
@@ -767,6 +810,82 @@
vertical-align: middle;
}
+.pro-label {
+ color: var(--tertiary);
+ font-size: 0.75em;
+}
+
+.admin-wizards-pro {
+ .admin-wizard-controls {
+ h3,
+ label {
+ margin: 0;
+ }
+
+ label {
+ padding: 0.4em 0.5em;
+ margin-left: 0.75em;
+ background-color: var(--success);
+ color: var(--secondary);
+ }
+
+ .buttons {
+ display: flex;
+ align-items: center;
+
+ .loading-container {
+ margin-right: 1em;
+ }
+ }
+ }
+
+ .custom-wizard-pro-subscription {
+ .title-container {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 0.5em;
+
+ h3 {
+ margin: 0;
+ }
+
+ .buttons > span {
+ margin-right: 0.5em;
+ }
+ }
+
+ .detail-container {
+ display: flex;
+ align-items: center;
+ padding: 1em;
+ background-color: var(--primary-very-low);
+
+ .subscription-state {
+ padding: 0.25em 0.5em;
+ margin-right: 0.75em;
+
+ &.active {
+ background-color: var(--success);
+ color: var(--secondary);
+ }
+ }
+ }
+ }
+}
+
+.wizard-pro-selector.select-kit.single-select {
+ .select-kit-row .texts {
+ display: flex;
+ align-items: center;
+ }
+
+ .pro-label {
+ margin-left: 0.75em;
+ padding-top: 0.25em;
+ }
+}
+
.btn.btn-pavilion-pro {
background: var(--pavilion-primary);
color: var(--pavilion-secondary);
diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml
index aef049fb..e7103957 100644
--- a/config/locales/client.en.yml
+++ b/config/locales/client.en.yml
@@ -58,10 +58,11 @@ en:
select_type: "Select a type"
condition: "Condition"
index: "Index"
+
pro_support_button:
title: "Request Pro Support"
label: "Pro Support"
-
+
message:
wizard:
select: "Select a wizard, or create a new one"
@@ -89,11 +90,19 @@ en:
no_file: Please choose a file to import
file_size_error: The file size must be 512kb or less
file_format_error: The file must be a .json file
- server_error: "Error: {{message}}"
importing: Importing wizards...
destroying: Destroying wizards...
import_complete: Import complete
destroy_complete: Destruction complete
+ pro:
+ documentation: Check out the PRO documentation
+ authorize: "Authorize this forum to use your PRO subscription plan on %{server}."
+ not_subscribed: "You've authorized, but are not currently subscribed to a PRO plan on %{server}."
+ subscription_expiring: "Your subscription is active, but will expire in the next 48 hours."
+ subscription_active: "Your subscription is active."
+ subscription_inactive: "Your subscription is inactive on this forum. Read more in the documentation."
+ unauthorized: "You're unauthorized. If you have a subscription, it will become inactive in the next 48 hours."
+ unauthorize_failed: Failed to unauthorize.
submissions:
select: "Select a wizard to see its submissions"
viewing: "You're viewing the submissions of the %{wizardName}. Click 'Download' on the right to download them."
@@ -102,7 +111,6 @@ en:
viewing: "View recent logs for wizards on the forum"
documentation: "Check out the logs documentation"
-
editor:
show: "Show"
hide: "Hide"
@@ -182,9 +190,9 @@ en:
min_length_placeholder: "Minimum length in characters"
max_length: "Max Length"
max_length_placeholder: "Maximum length in characters"
- char_counter: "Character Counter"
+ char_counter: "Counter"
char_counter_placeholder: "Display Character Counter"
- field_placeholder: "Field Placeholder"
+ field_placeholder: "Placeholder"
file_types: "File Types"
preview_template: "Preview Template"
limit: "Limit"
@@ -195,7 +203,7 @@ en:
label: "Format"
instructions: " Moment.js format"
validations:
- header: "Realtime Validations"
+ header: "Validations"
enabled: "Enabled"
similar_topics: "Similar Topics"
position: "Position"
@@ -441,7 +449,25 @@ en:
imported: imported
upload: Select wizards.json
destroy: Destroy
- destroyed: destroyed
+ destroyed: destroyed
+
+ pro:
+ nav_label: PRO
+ label: PRO
+ title: Custom Wizard PRO
+ authorize: Authorize
+ authorized: Authorized
+ unauthorize: cancel
+ not_subscribed: You're not currently subscribed
+ subscription:
+ title:
+ community: Community Subscription
+ business: Business Subscription
+ status:
+ active: Active
+ inactive: Inactive
+ update: Update
+ last_updated: Last updated
wizard_js:
group:
diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml
index 7e507450..fffa01cc 100644
--- a/config/locales/server.en.yml
+++ b/config/locales/server.en.yml
@@ -17,6 +17,7 @@ en:
name_too_short: "'%{name}' is too short for a custom field name (min length is #{min_length})"
name_already_taken: "'%{name}' is already taken as a custom field name"
save_default: "Failed to save custom field '%{name}'"
+ pro_type: "%{type} custom fields require PRO Subscription"
field:
too_short: "%{label} must be at least %{min} characters"
@@ -49,6 +50,7 @@ en:
required: "%{property} is required"
conflict: "Wizard with id '%{wizard_id}' already exists"
after_time: "After time setting is invalid"
+ pro: "%{type} %{property} is PRO only"
site_settings:
custom_wizard_enabled: "Enable custom wizards."
diff --git a/config/routes.rb b/config/routes.rb
index 28fcbb82..abe36479 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -43,5 +43,11 @@ Discourse::Application.routes.append do
get 'admin/wizards/manager/export' => 'admin_manager#export'
post 'admin/wizards/manager/import' => 'admin_manager#import'
delete 'admin/wizards/manager/destroy' => 'admin_manager#destroy'
+
+ get 'admin/wizards/pro' => 'admin_pro#index'
+ get 'admin/wizards/pro/authorize' => 'admin_pro#authorize'
+ get 'admin/wizards/pro/authorize/callback' => 'admin_pro#authorize_callback'
+ delete 'admin/wizards/pro/authorize' => 'admin_pro#destroy_authentication'
+ post 'admin/wizards/pro/subscription' => 'admin_pro#update_subscription'
end
end
diff --git a/controllers/custom_wizard/admin/custom_fields.rb b/controllers/custom_wizard/admin/custom_fields.rb
index c52759c9..40ff64be 100644
--- a/controllers/custom_wizard/admin/custom_fields.rb
+++ b/controllers/custom_wizard/admin/custom_fields.rb
@@ -1,7 +1,10 @@
# frozen_string_literal: true
class CustomWizard::AdminCustomFieldsController < CustomWizard::AdminController
def index
- render_json_dump(custom_field_list)
+ render_json_dump(
+ custom_fields: custom_field_list,
+ pro_subscribed: CustomWizard::Pro.subscribed?
+ )
end
def update
diff --git a/controllers/custom_wizard/admin/pro.rb b/controllers/custom_wizard/admin/pro.rb
new file mode 100644
index 00000000..650743e6
--- /dev/null
+++ b/controllers/custom_wizard/admin/pro.rb
@@ -0,0 +1,48 @@
+# frozen_string_literal: true
+
+class CustomWizard::AdminProController < CustomWizard::AdminController
+ skip_before_action :check_xhr, :preload_json, :verify_authenticity_token, only: [:authorize, :authorize_callback]
+
+ def index
+ render_serialized(pro, CustomWizard::ProSerializer, root: false)
+ end
+
+ def authorize
+ request_id = SecureRandom.hex(32)
+ cookies[:user_api_request_id] = request_id
+ redirect_to pro.authentication_url(current_user.id, request_id).to_s
+ end
+
+ def authorize_callback
+ payload = params[:payload]
+ request_id = cookies[:user_api_request_id]
+
+ pro.authentication_response(request_id, payload)
+ pro.update_subscription
+
+ redirect_to '/admin/wizards/pro'
+ end
+
+ def destroy_authentication
+ if pro.destroy_authentication
+ render json: success_json
+ else
+ render json: failed_json
+ end
+ end
+
+ def update_subscription
+ if pro.update_subscription
+ subscription = CustomWizard::ProSubscriptionSerializer.new(pro.subscription, root: false)
+ render json: success_json.merge(subscription: subscription)
+ else
+ render json: failed_json
+ end
+ end
+
+ protected
+
+ def pro
+ @pro ||= CustomWizard::Pro.new
+ end
+end
diff --git a/controllers/custom_wizard/admin/wizard.rb b/controllers/custom_wizard/admin/wizard.rb
index 8da64fac..e824398c 100644
--- a/controllers/custom_wizard/admin/wizard.rb
+++ b/controllers/custom_wizard/admin/wizard.rb
@@ -10,7 +10,8 @@ class CustomWizard::AdminWizardController < CustomWizard::AdminController
),
field_types: CustomWizard::Field.types,
realtime_validations: CustomWizard::RealtimeValidation.types,
- custom_fields: custom_field_list
+ custom_fields: custom_field_list,
+ pro_subscribed: CustomWizard::Pro.subscribed?
)
end
diff --git a/controllers/custom_wizard/wizard.rb b/controllers/custom_wizard/wizard.rb
index 7125fc8a..732c5d5e 100644
--- a/controllers/custom_wizard/wizard.rb
+++ b/controllers/custom_wizard/wizard.rb
@@ -5,6 +5,7 @@ class CustomWizard::WizardController < ::ApplicationController
layout 'wizard'
before_action :ensure_plugin_enabled
+ before_action :update_pro_subscription, only: [:index]
helper_method :wizard_page_title
helper_method :wizard_theme_id
helper_method :wizard_theme_lookup
@@ -82,4 +83,8 @@ class CustomWizard::WizardController < ::ApplicationController
redirect_to path("/")
end
end
+
+ def update_pro_subscription
+ CustomWizard::Pro.update_subscription
+ end
end
diff --git a/coverage/.last_run.json b/coverage/.last_run.json
index 2d4d0378..cff5740b 100644
--- a/coverage/.last_run.json
+++ b/coverage/.last_run.json
@@ -1,5 +1,5 @@
{
"result": {
- "line": 91.83
+ "line": 91.96
}
}
diff --git a/jobs/refresh_api_access_token.rb b/jobs/regular/refresh_api_access_token.rb
similarity index 100%
rename from jobs/refresh_api_access_token.rb
rename to jobs/regular/refresh_api_access_token.rb
diff --git a/jobs/set_after_time_wizard.rb b/jobs/regular/set_after_time_wizard.rb
similarity index 100%
rename from jobs/set_after_time_wizard.rb
rename to jobs/regular/set_after_time_wizard.rb
diff --git a/jobs/scheduled/update_pro_subscription.rb b/jobs/scheduled/update_pro_subscription.rb
new file mode 100644
index 00000000..c790d529
--- /dev/null
+++ b/jobs/scheduled/update_pro_subscription.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class CustomWizard::UpdateProSubscription < ::Jobs::Scheduled
+ every 1.hour
+
+ def execute(args = {})
+ CustomWizard::Pro.update_subscription
+ end
+end
diff --git a/lib/custom_wizard/action.rb b/lib/custom_wizard/action.rb
index 113a0393..8ee11e54 100644
--- a/lib/custom_wizard/action.rb
+++ b/lib/custom_wizard/action.rb
@@ -749,4 +749,8 @@ class CustomWizard::Action
@log.join('; ')
)
end
+
+ def pro_actions
+ %w[send_message watch_categories send_to_api create_group create_category]
+ end
end
diff --git a/lib/custom_wizard/builder.rb b/lib/custom_wizard/builder.rb
index aede16a2..99370f19 100644
--- a/lib/custom_wizard/builder.rb
+++ b/lib/custom_wizard/builder.rb
@@ -21,13 +21,6 @@ class CustomWizard::Builder
@sorted_handlers.sort_by! { |h| -h[:priority] }
end
- def mapper
- CustomWizard::Mapper.new(
- user: @wizard.user,
- data: @wizard.current_submission&.fields_and_meta
- )
- end
-
def build(build_opts = {}, params = {})
return nil if !SiteSetting.custom_wizard_enabled || !@wizard
return @wizard if !@wizard.can_access? && !build_opts[:force]
@@ -79,6 +72,32 @@ class CustomWizard::Builder
@wizard
end
+ def check_condition(template)
+ if template['condition'].present?
+ result = CustomWizard::Mapper.new(
+ inputs: template['condition'],
+ user: @wizard.user,
+ data: @wizard.current_submission&.fields_and_meta,
+ opts: {
+ multiple: true
+ }
+ ).perform
+
+ result.any?
+ else
+ true
+ end
+ end
+
+ private
+
+ def mapper
+ CustomWizard::Mapper.new(
+ user: @wizard.user,
+ data: @wizard.current_submission&.fields_and_meta
+ )
+ end
+
def append_field(step, step_template, field_template, build_opts)
params = {
id: field_template['id'],
@@ -222,23 +241,6 @@ class CustomWizard::Builder
end
end
- def check_condition(template)
- if template['condition'].present?
- result = CustomWizard::Mapper.new(
- inputs: template['condition'],
- user: @wizard.user,
- data: @wizard.current_submission&.fields_and_meta,
- opts: {
- multiple: true
- }
- ).perform
-
- result.any?
- else
- true
- end
- end
-
def check_if_permitted(step, step_template)
step.permitted = true
diff --git a/lib/custom_wizard/custom_field.rb b/lib/custom_wizard/custom_field.rb
index 9cc185ba..bc1a146d 100644
--- a/lib/custom_wizard/custom_field.rb
+++ b/lib/custom_wizard/custom_field.rb
@@ -17,7 +17,9 @@ class ::CustomWizard::CustomField
category: ["basic_category"],
post: ["post"]
}
+ PRO_CLASSES ||= ['category', 'group']
TYPES ||= ["string", "boolean", "integer", "json"]
+ PRO_TYPES ||= ["json"]
LIST_CACHE_KEY ||= 'custom_field_list'
def self.serializers
@@ -37,6 +39,8 @@ class ::CustomWizard::CustomField
send("#{attr}=", value)
end
end
+
+ @pro = CustomWizard::Pro.new
end
def save
@@ -81,6 +85,10 @@ class ::CustomWizard::CustomField
next
end
+ if attr == 'klass' && PRO_CLASSES.include?(value) && !@pro.subscribed?
+ add_error(I18n.t("wizard.custom_field.error.pro_type", type: value))
+ end
+
if attr == 'serializers' && (unsupported = value - CLASSES[klass.to_sym]).length > 0
add_error(I18n.t("#{i18n_key}.unsupported_serializers",
class: klass,
@@ -92,6 +100,10 @@ class ::CustomWizard::CustomField
add_error(I18n.t("#{i18n_key}.unsupported_type", type: value))
end
+ if attr == 'type' && PRO_TYPES.include?(value) && !@pro.subscribed?
+ add_error(I18n.t("wizard.custom_field.error.pro_type", type: value))
+ end
+
if attr == 'name'
unless value.is_a?(String)
add_error(I18n.t("#{i18n_key}.name_invalid", name: value))
diff --git a/lib/custom_wizard/mapper.rb b/lib/custom_wizard/mapper.rb
index 0c3543cf..6700e37d 100644
--- a/lib/custom_wizard/mapper.rb
+++ b/lib/custom_wizard/mapper.rb
@@ -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 opts[:template] && @pro.subscribed?
template = Liquid::Template.parse(string)
string = template.render(data)
end
diff --git a/lib/custom_wizard/pro.rb b/lib/custom_wizard/pro.rb
new file mode 100644
index 00000000..61097069
--- /dev/null
+++ b/lib/custom_wizard/pro.rb
@@ -0,0 +1,186 @@
+# frozen_string_literal: true
+
+class CustomWizard::Pro
+ include ActiveModel::Serialization
+
+ attr_accessor :authentication,
+ :subscription
+
+ def initialize
+ @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
+ "test.thepavilion.io"
+ end
+
+ def subscription_type
+ "stripe"
+ end
+
+ def client_name
+ "custom-wizard"
+ end
+
+ def scope
+ "discourse-subscription-server:user_subscription"
+ end
+
+ def update_subscription
+ 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::ProSubscription.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/pro/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::ProAuthentication.new(get_authentication)
+ !@authentication.active?
+ else
+ false
+ end
+ end
+
+ def self.subscribed?
+ self.new.subscribed?
+ end
+
+ def self.authorized?
+ self.new.authorized?
+ end
+
+ def self.update_subscription
+ self.new.update_subscription
+ end
+
+ 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)
+ get_authentication
+ end
+end
diff --git a/lib/custom_wizard/pro/authentication.rb b/lib/custom_wizard/pro/authentication.rb
new file mode 100644
index 00000000..23603898
--- /dev/null
+++ b/lib/custom_wizard/pro/authentication.rb
@@ -0,0 +1,95 @@
+# frozen_string_literal: true
+class CustomWizard::ProAuthentication
+ 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::Pro.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::Pro.namespace, "#{keys_db_key}_#{request_id}",
+ user_id: user_id,
+ pem: rsa.export,
+ nonce: nonce
+ )
+ end
+
+ 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
diff --git a/lib/custom_wizard/pro/subscription.rb b/lib/custom_wizard/pro/subscription.rb
new file mode 100644
index 00000000..7f5cf911
--- /dev/null
+++ b/lib/custom_wizard/pro/subscription.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+class CustomWizard::ProSubscription
+ 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(community business)
+ end
+
+ def active?
+ types.include?(type) && updated_at.to_datetime > (Time.zone.now - 2.hours).to_datetime
+ end
+end
diff --git a/lib/custom_wizard/validators/template.rb b/lib/custom_wizard/validators/template.rb
index c90944e9..839a070f 100644
--- a/lib/custom_wizard/validators/template.rb
+++ b/lib/custom_wizard/validators/template.rb
@@ -6,6 +6,7 @@ class CustomWizard::TemplateValidator
def initialize(data, opts = {})
@data = data
@opts = opts
+ @pro = CustomWizard::Pro.new
end
def perform
@@ -14,12 +15,15 @@ class CustomWizard::TemplateValidator
check_id(data, :wizard)
check_required(data, :wizard)
validate_after_time
+ validate_pro(data, :wizard)
data[:steps].each do |step|
check_required(step, :step)
+ validate_pro(step, :step)
- if data[:fields].present?
- data[:fields].each do |field|
+ if step[:fields].present?
+ step[:fields].each do |field|
+ validate_pro(field, :field)
check_required(field, :field)
end
end
@@ -27,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
@@ -47,16 +52,53 @@ class CustomWizard::TemplateValidator
}
end
+ def self.pro
+ {
+ wizard: {},
+ step: {
+ condition: 'present',
+ index: 'conditional'
+ },
+ field: {
+ condition: 'present',
+ index: 'conditional'
+ },
+ action: {
+ type: %w[
+ send_message
+ add_to_group
+ create_category
+ create_group
+ send_to_api
+ ]
+ }
+ }
+ end
+
private
def check_required(object, type)
- 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
end
end
+ def validate_pro(object, type)
+ self.class.pro[type].each do |property, pro_type|
+ is_pro = object[property.to_s].present? && (
+ pro_type === 'present' ||
+ (pro_type === 'conditional' && object[property.to_s].is_a?(Hash)) ||
+ (pro_type.is_a?(Array) && pro_type.include?(object[property.to_s]))
+ )
+
+ if is_pro && !@pro.subscribed?
+ errors.add :base, I18n.t("wizard.validation.pro", type: type.to_s, property: property)
+ end
+ end
+ end
+
def check_id(object, type)
if type === :wizard && @opts[:create] && CustomWizard::Template.exists?(object[:id])
errors.add :base, I18n.t("wizard.validation.conflict", wizard_id: object[:id])
diff --git a/lib/custom_wizard/wizard.rb b/lib/custom_wizard/wizard.rb
index e52feec4..98e7938b 100644
--- a/lib/custom_wizard/wizard.rb
+++ b/lib/custom_wizard/wizard.rb
@@ -310,10 +310,10 @@ class CustomWizard::Wizard
end
end
- def self.list(user, template_opts: {}, not_completed: false)
+ def self.list(user, template_opts = {}, not_completed = false)
return [] unless user
- CustomWizard::Template.list(template_opts).reduce([]) do |result, template|
+ CustomWizard::Template.list(**template_opts).reduce([]) do |result, template|
wizard = new(template, user)
result.push(wizard) if wizard.can_access? && (
!not_completed || !wizard.completed?
@@ -325,7 +325,7 @@ class CustomWizard::Wizard
def self.after_signup(user)
wizards = list(
user,
- template_opts: {
+ {
setting: 'after_signup',
order: "(value::json ->> 'permitted') IS NOT NULL DESC"
}
@@ -336,11 +336,11 @@ class CustomWizard::Wizard
def self.prompt_completion(user)
wizards = list(
user,
- template_opts: {
+ {
setting: 'prompt_completion',
order: "(value::json ->> 'permitted') IS NOT NULL DESC"
},
- not_completed: true
+ true
)
if wizards.any?
wizards.map do |w|
diff --git a/plugin.rb b/plugin.rb
index 58811291..808975d6 100644
--- a/plugin.rb
+++ b/plugin.rb
@@ -71,11 +71,13 @@ after_initialize do
../controllers/custom_wizard/admin/logs.rb
../controllers/custom_wizard/admin/manager.rb
../controllers/custom_wizard/admin/custom_fields.rb
+ ../controllers/custom_wizard/admin/pro.rb
../controllers/custom_wizard/wizard.rb
../controllers/custom_wizard/steps.rb
../controllers/custom_wizard/realtime_validations.rb
- ../jobs/refresh_api_access_token.rb
- ../jobs/set_after_time_wizard.rb
+ ../jobs/regular/refresh_api_access_token.rb
+ ../jobs/regular/set_after_time_wizard.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
@@ -94,6 +96,9 @@ after_initialize do
../lib/custom_wizard/submission.rb
../lib/custom_wizard/template.rb
../lib/custom_wizard/wizard.rb
+ ../lib/custom_wizard/pro.rb
+ ../lib/custom_wizard/pro/subscription.rb
+ ../lib/custom_wizard/pro/authentication.rb
../lib/custom_wizard/api/api.rb
../lib/custom_wizard/api/authorization.rb
../lib/custom_wizard/api/endpoint.rb
@@ -114,6 +119,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/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
diff --git a/serializers/custom_wizard/pro/authentication_serializer.rb b/serializers/custom_wizard/pro/authentication_serializer.rb
new file mode 100644
index 00000000..0a2915e4
--- /dev/null
+++ b/serializers/custom_wizard/pro/authentication_serializer.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+class CustomWizard::ProAuthenticationSerializer < ApplicationSerializer
+ attributes :active,
+ :client_id,
+ :auth_by,
+ :auth_at
+
+ def active
+ object.active?
+ end
+end
diff --git a/serializers/custom_wizard/pro/subscription_serializer.rb b/serializers/custom_wizard/pro/subscription_serializer.rb
new file mode 100644
index 00000000..d3e04e4e
--- /dev/null
+++ b/serializers/custom_wizard/pro/subscription_serializer.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+class CustomWizard::ProSubscriptionSerializer < ApplicationSerializer
+ attributes :type,
+ :active,
+ :updated_at
+
+ def active
+ object.active?
+ end
+end
diff --git a/serializers/custom_wizard/pro_serializer.rb b/serializers/custom_wizard/pro_serializer.rb
new file mode 100644
index 00000000..2f141c6d
--- /dev/null
+++ b/serializers/custom_wizard/pro_serializer.rb
@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+class CustomWizard::ProSerializer < ApplicationSerializer
+ attributes :server
+ has_one :authentication, serializer: CustomWizard::ProAuthenticationSerializer, embed: :objects
+ has_one :subscription, serializer: CustomWizard::ProSubscriptionSerializer, embed: :objects
+end
diff --git a/spec/components/custom_wizard/action_spec.rb b/spec/components/custom_wizard/action_spec.rb
index 8b617c39..34a08461 100644
--- a/spec/components/custom_wizard/action_spec.rb
+++ b/spec/components/custom_wizard/action_spec.rb
@@ -6,26 +6,22 @@ describe CustomWizard::Action do
fab!(:category) { Fabricate(:category, name: 'cat1', slug: 'cat-slug') }
fab!(:group) { Fabricate(:group) }
- let(:wizard_template) {
- JSON.parse(
- File.open(
- "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
- ).read
- )
- }
+ let(:wizard_template) { get_wizard_fixture("wizard") }
+ let(:open_composer) { get_wizard_fixture("actions/open_composer") }
+ let(:create_category) { get_wizard_fixture("actions/create_category") }
+ let(:create_group) { get_wizard_fixture("actions/create_group") }
+ let(:add_to_group) { get_wizard_fixture("actions/add_to_group") }
+ let(:send_message) { get_wizard_fixture("actions/send_message") }
+ let(:send_message_multi) { get_wizard_fixture("actions/send_message_multi") }
- let(:open_composer) {
- JSON.parse(
- File.open(
- "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/actions/open_composer.json"
- ).read
- )
- }
+ def update_template(template)
+ CustomWizard::Template.save(template, skip_jobs: true)
+ @template = CustomWizard::Template.find('super_mega_fun_wizard')
+ end
before do
Group.refresh_automatic_group!(:trust_level_2)
- CustomWizard::Template.save(wizard_template, skip_jobs: true)
- @template = CustomWizard::Template.find('super_mega_fun_wizard')
+ update_template(wizard_template)
end
context 'creating a topic' do
@@ -110,54 +106,6 @@ describe CustomWizard::Action do
end
end
- context 'sending a message' do
- it 'works' do
- User.create(username: 'angus1', email: "angus1@email.com")
-
- wizard = CustomWizard::Builder.new(@template[:id], user).build
- wizard.create_updater(wizard.steps[0].id, {}).update
- wizard.create_updater(wizard.steps[1].id, {}).update
-
- topic = Topic.where(
- archetype: Archetype.private_message,
- title: "Message title"
- )
-
- post = Post.where(
- topic_id: topic.pluck(:id),
- raw: "I will interpolate some wizard fields"
- )
-
- expect(topic.exists?).to eq(true)
- expect(topic.first.topic_allowed_users.first.user.username).to eq('angus1')
- expect(post.exists?).to eq(true)
- end
-
- it 'allows using multiple PM targets' do
- User.create(username: 'angus1', email: "angus1@email.com")
- User.create(username: 'faiz', email: "faiz@email.com")
- Group.create(name: "cool_group")
- Group.create(name: 'cool_group_1')
- wizard = CustomWizard::Builder.new(@template[:id], user).build
- wizard.create_updater(wizard.steps[0].id, {}).update
- wizard.create_updater(wizard.steps[1].id, {}).update
-
- topic = Topic.where(
- archetype: Archetype.private_message,
- title: "Multiple Recipients title"
- )
-
- post = Post.where(
- topic_id: topic.pluck(:id),
- raw: "I will interpolate some wizard fields"
- )
- expect(topic.exists?).to eq(true)
- expect(topic.first.all_allowed_users.map(&:username)).to include('angus1', 'faiz')
- expect(topic.first.allowed_groups.map(&:name)).to include('cool_group', 'cool_group_1')
- expect(post.exists?).to eq(true)
- end
- end
-
it 'updates a profile' do
wizard = CustomWizard::Builder.new(@template[:id], user).build
upload = Upload.create!(
@@ -182,10 +130,8 @@ describe CustomWizard::Action do
updater = wizard.create_updater(wizard.steps[1].id, {})
updater.update
- category = Category.find_by(id: wizard.current_submission.fields['action_8'])
-
expect(updater.result[:redirect_on_next]).to eq(
- "/new-topic?title=Title%20of%20the%20composer%20topic&body=I%20am%20interpolating%20some%20user%20fields%20Angus%20angus%20angus%40email.com&category_id=#{category.id}&tags=tag1"
+ "/new-topic?title=Title%20of%20the%20composer%20topic&body=I%20am%20interpolating%20some%20user%20fields%20Angus%20angus%20angus%40email.com&tags=tag1"
)
end
@@ -210,35 +156,11 @@ describe CustomWizard::Action do
end
end
- it 'creates a category' do
- wizard = CustomWizard::Builder.new(@template[:id], user).build
- wizard.create_updater(wizard.steps[0].id, step_1_field_1: "Text input").update
- wizard.create_updater(wizard.steps[1].id, {}).update
- expect(Category.where(id: wizard.current_submission.fields['action_8']).exists?).to eq(true)
- end
-
- it 'creates a group' do
- wizard = CustomWizard::Builder.new(@template[:id], user).build
- wizard.create_updater(wizard.steps[0].id, step_1_field_1: "Text input").update
- expect(Group.where(name: wizard.current_submission.fields['action_9']).exists?).to eq(true)
- end
-
- it 'adds a user to a group' do
- wizard = CustomWizard::Builder.new(@template[:id], user).build
- step_id = wizard.steps[0].id
- updater = wizard.create_updater(step_id, step_1_field_1: "Text input").update
- group = Group.find_by(name: wizard.current_submission.fields['action_9'])
- expect(group.users.first.username).to eq('angus')
- end
-
it 'watches categories' do
wizard = CustomWizard::Builder.new(@template[:id], user).build
wizard.create_updater(wizard.steps[0].id, step_1_field_1: "Text input").update
wizard.create_updater(wizard.steps[1].id, {}).update
- expect(CategoryUser.where(
- category_id: wizard.current_submission.fields['action_8'],
- user_id: user.id
- ).first.notification_level).to eq(2)
+
expect(CategoryUser.where(
category_id: category.id,
user_id: user.id
@@ -251,4 +173,97 @@ describe CustomWizard::Action do
updater.update
expect(updater.result[:redirect_on_next]).to eq("https://google.com")
end
+
+ context "pro actions" do
+ before do
+ enable_pro
+ end
+
+ it '#send_message' do
+ wizard_template['actions'] << send_message
+ update_template(wizard_template)
+
+ User.create(username: 'angus1', email: "angus1@email.com")
+
+ wizard = CustomWizard::Builder.new(@template[:id], user).build
+ wizard.create_updater(wizard.steps[0].id, {}).update
+ wizard.create_updater(wizard.steps[1].id, {}).update
+
+ topic = Topic.where(
+ archetype: Archetype.private_message,
+ title: "Message title"
+ )
+
+ post = Post.where(
+ topic_id: topic.pluck(:id),
+ raw: "I will interpolate some wizard fields"
+ )
+
+ expect(topic.exists?).to eq(true)
+ expect(topic.first.topic_allowed_users.first.user.username).to eq('angus1')
+ expect(post.exists?).to eq(true)
+ end
+
+ it '#send_message allows using multiple targets' do
+ wizard_template['actions'] << send_message_multi
+ update_template(wizard_template)
+
+ User.create(username: 'angus1', email: "angus1@email.com")
+ User.create(username: 'faiz', email: "faiz@email.com")
+ Group.create(name: "cool_group")
+ Group.create(name: 'cool_group_1')
+ wizard = CustomWizard::Builder.new(@template[:id], user).build
+ wizard.create_updater(wizard.steps[0].id, {}).update
+ wizard.create_updater(wizard.steps[1].id, {}).update
+
+ topic = Topic.where(
+ archetype: Archetype.private_message,
+ title: "Multiple Recipients title"
+ )
+
+ post = Post.where(
+ topic_id: topic.pluck(:id),
+ raw: "I will interpolate some wizard fields"
+ )
+
+ expect(topic.exists?).to eq(true)
+ expect(topic.first.all_allowed_users.map(&:username)).to include('angus1', 'faiz')
+ expect(topic.first.allowed_groups.map(&:name)).to include('cool_group', 'cool_group_1')
+ expect(post.exists?).to eq(true)
+ end
+
+ it '#create_category' do
+ wizard_template['actions'] << create_category
+ update_template(wizard_template)
+
+ wizard = CustomWizard::Builder.new(@template[:id], user).build
+ wizard.create_updater(wizard.steps[0].id, step_1_field_1: "Text input").update
+ wizard.create_updater(wizard.steps[1].id, {}).update
+
+ expect(Category.where(id: wizard.current_submission.fields['action_8']).exists?).to eq(true)
+ end
+
+ it '#create_group' do
+ wizard_template['actions'] << create_group
+ update_template(wizard_template)
+
+ wizard = CustomWizard::Builder.new(@template[:id], user).build
+ wizard.create_updater(wizard.steps[0].id, step_1_field_1: "Text input").update
+
+ expect(Group.where(name: wizard.current_submission.fields['action_9']).exists?).to eq(true)
+ end
+
+ it '#add_to_group' do
+ wizard_template['actions'] << create_group
+ wizard_template['actions'] << add_to_group
+ update_template(wizard_template)
+
+ wizard = CustomWizard::Builder.new(@template[:id], user).build
+ step_id = wizard.steps[0].id
+ updater = wizard.create_updater(step_id, step_1_field_1: "Text input").update
+ group = Group.find_by(name: wizard.current_submission.fields['action_9'])
+
+ expect(group.users.first.username).to eq('angus')
+ end
+ end
end
diff --git a/spec/components/custom_wizard/builder_spec.rb b/spec/components/custom_wizard/builder_spec.rb
index 8e80d806..9b9c6000 100644
--- a/spec/components/custom_wizard/builder_spec.rb
+++ b/spec/components/custom_wizard/builder_spec.rb
@@ -15,45 +15,15 @@ describe CustomWizard::Builder do
fab!(:category2) { Fabricate(:category, name: 'cat2') }
fab!(:group) { Fabricate(:group) }
- let(:required_data_json) {
- JSON.parse(
- File.open(
- "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/step/required_data.json"
- ).read
- )
- }
-
- let(:permitted_json) {
- JSON.parse(
- File.open(
- "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard/permitted.json"
- ).read
- )
- }
-
- let(:permitted_param_json) {
- JSON.parse(
- File.open(
- "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/step/permitted_params.json"
- ).read
- )
- }
-
- let(:user_condition_json) {
- JSON.parse(
- File.open(
- "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/condition/user_condition.json"
- ).read
- )
- }
+ let(:wizard_template) { get_wizard_fixture("wizard") }
+ let(:required_data_json) { get_wizard_fixture("step/required_data") }
+ let(:permitted_json) { get_wizard_fixture("wizard/permitted") }
+ let(:permitted_param_json) { get_wizard_fixture("step/permitted_params") }
+ let(:user_condition_json) { get_wizard_fixture("condition/user_condition") }
before do
Group.refresh_automatic_group!(:trust_level_3)
- CustomWizard::Template.save(
- JSON.parse(File.open(
- "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
- ).read),
- skip_jobs: true)
+ CustomWizard::Template.save(wizard_template, skip_jobs: true)
@template = CustomWizard::Template.find('super_mega_fun_wizard')
end
@@ -283,6 +253,7 @@ describe CustomWizard::Builder do
context "with condition" do
before do
+ enable_pro
@template[:steps][0][:condition] = user_condition_json['condition']
CustomWizard::Template.save(@template.as_json)
end
@@ -321,6 +292,7 @@ describe CustomWizard::Builder do
context "with condition" do
before do
+ enable_pro
@template[:steps][0][:fields][0][:condition] = user_condition_json['condition']
CustomWizard::Template.save(@template.as_json)
end
diff --git a/spec/components/custom_wizard/custom_field_spec.rb b/spec/components/custom_wizard/custom_field_spec.rb
index b17e26c6..2204264f 100644
--- a/spec/components/custom_wizard/custom_field_spec.rb
+++ b/spec/components/custom_wizard/custom_field_spec.rb
@@ -3,12 +3,8 @@
require_relative '../../plugin_helper'
describe CustomWizard::CustomField do
-
- let(:custom_field_json) {
- JSON.parse(File.open(
- "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/custom_field/custom_fields.json"
- ).read)
- }
+ let(:custom_field_json) { get_wizard_fixture("custom_field/custom_fields") }
+ let(:custom_field_pro_json) { get_wizard_fixture("custom_field/pro_custom_fields") }
before do
CustomWizard::CustomField.invalidate_cache
@@ -104,8 +100,8 @@ describe CustomWizard::CustomField do
it "does not save with an unsupported serializer" do
invalid_field_json = custom_field_json['custom_fields'].first
- invalid_field_json['klass'] = 'category'
- invalid_field_json['serializers'] = ['category', 'site_category']
+ invalid_field_json['klass'] = 'post'
+ invalid_field_json['serializers'] = ['post', 'post_revision']
custom_field = CustomWizard::CustomField.new(nil, invalid_field_json)
@@ -113,8 +109,8 @@ describe CustomWizard::CustomField do
expect(custom_field.valid?).to eq(false)
expect(custom_field.errors.full_messages.first).to eq(
I18n.t("wizard.custom_field.error.unsupported_serializers",
- class: "category",
- serializers: "category, site_category"
+ class: "post",
+ serializers: "post_revision"
)
)
expect(
@@ -196,6 +192,50 @@ describe CustomWizard::CustomField do
).exists?
).to eq(false)
end
+
+ it "does not save pro field types without a pro subscription" do
+ pro_field_json = custom_field_pro_json['custom_fields'].first
+ custom_field = CustomWizard::CustomField.new(nil, pro_field_json)
+
+ expect(custom_field.save).to eq(false)
+ expect(custom_field.valid?).to eq(false)
+ expect(custom_field.errors.full_messages.first).to eq(
+ I18n.t("wizard.custom_field.error.pro_type", type: "json")
+ )
+ end
+
+ it "does not save pro field classes without a pro subscription" do
+ pro_field_json = custom_field_pro_json['custom_fields'].second
+ custom_field = CustomWizard::CustomField.new(nil, pro_field_json)
+
+ expect(custom_field.save).to eq(false)
+ expect(custom_field.valid?).to eq(false)
+ expect(custom_field.errors.full_messages.first).to eq(
+ I18n.t("wizard.custom_field.error.pro_type", type: "category")
+ )
+ end
+
+ context "with a pro subscription" do
+ before do
+ enable_pro
+ end
+
+ it "saves pro field types" do
+ pro_field_json = custom_field_pro_json['custom_fields'].first
+ custom_field = CustomWizard::CustomField.new(nil, pro_field_json)
+
+ expect(custom_field.save).to eq(true)
+ expect(custom_field.valid?).to eq(true)
+ end
+
+ it "saves pro field classes" do
+ pro_field_json = custom_field_pro_json['custom_fields'].second
+ custom_field = CustomWizard::CustomField.new(nil, pro_field_json)
+
+ expect(custom_field.save).to eq(true)
+ expect(custom_field.valid?).to eq(true)
+ end
+ end
end
context "lists" do
@@ -205,15 +245,15 @@ describe CustomWizard::CustomField do
end
end
- it "lists saved custom field records" do
- expect(CustomWizard::CustomField.list.length).to eq(4)
+ it "saved custom field records" do
+ expect(CustomWizard::CustomField.list.length).to eq(2)
end
- it "lists saved custom field records by attribute value" do
+ it "saved custom field records by attribute value" do
expect(CustomWizard::CustomField.list_by(:klass, 'topic').length).to eq(1)
end
- it "lists saved custom field records by optional values" do
+ it "saved custom field records by optional values" do
field_json = custom_field_json['custom_fields'].first
field_json['serializers'] = nil
@@ -221,12 +261,12 @@ describe CustomWizard::CustomField do
expect(CustomWizard::CustomField.list_by(:serializers, ['post']).length).to eq(0)
end
- it "lists custom field records added by other plugins " do
- expect(CustomWizard::CustomField.external_list.length).to eq(11)
+ it "custom field records added by other plugins " do
+ expect(CustomWizard::CustomField.external_list.length).to be > 10
end
- it "lists all custom field records" do
- expect(CustomWizard::CustomField.full_list.length).to eq(15)
+ it "all custom field records" do
+ expect(CustomWizard::CustomField.full_list.length).to be > 12
end
end
diff --git a/spec/components/custom_wizard/field_spec.rb b/spec/components/custom_wizard/field_spec.rb
index 871c42cd..0fcf9fc2 100644
--- a/spec/components/custom_wizard/field_spec.rb
+++ b/spec/components/custom_wizard/field_spec.rb
@@ -2,11 +2,7 @@
require_relative '../../plugin_helper'
describe CustomWizard::Field do
- let(:field_hash) do
- JSON.parse(File.open(
- "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/field/field.json"
- ).read).with_indifferent_access
- end
+ let(:field_hash) { get_wizard_fixture("field/field") }
before do
CustomWizard::Field.register(
diff --git a/spec/components/custom_wizard/mapper_spec.rb b/spec/components/custom_wizard/mapper_spec.rb
index ed66d7c1..b210c588 100644
--- a/spec/components/custom_wizard/mapper_spec.rb
+++ b/spec/components/custom_wizard/mapper_spec.rb
@@ -31,16 +31,8 @@ describe CustomWizard::Mapper do
]
)
}
- let(:inputs) {
- JSON.parse(File.open(
- "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/mapper/inputs.json"
- ).read)
- }
- let(:data) {
- JSON.parse(File.open(
- "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/mapper/data.json"
- ).read)
- }
+ let(:inputs) { get_wizard_fixture("mapper/inputs") }
+ let(:data) { get_wizard_fixture("mapper/data") }
let(:template_params) {
{
"step_1_field_1" => "Hello"
@@ -352,7 +344,7 @@ describe CustomWizard::Mapper do
expect(result).to eq(template_params["step_1_field_1"])
end
- it "treats replaced values as string literals" do
+ it "requires a pro subscription" do
template = '{{ "w{step_1_field_1}" | size }}'
mapper = create_template_mapper(template_params, user1)
result = mapper.interpolate(
@@ -362,60 +354,17 @@ describe CustomWizard::Mapper do
wizard: true,
value: true
)
- expect(result).to eq(template_params["step_1_field_1"].size.to_s)
+ expect(result).to eq("{{ \"#{template_params["step_1_field_1"]}\" | size }}")
end
- it "allows the wizard values to be used inside conditionals" do
- template = <<-LIQUID
- {%- if "w{step_1_field_1}" contains "ello" -%}
- Correct
- {%- else -%}
- Incorrect
- {%-endif-%}
- LIQUID
- mapper = create_template_mapper(template_params, user1)
- result = mapper.interpolate(
- template.dup,
- template: true,
- user: true,
- wizard: true,
- value: true
- )
- expect(result).to eq("Correct")
- end
+ context "with a pro subscription" do
+ before do
+ enable_pro
+ end
- it "can access data passed to render method as variable" do
- template = "{{step_1_field_1.size}}"
- mapper = create_template_mapper(template_params, user1)
- result = mapper.interpolate(
- template.dup,
- template: true,
- user: true,
- wizard: true,
- value: true
- )
- expect(result).to eq(template_params["step_1_field_1"].size.to_s)
- end
-
- it "doesn't parse the template when template param is false" do
- template = <<-LIQUID.strip
- {{ "w{step_1_field_1}" | size}}
- LIQUID
- mapper = create_template_mapper(template_params, user1)
- result = mapper.interpolate(
- template.dup,
- template: false,
- )
- expect(result).to eq(template)
- end
-
- context "custom filter: 'first_non_empty'" do
- it "gives first non empty element from list" do
- template = <<-LIQUID.strip
- {%- assign entry = "" | first_non_empty: step_1_field_1, step_1_field_2, step_1_field_3 -%}
- {{ entry }}
- LIQUID
- mapper = create_template_mapper(template_params_non_empty, user1)
+ it "treats replaced values as string literals" do
+ template = '{{ "w{step_1_field_1}" | size }}'
+ mapper = create_template_mapper(template_params, user1)
result = mapper.interpolate(
template.dup,
template: true,
@@ -423,15 +372,18 @@ describe CustomWizard::Mapper do
wizard: true,
value: true
)
- expect(result).to eq(template_params_non_empty["step_1_field_3"])
+ expect(result).to eq(template_params["step_1_field_1"].size.to_s)
end
- it "gives first non empty element from list when multiple non empty values present" do
- template = <<-LIQUID.strip
- {%- assign entry = "" | first_non_empty: step_1_field_1, step_1_field_2, step_1_field_3 -%}
- {{ entry }}
+ it "allows the wizard values to be used inside conditionals" do
+ template = <<-LIQUID
+ {%- if "w{step_1_field_1}" contains "ello" -%}
+ Correct
+ {%- else -%}
+ Incorrect
+ {%-endif-%}
LIQUID
- mapper = create_template_mapper(template_params_multiple_non_empty, user1)
+ mapper = create_template_mapper(template_params, user1)
result = mapper.interpolate(
template.dup,
template: true,
@@ -439,25 +391,84 @@ describe CustomWizard::Mapper do
wizard: true,
value: true
)
- expect(result).to eq(template_params_multiple_non_empty["step_1_field_2"])
+ expect(result).to eq("Correct")
end
- it "gives empty if all elements are empty" do
+ it "can access data passed to render method as variable" do
+ template = "{{step_1_field_1.size}}"
+ mapper = create_template_mapper(template_params, user1)
+ result = mapper.interpolate(
+ template.dup,
+ template: true,
+ user: true,
+ wizard: true,
+ value: true
+ )
+ expect(result).to eq(template_params["step_1_field_1"].size.to_s)
+ end
+
+ it "doesn't parse the template when template param is false" do
template = <<-LIQUID.strip
- {%- assign entry = "" | first_non_empty: step_1_field_1, step_1_field_2, step_1_field_3 -%}
- {%- if entry -%}
+ {{ "w{step_1_field_1}" | size}}
+ LIQUID
+ mapper = create_template_mapper(template_params, user1)
+ result = mapper.interpolate(
+ template.dup,
+ template: false,
+ )
+ expect(result).to eq(template)
+ end
+
+ context "custom filter: 'first_non_empty'" do
+ it "gives first non empty element from list" do
+ template = <<-LIQUID.strip
+ {%- assign entry = "" | first_non_empty: step_1_field_1, step_1_field_2, step_1_field_3 -%}
{{ entry }}
- {%- endif -%}
- LIQUID
- mapper = create_template_mapper(template_params_empty, user1)
- result = mapper.interpolate(
- template.dup,
- template: true,
- user: true,
- wizard: true,
- value: true
- )
- expect(result).to eq("")
+ LIQUID
+ mapper = create_template_mapper(template_params_non_empty, user1)
+ result = mapper.interpolate(
+ template.dup,
+ template: true,
+ user: true,
+ wizard: true,
+ value: true
+ )
+ expect(result).to eq(template_params_non_empty["step_1_field_3"])
+ end
+
+ it "gives first non empty element from list when multiple non empty values present" do
+ template = <<-LIQUID.strip
+ {%- assign entry = "" | first_non_empty: step_1_field_1, step_1_field_2, step_1_field_3 -%}
+ {{ entry }}
+ LIQUID
+ mapper = create_template_mapper(template_params_multiple_non_empty, user1)
+ result = mapper.interpolate(
+ template.dup,
+ template: true,
+ user: true,
+ wizard: true,
+ value: true
+ )
+ expect(result).to eq(template_params_multiple_non_empty["step_1_field_2"])
+ end
+
+ it "gives empty if all elements are empty" do
+ template = <<-LIQUID.strip
+ {%- assign entry = "" | first_non_empty: step_1_field_1, step_1_field_2, step_1_field_3 -%}
+ {%- if entry -%}
+ {{ entry }}
+ {%- endif -%}
+ LIQUID
+ mapper = create_template_mapper(template_params_empty, user1)
+ result = mapper.interpolate(
+ template.dup,
+ template: true,
+ user: true,
+ wizard: true,
+ value: true
+ )
+ expect(result).to eq("")
+ end
end
end
end
diff --git a/spec/components/custom_wizard/pro_spec.rb b/spec/components/custom_wizard/pro_spec.rb
new file mode 100644
index 00000000..6499b668
--- /dev/null
+++ b/spec/components/custom_wizard/pro_spec.rb
@@ -0,0 +1,125 @@
+# frozen_string_literal: true
+
+require_relative '../../plugin_helper'
+
+describe CustomWizard::Pro do
+ fab!(:user) { Fabricate(:user) }
+
+ it "initializes pro authentication and subscription" do
+ pro = described_class.new
+ expect(pro.authentication.class).to eq(CustomWizard::ProAuthentication)
+ expect(pro.subscription.class).to eq(CustomWizard::ProSubscription)
+ end
+
+ it "returns authorized and subscribed states" do
+ pro = described_class.new
+ expect(pro.authorized?).to eq(false)
+ expect(pro.subscribed?).to eq(false)
+ end
+
+ context "subscription" do
+ before do
+ @pro = described_class.new
+ end
+
+ it "updates valid subscriptions" do
+ stub_subscription_request(200, valid_subscription)
+ expect(@pro.update_subscription).to eq(true)
+ expect(@pro.subscribed?).to eq(true)
+ end
+
+ it "handles invalid subscriptions" do
+ stub_subscription_request(200, invalid_subscription)
+ expect(@pro.update_subscription).to eq(false)
+ expect(@pro.subscribed?).to eq(false)
+ end
+
+ it "handles subscription http errors" do
+ stub_subscription_request(404, {})
+ expect(@pro.update_subscription).to eq(false)
+ expect(@pro.subscribed?).to eq(false)
+ end
+
+ it "destroys subscriptions" do
+ stub_subscription_request(200, valid_subscription)
+ expect(@pro.update_subscription).to eq(true)
+ expect(@pro.destroy_subscription).to eq(true)
+ expect(@pro.subscribed?).to eq(false)
+ end
+
+ it "has class aliases" do
+ authenticate_pro
+ stub_subscription_request(200, valid_subscription)
+ expect(described_class.update_subscription).to eq(true)
+ expect(described_class.subscribed?).to eq(true)
+ end
+ end
+
+ context "authentication" do
+ before do
+ @pro = described_class.new
+ user.update!(admin: true)
+ end
+
+ it "generates a valid authentication request url" do
+ request_id = SecureRandom.hex(32)
+ uri = URI(@pro.authentication_url(user.id, request_id))
+ expect(uri.host).to eq(@pro.server)
+
+ parsed_query = Rack::Utils.parse_query uri.query
+ expect(parsed_query['public_key'].present?).to eq(true)
+ expect(parsed_query['nonce'].present?).to eq(true)
+ expect(parsed_query['client_id'].present?).to eq(true)
+ expect(parsed_query['auth_redirect'].present?).to eq(true)
+ expect(parsed_query['application_name']).to eq(SiteSetting.title)
+ expect(parsed_query['scopes']).to eq(@pro.scope)
+ end
+
+ def generate_payload(request_id, user_id)
+ uri = URI(@pro.authentication_url(user_id, request_id))
+ keys = @pro.authentication.get_keys(request_id)
+ raw_payload = {
+ key: "12345",
+ nonce: keys.nonce,
+ push: false,
+ api: UserApiKeysController::AUTH_API_VERSION
+ }.to_json
+ public_key = OpenSSL::PKey::RSA.new(keys.pem)
+ Base64.encode64(public_key.public_encrypt(raw_payload))
+ end
+
+ it "handles authentication response if request and response is valid" do
+ request_id = SecureRandom.hex(32)
+ payload = generate_payload(request_id, user.id)
+
+ expect(@pro.authentication_response(request_id, payload)).to eq(true)
+ expect(@pro.authorized?).to eq(true)
+ end
+
+ it "discards authentication response if user who made request as not an admin" do
+ user.update!(admin: false)
+
+ request_id = SecureRandom.hex(32)
+ payload = generate_payload(request_id, user.id)
+
+ expect(@pro.authentication_response(request_id, payload)).to eq(false)
+ expect(@pro.authorized?).to eq(false)
+ end
+
+ it "discards authentication response if request_id is invalid" do
+ payload = generate_payload(SecureRandom.hex(32), user.id)
+
+ expect(@pro.authentication_response(SecureRandom.hex(32), payload)).to eq(false)
+ expect(@pro.authorized?).to eq(false)
+ end
+
+ it "destroys authentication" do
+ request_id = SecureRandom.hex(32)
+ payload = generate_payload(request_id, user.id)
+ @pro.authentication_response(request_id, payload)
+
+ expect(@pro.destroy_authentication).to eq(true)
+ expect(@pro.authorized?).to eq(false)
+ end
+ end
+end
diff --git a/spec/components/custom_wizard/step_spec.rb b/spec/components/custom_wizard/step_spec.rb
index bf4613a4..9ad02176 100644
--- a/spec/components/custom_wizard/step_spec.rb
+++ b/spec/components/custom_wizard/step_spec.rb
@@ -2,21 +2,8 @@
require_relative '../../plugin_helper'
describe CustomWizard::Step do
- let(:step_hash) do
- JSON.parse(
- File.open(
- "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/step/step.json"
- ).read
- ).with_indifferent_access
- end
-
- let(:field_hash) do
- JSON.parse(
- File.open(
- "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/field/field.json"
- ).read
- ).with_indifferent_access
- end
+ let(:step_hash) { get_wizard_fixture("step/step") }
+ let(:field_hash) { get_wizard_fixture("field/field") }
before do
@step = CustomWizard::Step.new(step_hash[:id])
diff --git a/spec/components/custom_wizard/submission_spec.rb b/spec/components/custom_wizard/submission_spec.rb
index 4e8d86c0..a6c8a0d3 100644
--- a/spec/components/custom_wizard/submission_spec.rb
+++ b/spec/components/custom_wizard/submission_spec.rb
@@ -4,12 +4,7 @@ require_relative '../../plugin_helper'
describe CustomWizard::Submission do
fab!(:user) { Fabricate(:user) }
fab!(:user2) { Fabricate(:user) }
-
- let(:template_json) {
- JSON.parse(File.open(
- "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
- ).read)
- }
+ let(:template_json) { get_wizard_fixture("wizard") }
before do
CustomWizard::Template.save(template_json, skip_jobs: true)
diff --git a/spec/components/custom_wizard/template_spec.rb b/spec/components/custom_wizard/template_spec.rb
index 0e3dbdbe..fca7f91e 100644
--- a/spec/components/custom_wizard/template_spec.rb
+++ b/spec/components/custom_wizard/template_spec.rb
@@ -3,17 +3,8 @@ require_relative '../../plugin_helper'
describe CustomWizard::Template do
fab!(:user) { Fabricate(:user) }
-
- let(:template_json) {
- JSON.parse(File.open(
- "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
- ).read)
- }
- let(:permitted_json) {
- JSON.parse(File.open(
- "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard/permitted.json"
- ).read)
- }
+ let(:template_json) { get_wizard_fixture("wizard") }
+ let(:permitted_json) { get_wizard_fixture("wizard/permitted") }
before do
CustomWizard::Template.save(template_json, skip_jobs: true)
diff --git a/spec/components/custom_wizard/template_validator_spec.rb b/spec/components/custom_wizard/template_validator_spec.rb
index c8ce915a..7a84660c 100644
--- a/spec/components/custom_wizard/template_validator_spec.rb
+++ b/spec/components/custom_wizard/template_validator_spec.rb
@@ -3,12 +3,9 @@ require_relative '../../plugin_helper'
describe CustomWizard::TemplateValidator do
fab!(:user) { Fabricate(:user) }
-
- let(:template) {
- JSON.parse(File.open(
- "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
- ).read).with_indifferent_access
- }
+ let(:template) { get_wizard_fixture("wizard") }
+ let(:create_category) { get_wizard_fixture("actions/create_category") }
+ let(:user_condition) { get_wizard_fixture("condition/user_condition") }
it "validates valid templates" do
expect(
@@ -45,4 +42,52 @@ describe CustomWizard::TemplateValidator do
CustomWizard::TemplateValidator.new(template).perform
).to eq(false)
end
+
+ it "invalidates pro step attributes without a pro subscription" do
+ template[:steps][0][:condition] = user_condition['condition']
+ expect(
+ CustomWizard::TemplateValidator.new(template).perform
+ ).to eq(false)
+ end
+
+ it "invalidates pro field attributes without a pro subscription" do
+ template[:steps][0][:fields][0][:condition] = user_condition['condition']
+ expect(
+ CustomWizard::TemplateValidator.new(template).perform
+ ).to eq(false)
+ end
+
+ it "invalidates pro actions without a pro subscription" do
+ template[:actions] << create_category
+ expect(
+ CustomWizard::TemplateValidator.new(template).perform
+ ).to eq(false)
+ end
+
+ context "with pro subscription" do
+ before do
+ enable_pro
+ end
+
+ it "validates pro step attributes" do
+ template[:steps][0][:condition] = user_condition['condition']
+ expect(
+ CustomWizard::TemplateValidator.new(template).perform
+ ).to eq(true)
+ end
+
+ it "validates pro field attributes" do
+ template[:steps][0][:fields][0][:condition] = user_condition['condition']
+ expect(
+ CustomWizard::TemplateValidator.new(template).perform
+ ).to eq(true)
+ end
+
+ it "validates pro actions" do
+ template[:actions] << create_category
+ expect(
+ CustomWizard::TemplateValidator.new(template).perform
+ ).to eq(true)
+ end
+ end
end
diff --git a/spec/components/custom_wizard/update_validator_spec.rb b/spec/components/custom_wizard/update_validator_spec.rb
index e976e1ff..c79a5b0b 100644
--- a/spec/components/custom_wizard/update_validator_spec.rb
+++ b/spec/components/custom_wizard/update_validator_spec.rb
@@ -3,12 +3,7 @@ require_relative '../../plugin_helper'
describe CustomWizard::UpdateValidator do
fab!(:user) { Fabricate(:user) }
-
- let(:template) {
- JSON.parse(File.open(
- "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
- ).read).with_indifferent_access
- }
+ let(:template) { get_wizard_fixture("wizard") }
before do
CustomWizard::Template.save(template, skip_jobs: true)
diff --git a/spec/components/custom_wizard/wizard_spec.rb b/spec/components/custom_wizard/wizard_spec.rb
index 67905f5a..9cccff97 100644
--- a/spec/components/custom_wizard/wizard_spec.rb
+++ b/spec/components/custom_wizard/wizard_spec.rb
@@ -6,22 +6,8 @@ describe CustomWizard::Wizard do
fab!(:user) { Fabricate(:user) }
fab!(:trusted_user) { Fabricate(:user, trust_level: TrustLevel[3]) }
fab!(:admin_user) { Fabricate(:user, admin: true) }
-
- let(:template_json) {
- JSON.parse(
- File.open(
- "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
- ).read
- )
- }
-
- let(:permitted_json) {
- JSON.parse(
- File.open(
- "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard/permitted.json"
- ).read
- )
- }
+ let(:template_json) { get_wizard_fixture("wizard") }
+ let(:permitted_json) { get_wizard_fixture("wizard/permitted") }
before do
Group.refresh_automatic_group!(:trust_level_3)
diff --git a/spec/extensions/custom_field_extensions_spec.rb b/spec/extensions/custom_field_extensions_spec.rb
index f0ce32f5..bf1b3ff2 100644
--- a/spec/extensions/custom_field_extensions_spec.rb
+++ b/spec/extensions/custom_field_extensions_spec.rb
@@ -9,11 +9,8 @@ describe "custom field extensions" do
fab!(:group) { Fabricate(:group) }
fab!(:user) { Fabricate(:user) }
- let(:custom_field_json) {
- JSON.parse(File.open(
- "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/custom_field/custom_fields.json"
- ).read)
- }
+ let(:custom_field_json) { get_wizard_fixture("custom_field/custom_fields") }
+ let(:pro_custom_field_json) { get_wizard_fixture("custom_field/pro_custom_fields") }
before do
custom_field_json['custom_fields'].each do |field_json|
@@ -75,43 +72,54 @@ describe "custom field extensions" do
end
end
- context "category" do
- it "registers category custom fields" do
- category
- expect(Category.get_custom_field_type("category_field_1")).to eq(:json)
+ context "pro custom fields" do
+ before do
+ enable_pro
+
+ pro_custom_field_json['custom_fields'].each do |field_json|
+ custom_field = CustomWizard::CustomField.new(nil, field_json)
+ custom_field.save
+ end
end
- it "adds category custom fields to the basic category serializer" do
- category.custom_fields["category_field_1"] = { a: 1, b: 2 }.to_json
- category.save_custom_fields(true)
+ context "category" do
+ it "registers" do
+ category
+ expect(Category.get_custom_field_type("category_field_1")).to eq(:json)
+ end
- serializer = BasicCategorySerializer.new(
- category,
- scope: Guardian.new(user),
- root: false
- ).as_json
+ it "adds custom fields to the basic category serializer" do
+ category.custom_fields["category_field_1"] = { a: 1, b: 2 }.to_json
+ category.save_custom_fields(true)
- expect(serializer[:category_field_1]).to eq({ a: 1, b: 2 }.to_json)
- end
- end
+ serializer = BasicCategorySerializer.new(
+ category,
+ scope: Guardian.new(user),
+ root: false
+ ).as_json
- context "group" do
- it "registers group custom fields" do
- group
- expect(Group.get_custom_field_type("group_field_1")).to eq(:string)
+ expect(serializer[:category_field_1]).to eq({ a: 1, b: 2 }.to_json)
+ end
end
- it "adds group custom fields to the basic group serializer" do
- group.custom_fields["group_field_1"] = "Hello"
- group.save_custom_fields(true)
+ context "group" do
+ it "registers" do
+ group
+ expect(Group.get_custom_field_type("group_field_1")).to eq(:string)
+ end
- serializer = BasicGroupSerializer.new(
- group,
- scope: Guardian.new(user),
- root: false
- ).as_json
+ it "adds custom fields to the basic group serializer" do
+ group.custom_fields["group_field_1"] = "Hello"
+ group.save_custom_fields(true)
- expect(serializer[:group_field_1]).to eq("Hello")
+ serializer = BasicGroupSerializer.new(
+ group,
+ scope: Guardian.new(user),
+ root: false
+ ).as_json
+
+ expect(serializer[:group_field_1]).to eq("Hello")
+ end
end
end
end
diff --git a/spec/extensions/extra_locales_controller_spec.rb b/spec/extensions/extra_locales_controller_spec.rb
index a71e39c4..bb451b7c 100644
--- a/spec/extensions/extra_locales_controller_spec.rb
+++ b/spec/extensions/extra_locales_controller_spec.rb
@@ -4,18 +4,8 @@ require_relative '../plugin_helper'
describe ExtraLocalesControllerCustomWizard, type: :request do
let(:new_user) { Fabricate(:user, trust_level: TrustLevel[0]) }
let(:staff_user) { Fabricate(:moderator) }
-
- let(:template) {
- JSON.parse(File.open(
- "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
- ).read)
- }
-
- let(:permitted) {
- JSON.parse(File.open(
- "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard/permitted.json"
- ).read)
- }
+ let(:template) { get_wizard_fixture("wizard") }
+ let(:permitted) { get_wizard_fixture("wizard/permitted") }
before do
CustomWizard::Template.save(template, skip_jobs: true)
diff --git a/spec/extensions/invites_controller_spec.rb b/spec/extensions/invites_controller_spec.rb
index 47c4ca84..93fdd9d7 100644
--- a/spec/extensions/invites_controller_spec.rb
+++ b/spec/extensions/invites_controller_spec.rb
@@ -4,12 +4,7 @@ require_relative '../plugin_helper'
describe InvitesControllerCustomWizard, type: :request do
fab!(:topic) { Fabricate(:topic) }
let(:invite) { Invite.generate(topic.user, email: "angus@mcleod.org", topic: topic) }
-
- let(:template) do
- JSON.parse(File.open(
- "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
- ).read)
- end
+ let(:template) { get_wizard_fixture("wizard") }
before do
@controller = InvitesController.new
diff --git a/spec/extensions/users_controller_spec.rb b/spec/extensions/users_controller_spec.rb
index f4ba8e51..e4cd972f 100644
--- a/spec/extensions/users_controller_spec.rb
+++ b/spec/extensions/users_controller_spec.rb
@@ -2,11 +2,7 @@
require_relative '../plugin_helper'
describe CustomWizardUsersController, type: :request do
- let(:template) do
- JSON.parse(File.open(
- "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
- ).read)
- end
+ let(:template) { get_wizard_fixture("wizard") }
before do
@controller = UsersController.new
diff --git a/spec/fixtures/actions/add_to_group.json b/spec/fixtures/actions/add_to_group.json
new file mode 100644
index 00000000..2a5af1c3
--- /dev/null
+++ b/spec/fixtures/actions/add_to_group.json
@@ -0,0 +1,13 @@
+{
+ "id": "action_6",
+ "run_after": "step_1",
+ "type": "add_to_group",
+ "group": [
+ {
+ "type": "assignment",
+ "output": "action_9",
+ "output_type": "wizard_action",
+ "output_connector": "set"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/spec/fixtures/actions/create_category.json b/spec/fixtures/actions/create_category.json
new file mode 100644
index 00000000..c5f3d5af
--- /dev/null
+++ b/spec/fixtures/actions/create_category.json
@@ -0,0 +1,51 @@
+{
+ "id": "action_8",
+ "run_after": "step_1",
+ "type": "create_category",
+ "custom_fields": [
+ {
+ "type": "association",
+ "pairs": [
+ {
+ "index": 0,
+ "key": "category_custom_field",
+ "key_type": "text",
+ "value": "CC Val",
+ "value_type": "text",
+ "connector": "association"
+ }
+ ]
+ }
+ ],
+ "name": [
+ {
+ "type": "assignment",
+ "output": "step_1_field_1",
+ "output_type": "wizard_field",
+ "output_connector": "set"
+ }
+ ],
+ "slug": [
+ {
+ "type": "assignment",
+ "output": "step_1_field_1",
+ "output_type": "wizard_field",
+ "output_connector": "set"
+ }
+ ],
+ "permissions": [
+ {
+ "type": "association",
+ "pairs": [
+ {
+ "index": 0,
+ "key": "action_9",
+ "key_type": "wizard_action",
+ "value": "2",
+ "value_type": "text",
+ "connector": "association"
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/spec/fixtures/actions/create_group.json b/spec/fixtures/actions/create_group.json
new file mode 100644
index 00000000..e2e52ef2
--- /dev/null
+++ b/spec/fixtures/actions/create_group.json
@@ -0,0 +1,104 @@
+{
+ "id": "action_9",
+ "run_after": "step_1",
+ "type": "create_group",
+ "title": [
+ {
+ "type": "assignment",
+ "output": "New Group Member",
+ "output_type": "text",
+ "output_connector": "set"
+ }
+ ],
+ "custom_fields": [
+ {
+ "type": "association",
+ "pairs": [
+ {
+ "index": 0,
+ "key": "group_custom_field",
+ "key_type": "text",
+ "value": "step_3_field_1",
+ "value_type": "wizard_field",
+ "connector": "association"
+ }
+ ]
+ }
+ ],
+ "name": [
+ {
+ "type": "assignment",
+ "output": "step_1_field_1",
+ "output_type": "wizard_field",
+ "output_connector": "set"
+ }
+ ],
+ "full_name": [
+ {
+ "type": "assignment",
+ "output": "step_1_field_1",
+ "output_type": "wizard_field",
+ "output_connector": "set"
+ }
+ ],
+ "usernames": [
+ {
+ "type": "assignment",
+ "output_type": "user",
+ "output_connector": "set",
+ "output": [
+ "angus1"
+ ]
+ }
+ ],
+ "owner_usernames": [
+ {
+ "type": "assignment",
+ "output_type": "user",
+ "output_connector": "set",
+ "output": [
+ "angus"
+ ]
+ }
+ ],
+ "grant_trust_level": [
+ {
+ "type": "assignment",
+ "output": "3",
+ "output_type": "text",
+ "output_connector": "set"
+ }
+ ],
+ "mentionable_level": [
+ {
+ "type": "assignment",
+ "output": "1",
+ "output_type": "text",
+ "output_connector": "set"
+ }
+ ],
+ "messageable_level": [
+ {
+ "type": "assignment",
+ "output": "2",
+ "output_type": "text",
+ "output_connector": "set"
+ }
+ ],
+ "visibility_level": [
+ {
+ "type": "assignment",
+ "output": "3",
+ "output_type": "text",
+ "output_connector": "set"
+ }
+ ],
+ "members_visibility_level": [
+ {
+ "type": "assignment",
+ "output": "99",
+ "output_type": "text",
+ "output_connector": "set"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/spec/fixtures/actions/send_message.json b/spec/fixtures/actions/send_message.json
new file mode 100644
index 00000000..ddc9bf10
--- /dev/null
+++ b/spec/fixtures/actions/send_message.json
@@ -0,0 +1,25 @@
+{
+ "id": "action_2",
+ "run_after": "step_2",
+ "type": "send_message",
+ "post_builder": true,
+ "post_template": "I will interpolate some wizard fields w{step_1_field_1} w{step_1_field_2}",
+ "title": [
+ {
+ "type": "assignment",
+ "output": "Message title",
+ "output_type": "text",
+ "output_connector": "set"
+ }
+ ],
+ "recipient": [
+ {
+ "type": "assignment",
+ "output_type": "user",
+ "output_connector": "set",
+ "output": [
+ "angus1"
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/spec/fixtures/actions/send_message_multi.json b/spec/fixtures/actions/send_message_multi.json
new file mode 100644
index 00000000..8ab0fdb7
--- /dev/null
+++ b/spec/fixtures/actions/send_message_multi.json
@@ -0,0 +1,28 @@
+{
+ "id": "action_11",
+ "run_after": "step_2",
+ "type": "send_message",
+ "post_builder": true,
+ "post_template": "I will interpolate some wizard fields w{step_1_field_1} w{step_1_field_2}",
+ "title": [
+ {
+ "type": "assignment",
+ "output": "Multiple Recipients title",
+ "output_type": "text",
+ "output_connector": "set"
+ }
+ ],
+ "recipient": [
+ {
+ "type": "assignment",
+ "output_type": "user",
+ "output_connector": "set",
+ "output": [
+ "angus1",
+ "faiz",
+ "cool_group",
+ "cool_group_1"
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/spec/fixtures/custom_field/custom_fields.json b/spec/fixtures/custom_field/custom_fields.json
index 0e7741ce..c9b98476 100644
--- a/spec/fixtures/custom_field/custom_fields.json
+++ b/spec/fixtures/custom_field/custom_fields.json
@@ -16,22 +16,6 @@
"serializers": [
"post"
]
- },
- {
- "klass": "category",
- "name": "category_field_1",
- "type": "json",
- "serializers": [
- "basic_category"
- ]
- },
- {
- "klass": "group",
- "name": "group_field_1",
- "type": "string",
- "serializers": [
- "basic_group"
- ]
}
]
}
\ No newline at end of file
diff --git a/spec/fixtures/custom_field/pro_custom_fields.json b/spec/fixtures/custom_field/pro_custom_fields.json
new file mode 100644
index 00000000..b7060bdf
--- /dev/null
+++ b/spec/fixtures/custom_field/pro_custom_fields.json
@@ -0,0 +1,28 @@
+{
+ "custom_fields": [
+ {
+ "klass": "topic",
+ "name": "topic_field_2",
+ "type": "json",
+ "serializers": [
+ "topic_view"
+ ]
+ },
+ {
+ "klass": "category",
+ "name": "category_field_1",
+ "type": "json",
+ "serializers": [
+ "basic_category"
+ ]
+ },
+ {
+ "klass": "group",
+ "name": "group_field_1",
+ "type": "string",
+ "serializers": [
+ "basic_group"
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/spec/fixtures/wizard.json b/spec/fixtures/wizard.json
index a505c0d3..1560acef 100644
--- a/spec/fixtures/wizard.json
+++ b/spec/fixtures/wizard.json
@@ -163,197 +163,6 @@
}
],
"actions": [
- {
- "id": "action_9",
- "run_after": "step_1",
- "type": "create_group",
- "title": [
- {
- "type": "assignment",
- "output": "New Group Member",
- "output_type": "text",
- "output_connector": "set"
- }
- ],
- "custom_fields": [
- {
- "type": "association",
- "pairs": [
- {
- "index": 0,
- "key": "group_custom_field",
- "key_type": "text",
- "value": "step_3_field_1",
- "value_type": "wizard_field",
- "connector": "association"
- }
- ]
- }
- ],
- "name": [
- {
- "type": "assignment",
- "output": "step_1_field_1",
- "output_type": "wizard_field",
- "output_connector": "set"
- }
- ],
- "full_name": [
- {
- "type": "assignment",
- "output": "step_1_field_1",
- "output_type": "wizard_field",
- "output_connector": "set"
- }
- ],
- "usernames": [
- {
- "type": "assignment",
- "output_type": "user",
- "output_connector": "set",
- "output": [
- "angus1"
- ]
- }
- ],
- "owner_usernames": [
- {
- "type": "assignment",
- "output_type": "user",
- "output_connector": "set",
- "output": [
- "angus"
- ]
- }
- ],
- "grant_trust_level": [
- {
- "type": "assignment",
- "output": "3",
- "output_type": "text",
- "output_connector": "set"
- }
- ],
- "mentionable_level": [
- {
- "type": "assignment",
- "output": "1",
- "output_type": "text",
- "output_connector": "set"
- }
- ],
- "messageable_level": [
- {
- "type": "assignment",
- "output": "2",
- "output_type": "text",
- "output_connector": "set"
- }
- ],
- "visibility_level": [
- {
- "type": "assignment",
- "output": "3",
- "output_type": "text",
- "output_connector": "set"
- }
- ],
- "members_visibility_level": [
- {
- "type": "assignment",
- "output": "99",
- "output_type": "text",
- "output_connector": "set"
- }
- ]
- },
- {
- "id": "action_6",
- "run_after": "step_1",
- "type": "add_to_group",
- "group": [
- {
- "type": "assignment",
- "output": "action_9",
- "output_type": "wizard_action",
- "output_connector": "set"
- }
- ]
- },
- {
- "id": "action_8",
- "run_after": "step_1",
- "type": "create_category",
- "custom_fields": [
- {
- "type": "association",
- "pairs": [
- {
- "index": 0,
- "key": "category_custom_field",
- "key_type": "text",
- "value": "CC Val",
- "value_type": "text",
- "connector": "association"
- }
- ]
- }
- ],
- "name": [
- {
- "type": "assignment",
- "output": "step_1_field_1",
- "output_type": "wizard_field",
- "output_connector": "set"
- }
- ],
- "slug": [
- {
- "type": "assignment",
- "output": "step_1_field_1",
- "output_type": "wizard_field",
- "output_connector": "set"
- }
- ],
- "permissions": [
- {
- "type": "association",
- "pairs": [
- {
- "index": 0,
- "key": "action_9",
- "key_type": "wizard_action",
- "value": "2",
- "value_type": "text",
- "connector": "association"
- }
- ]
- }
- ]
- },
- {
- "id": "action_5",
- "run_after": "step_1",
- "type": "watch_categories",
- "notification_level": "tracking",
- "wizard_user": true,
- "categories": [
- {
- "type": "assignment",
- "output": "action_8",
- "output_type": "wizard_action",
- "output_connector": "set"
- }
- ],
- "mute_remainder": [
- {
- "type": "assignment",
- "output": "true",
- "output_type": "text",
- "output_connector": "set"
- }
- ]
- },
{
"id": "action_1",
"run_after": "step_3",
@@ -442,6 +251,29 @@
}
]
},
+ {
+ "id": "action_5",
+ "run_after": "step_1",
+ "type": "watch_categories",
+ "notification_level": "tracking",
+ "wizard_user": true,
+ "categories": [
+ {
+ "type": "assignment",
+ "output": "action_8",
+ "output_type": "wizard_action",
+ "output_connector": "set"
+ }
+ ],
+ "mute_remainder": [
+ {
+ "type": "assignment",
+ "output": "true",
+ "output_type": "text",
+ "output_connector": "set"
+ }
+ ]
+ },
{
"id": "action_4",
"run_after": "step_2",
@@ -462,59 +294,6 @@
}
]
},
- {
- "id": "action_2",
- "run_after": "step_2",
- "type": "send_message",
- "post_builder": true,
- "post_template": "I will interpolate some wizard fields w{step_1_field_1} w{step_1_field_2}",
- "title": [
- {
- "type": "assignment",
- "output": "Message title",
- "output_type": "text",
- "output_connector": "set"
- }
- ],
- "recipient": [
- {
- "type": "assignment",
- "output_type": "user",
- "output_connector": "set",
- "output": [
- "angus1"
- ]
- }
- ]
- },
- {
- "id": "action_11",
- "run_after": "step_2",
- "type": "send_message",
- "post_builder": true,
- "post_template": "I will interpolate some wizard fields w{step_1_field_1} w{step_1_field_2}",
- "title": [
- {
- "type": "assignment",
- "output": "Multiple Recipients title",
- "output_type": "text",
- "output_connector": "set"
- }
- ],
- "recipient": [
- {
- "type": "assignment",
- "output_type": "user",
- "output_connector": "set",
- "output": [
- "angus1",
- "faiz",
- "cool_group",
- "cool_group_1"
- ]
- }
- ]
- },
{
"id": "action_3",
"run_after": "step_2",
@@ -529,24 +308,6 @@
"output_connector": "set"
}
],
- "category": [
- {
- "type": "assignment",
- "output": "action_8",
- "output_type": "wizard_action",
- "output_connector": "set",
- "pairs": [
- {
- "index": 0,
- "key": "step_2_field_5",
- "key_type": "wizard_field",
- "value": "true",
- "value_type": "text",
- "connector": "is"
- }
- ]
- }
- ],
"tags": [
{
"type": "assignment",
diff --git a/spec/jobs/set_after_time_wizard_spec.rb b/spec/jobs/set_after_time_wizard_spec.rb
index 35576f01..93e46c8b 100644
--- a/spec/jobs/set_after_time_wizard_spec.rb
+++ b/spec/jobs/set_after_time_wizard_spec.rb
@@ -7,11 +7,7 @@ describe Jobs::SetAfterTimeWizard do
fab!(:user2) { Fabricate(:user) }
fab!(:user3) { Fabricate(:user) }
- let(:template) {
- JSON.parse(File.open(
- "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
- ).read).with_indifferent_access
- }
+ let(:template) { get_wizard_fixture("wizard") }
it "sets wizard redirect for all users " do
after_time_template = template.dup
diff --git a/spec/jobs/update_pro_subscription_spec.rb b/spec/jobs/update_pro_subscription_spec.rb
new file mode 100644
index 00000000..0aae9668
--- /dev/null
+++ b/spec/jobs/update_pro_subscription_spec.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+require_relative '../plugin_helper'
+
+describe CustomWizard::UpdateProSubscription do
+ it "updates the pro subscription" do
+ stub_subscription_request(200, valid_subscription)
+ described_class.new.execute
+ expect(CustomWizard::Pro.subscribed?).to eq(true)
+ end
+end
diff --git a/spec/plugin_helper.rb b/spec/plugin_helper.rb
index 9e4bbbbe..4aa34029 100644
--- a/spec/plugin_helper.rb
+++ b/spec/plugin_helper.rb
@@ -15,3 +15,44 @@ require 'oj'
Oj.default_options = Oj.default_options.merge(cache_str: -1)
require 'rails_helper'
+
+def get_wizard_fixture(path)
+ JSON.parse(
+ File.open(
+ "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/#{path}.json"
+ ).read
+ ).with_indifferent_access
+end
+
+def authenticate_pro
+ CustomWizard::ProAuthentication.any_instance.stubs(:active?).returns(true)
+end
+
+def enable_pro
+ CustomWizard::Pro.any_instance.stubs(:subscribed?).returns(true)
+end
+
+def disable_pro
+ CustomWizard::Pro.any_instance.stubs(:subscribed?).returns(false)
+end
+
+def valid_subscription
+ {
+ product_id: "prod_CBTNpi3fqWWkq0",
+ price_id: "price_id",
+ price_nickname: "business"
+ }
+end
+
+def invalid_subscription
+ {
+ product_id: "prod_CBTNpi3fqWWkq0",
+ price_id: "price_id"
+ }
+end
+
+def stub_subscription_request(status, subscription)
+ authenticate_pro
+ pro = CustomWizard::Pro.new
+ stub_request(:get, "https://#{pro.server}/subscription-server/user-subscriptions/#{pro.subscription_type}/#{pro.client_name}").to_return(status: status, body: { subscriptions: [subscription] }.to_json)
+end
diff --git a/spec/requests/custom_wizard/admin/custom_fields_controller_spec.rb b/spec/requests/custom_wizard/admin/custom_fields_controller_spec.rb
index 8c1a8550..d2e086d2 100644
--- a/spec/requests/custom_wizard/admin/custom_fields_controller_spec.rb
+++ b/spec/requests/custom_wizard/admin/custom_fields_controller_spec.rb
@@ -3,12 +3,7 @@ require_relative '../../../plugin_helper'
describe CustomWizard::AdminCustomFieldsController do
fab!(:admin_user) { Fabricate(:user, admin: true) }
-
- let(:custom_field_json) {
- JSON.parse(File.open(
- "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/custom_field/custom_fields.json"
- ).read)
- }
+ let(:custom_field_json) { get_wizard_fixture("custom_field/custom_fields") }
before do
custom_field_json['custom_fields'].each do |field_json|
@@ -19,7 +14,7 @@ describe CustomWizard::AdminCustomFieldsController do
it "returns the full list of custom fields" do
get "/admin/wizards/custom-fields.json"
- expect(response.parsed_body.length).to eq(15)
+ expect(response.parsed_body["custom_fields"].length).to be > 12
end
it "saves custom fields" do
diff --git a/spec/requests/custom_wizard/admin/manager_controller_spec.rb b/spec/requests/custom_wizard/admin/manager_controller_spec.rb
index 87c980f2..6218ea7e 100644
--- a/spec/requests/custom_wizard/admin/manager_controller_spec.rb
+++ b/spec/requests/custom_wizard/admin/manager_controller_spec.rb
@@ -3,12 +3,7 @@ require_relative '../../../plugin_helper'
describe CustomWizard::AdminManagerController do
fab!(:admin_user) { Fabricate(:user, admin: true) }
-
- let(:template) {
- JSON.parse(File.open(
- "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
- ).read)
- }
+ let(:template) { get_wizard_fixture("wizard") }
before do
sign_in(admin_user)
diff --git a/spec/requests/custom_wizard/admin/pro_controller_spec.rb b/spec/requests/custom_wizard/admin/pro_controller_spec.rb
new file mode 100644
index 00000000..563572bd
--- /dev/null
+++ b/spec/requests/custom_wizard/admin/pro_controller_spec.rb
@@ -0,0 +1,71 @@
+# frozen_string_literal: true
+require_relative '../../../plugin_helper'
+
+describe CustomWizard::AdminProController do
+ fab!(:admin_user) { Fabricate(:user, admin: true) }
+
+ def generate_payload(request_id, user_id)
+ uri = URI(@pro.authentication_url(user_id, request_id))
+ keys = @pro.authentication.get_keys(request_id)
+ raw_payload = {
+ key: "12345",
+ nonce: keys.nonce,
+ push: false,
+ api: UserApiKeysController::AUTH_API_VERSION
+ }.to_json
+ public_key = OpenSSL::PKey::RSA.new(keys.pem)
+ Base64.encode64(public_key.public_encrypt(raw_payload))
+ end
+
+ before do
+ @pro = CustomWizard::Pro.new
+ sign_in(admin_user)
+ end
+
+ it "#index" do
+ get "/admin/wizards/pro.json"
+ expect(response.parsed_body['server']).to eq(@pro.server)
+ expect(response.parsed_body['authentication'].deep_symbolize_keys).to eq(CustomWizard::ProAuthenticationSerializer.new(@pro.authentication, root: false).as_json)
+ expect(response.parsed_body['subscription'].deep_symbolize_keys).to eq(CustomWizard::ProSubscriptionSerializer.new(@pro.subscription, root: false).as_json)
+ end
+
+ it "#authorize" do
+ get "/admin/wizards/pro/authorize"
+ expect(response.status).to eq(302)
+ expect(cookies[:user_api_request_id].present?).to eq(true)
+ end
+
+ it "#destroy_authentication" do
+ request_id = SecureRandom.hex(32)
+ payload = generate_payload(request_id, admin_user.id)
+ @pro.authentication_response(request_id, payload)
+
+ delete "/admin/wizards/pro/authorize.json"
+
+ expect(response.status).to eq(200)
+ expect(CustomWizard::Pro.authorized?).to eq(false)
+ end
+
+ context "subscription" do
+ before do
+ stub_subscription_request(200, valid_subscription)
+ end
+
+ it "handles authentication response and the updates subscription" do
+ request_id = cookies[:user_api_request_id] = SecureRandom.hex(32)
+ payload = generate_payload(request_id, admin_user.id)
+ get "/admin/wizards/pro/authorize/callback", params: { payload: payload }
+
+ expect(response).to redirect_to("/admin/wizards/pro")
+ expect(CustomWizard::Pro.subscribed?).to eq(true)
+ end
+
+ it "updates the subscription" do
+ authenticate_pro
+ post "/admin/wizards/pro/subscription.json"
+
+ expect(response.status).to eq(200)
+ expect(CustomWizard::Pro.subscribed?).to eq(true)
+ end
+ end
+end
diff --git a/spec/requests/custom_wizard/admin/submissions_controller_spec.rb b/spec/requests/custom_wizard/admin/submissions_controller_spec.rb
index 36296e95..5a79679f 100644
--- a/spec/requests/custom_wizard/admin/submissions_controller_spec.rb
+++ b/spec/requests/custom_wizard/admin/submissions_controller_spec.rb
@@ -7,12 +7,7 @@ describe CustomWizard::AdminSubmissionsController do
fab!(:user2) { Fabricate(:user) }
fab!(:user3) { Fabricate(:user) }
- let(:template) {
- JSON.parse(File.open(
- "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
- ).read)
- }
-
+ let(:template) { get_wizard_fixture("wizard") }
let(:template_2) {
temp = template.dup
temp["id"] = "super_mega_fun_wizard_2"
diff --git a/spec/requests/custom_wizard/admin/wizard_controller_spec.rb b/spec/requests/custom_wizard/admin/wizard_controller_spec.rb
index 82aa4fc5..9d7ed18d 100644
--- a/spec/requests/custom_wizard/admin/wizard_controller_spec.rb
+++ b/spec/requests/custom_wizard/admin/wizard_controller_spec.rb
@@ -5,12 +5,7 @@ describe CustomWizard::AdminWizardController do
fab!(:admin_user) { Fabricate(:user, admin: true) }
fab!(:user1) { Fabricate(:user) }
fab!(:user2) { Fabricate(:user) }
-
- let(:template) {
- JSON.parse(File.open(
- "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
- ).read)
- }
+ let(:template) { get_wizard_fixture("wizard") }
before do
CustomWizard::Template.save(template, skip_jobs: true)
diff --git a/spec/requests/custom_wizard/application_controller_spec.rb b/spec/requests/custom_wizard/application_controller_spec.rb
index 0835f246..f92ac7c0 100644
--- a/spec/requests/custom_wizard/application_controller_spec.rb
+++ b/spec/requests/custom_wizard/application_controller_spec.rb
@@ -2,21 +2,11 @@
require_relative '../../plugin_helper'
describe ApplicationController do
- fab!(:user) {
- Fabricate(
- :user,
- username: 'angus',
- email: "angus@email.com",
- trust_level: TrustLevel[3]
- )
- }
+ fab!(:user) { Fabricate(:user, username: 'angus', email: "angus@email.com", trust_level: TrustLevel[3]) }
+ let(:wizard_template) { get_wizard_fixture("wizard") }
before do
- CustomWizard::Template.save(
- JSON.parse(File.open(
- "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
- ).read),
- skip_jobs: true)
+ CustomWizard::Template.save(wizard_template, skip_jobs: true)
@template = CustomWizard::Template.find('super_mega_fun_wizard')
end
diff --git a/spec/requests/custom_wizard/custom_field_extensions_spec.rb b/spec/requests/custom_wizard/custom_field_extensions_spec.rb
index b991769a..64d7c755 100644
--- a/spec/requests/custom_wizard/custom_field_extensions_spec.rb
+++ b/spec/requests/custom_wizard/custom_field_extensions_spec.rb
@@ -8,12 +8,8 @@ describe "custom field extensions" do
let!(:category) { Fabricate(:category) }
let!(:user) { Fabricate(:user) }
let!(:group) { Fabricate(:group, users: [user]) }
-
- let(:custom_field_json) {
- JSON.parse(File.open(
- "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/custom_field/custom_fields.json"
- ).read)
- }
+ let(:custom_field_json) { get_wizard_fixture("custom_field/custom_fields") }
+ let(:pro_custom_field_json) { get_wizard_fixture("custom_field/pro_custom_fields") }
before do
custom_field_json['custom_fields'].each do |field_json|
@@ -32,27 +28,6 @@ describe "custom field extensions" do
expect(response.parsed_body["topic_field_1"]).to eq(true)
end
- it "adds category custom fields to the show categories response" do
- category.custom_fields["category_field_1"] = { a: 1, b: 2 }
- category.save_custom_fields(true)
-
- get "/c/#{category.id}/show.json"
-
- expect(response.status).to eq(200)
- expect(response.parsed_body["category"]["category_field_1"]).to eq({ a: 1, b: 2 }.as_json)
- end
-
- it "adds group custom fields to the show group response" do
- group.custom_fields["group_field_1"] = "Group cf entry"
- group.save_custom_fields(true)
-
- sign_in(user)
- get "/groups/#{group.name}.json"
-
- expect(response.status).to eq(200)
- expect(response.parsed_body['group']['group_field_1']).to eq("Group cf entry")
- end
-
it "adds post custom fields to the show post response" do
post.custom_fields["post_field_1"] = 7
post.save_custom_fields(true)
@@ -63,32 +38,64 @@ describe "custom field extensions" do
expect(response.parsed_body['post_field_1']).to eq(7)
end
- context "preloaded" do
- it "preloads category custom fields on site categories" do
- Site.preloaded_category_custom_fields << "other_field"
+ context "with a pro subscription" do
+ before do
+ enable_pro
+ pro_custom_field_json['custom_fields'].each do |field_json|
+ custom_field = CustomWizard::CustomField.new(nil, field_json)
+ custom_field.save
+ end
+ end
+
+ it "adds category custom fields to the show categories response" do
category.custom_fields["category_field_1"] = { a: 1, b: 2 }
category.save_custom_fields(true)
- get "/site.json"
- expect(response.status).to eq(200)
+ get "/c/#{category.id}/show.json"
- site_category = response.parsed_body['categories'].select { |c| c['id'] == category.id }.first
- expect(site_category["category_field_1"]).to eq({ a: 1, b: 2 }.as_json)
+ expect(response.status).to eq(200)
+ expect(response.parsed_body["category"]["category_field_1"]).to eq({ a: 1, b: 2 }.as_json)
end
- it "preloads group custom fields on group index" do
- Group.preloaded_custom_field_names << "other_field"
-
- group = Fabricate(:group)
+ it "adds group custom fields to the show group response" do
group.custom_fields["group_field_1"] = "Group cf entry"
group.save_custom_fields(true)
- get "/groups.json"
- expect(response.status).to eq(200)
+ sign_in(user)
+ get "/groups/#{group.name}.json"
- group = response.parsed_body['groups'].select { |g| g['id'] == group.id }.first
- expect(group['group_field_1']).to eq("Group cf entry")
+ expect(response.status).to eq(200)
+ expect(response.parsed_body['group']['group_field_1']).to eq("Group cf entry")
+ end
+
+ context "preloaded" do
+ it "preloads category custom fields on site categories" do
+ Site.preloaded_category_custom_fields << "other_field"
+
+ category.custom_fields["category_field_1"] = { a: 1, b: 2 }
+ category.save_custom_fields(true)
+
+ get "/site.json"
+ expect(response.status).to eq(200)
+
+ site_category = response.parsed_body['categories'].select { |c| c['id'] == category.id }.first
+ expect(site_category["category_field_1"]).to eq({ a: 1, b: 2 }.as_json)
+ end
+
+ it "preloads group custom fields on group index" do
+ Group.preloaded_custom_field_names << "other_field"
+
+ group = Fabricate(:group)
+ group.custom_fields["group_field_1"] = "Group cf entry"
+ group.save_custom_fields(true)
+
+ get "/groups.json"
+ expect(response.status).to eq(200)
+
+ group = response.parsed_body['groups'].select { |g| g['id'] == group.id }.first
+ expect(group['group_field_1']).to eq("Group cf entry")
+ end
end
end
end
diff --git a/spec/requests/custom_wizard/steps_controller_spec.rb b/spec/requests/custom_wizard/steps_controller_spec.rb
index 5da75d8d..85353e4c 100644
--- a/spec/requests/custom_wizard/steps_controller_spec.rb
+++ b/spec/requests/custom_wizard/steps_controller_spec.rb
@@ -2,55 +2,12 @@
require_relative '../../plugin_helper'
describe CustomWizard::StepsController do
- fab!(:user) {
- Fabricate(
- :user,
- username: 'angus',
- email: "angus@email.com",
- trust_level: TrustLevel[3]
- )
- }
-
- fab!(:user2) {
- Fabricate(
- :user,
- username: 'bob',
- email: "bob@email.com",
- trust_level: TrustLevel[2]
- )
- }
-
- let(:wizard_template) {
- JSON.parse(
- File.open(
- "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
- ).read
- )
- }
-
- let(:wizard_field_condition_template) {
- JSON.parse(
- File.open(
- "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/condition/wizard_field_condition.json"
- ).read
- )
- }
-
- let(:user_condition_template) {
- JSON.parse(
- File.open(
- "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/condition/user_condition.json"
- ).read
- )
- }
-
- let(:permitted_json) {
- JSON.parse(
- File.open(
- "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard/permitted.json"
- ).read
- )
- }
+ fab!(:user) { Fabricate(:user, username: 'angus', email: "angus@email.com", trust_level: TrustLevel[3]) }
+ fab!(:user2) { Fabricate(:user, username: 'bob', email: "bob@email.com", trust_level: TrustLevel[2]) }
+ let(:wizard_template) { get_wizard_fixture("wizard") }
+ let(:wizard_field_condition_template) { get_wizard_fixture("condition/wizard_field_condition") }
+ let(:user_condition_template) { get_wizard_fixture("condition/user_condition") }
+ let(:permitted_json) { get_wizard_fixture("wizard/permitted") }
before do
CustomWizard::Template.save(wizard_template, skip_jobs: true)
@@ -90,17 +47,6 @@ describe CustomWizard::StepsController do
put '/w/super-mega-fun-wizard/steps/step_10.json'
expect(response.status).to eq(400)
end
-
- it "when user cant see the step due to conditions" do
- sign_in(user2)
-
- new_wizard_template = wizard_template.dup
- new_wizard_template['steps'][0]['condition'] = user_condition_template['condition']
- CustomWizard::Template.save(new_wizard_template, skip_jobs: true)
-
- put '/w/super-mega-fun-wizard/steps/step_1.json'
- expect(response.status).to eq(403)
- end
end
it "works if the step has no fields" do
@@ -123,64 +69,40 @@ describe CustomWizard::StepsController do
expect(response.parsed_body['wizard']['start']).to eq("step_2")
end
- it "returns an updated wizard when condition doesnt pass" do
- new_template = wizard_template.dup
- new_template['steps'][1]['condition'] = wizard_field_condition_template['condition']
- CustomWizard::Template.save(new_template, skip_jobs: true)
-
- put '/w/super-mega-fun-wizard/steps/step_1.json', params: {
- fields: {
- step_1_field_1: "Condition wont pass"
- }
- }
- expect(response.status).to eq(200)
- expect(response.parsed_body['wizard']['start']).to eq("step_3")
- end
-
it "runs completion actions if user has completed wizard" do
new_template = wizard_template.dup
## route_to action
new_template['actions'].last['run_after'] = 'wizard_completion'
- new_template['steps'][1]['condition'] = wizard_field_condition_template['condition']
- new_template['steps'][2]['condition'] = wizard_field_condition_template['condition']
CustomWizard::Template.save(new_template, skip_jobs: true)
- put '/w/super-mega-fun-wizard/steps/step_1.json', params: {
- fields: {
- step_1_field_1: "Condition wont pass"
- }
- }
+ put '/w/super-mega-fun-wizard/steps/step_1.json'
+ put '/w/super-mega-fun-wizard/steps/step_2.json'
+ put '/w/super-mega-fun-wizard/steps/step_3.json'
expect(response.status).to eq(200)
expect(response.parsed_body['redirect_on_complete']).to eq("https://google.com")
end
it "saves results of completion actions if user has completed wizard" do
new_template = wizard_template.dup
-
- ## Create group action
new_template['actions'].first['run_after'] = 'wizard_completion'
- new_template['steps'][1]['condition'] = wizard_field_condition_template['condition']
CustomWizard::Template.save(new_template, skip_jobs: true)
put '/w/super-mega-fun-wizard/steps/step_1.json', params: {
fields: {
- step_1_field_1: "My cool group"
+ step_1_field_1: "Topic title",
+ step_1_field_2: "Topic post"
}
}
- expect(response.status).to eq(200)
-
+ put '/w/super-mega-fun-wizard/steps/step_2.json'
put '/w/super-mega-fun-wizard/steps/step_3.json'
- expect(response.status).to eq(200)
wizard_id = response.parsed_body['wizard']['id']
wizard = CustomWizard::Wizard.create(wizard_id, user)
- group_name = wizard.submissions.first.fields['action_9']
- group = Group.find_by(name: group_name)
-
- expect(group.present?).to eq(true)
- expect(group.full_name).to eq("My cool group")
+ topic_id = wizard.submissions.first.fields[new_template['actions'].first['id']]
+ topic = Topic.find(topic_id)
+ expect(topic.present?).to eq(true)
end
it "returns a final step without conditions" do
@@ -197,88 +119,119 @@ describe CustomWizard::StepsController do
expect(response.parsed_body['final']).to eq(true)
end
- it "returns the correct final step when the conditional final step and last step are the same" do
- new_template = wizard_template.dup
- new_template['steps'][0]['condition'] = user_condition_template['condition']
- new_template['steps'][2]['condition'] = wizard_field_condition_template['condition']
- CustomWizard::Template.save(new_template, skip_jobs: true)
+ context "pro" do
+ before do
+ enable_pro
+ end
- put '/w/super-mega-fun-wizard/steps/step_1.json', params: {
- fields: {
- step_1_field_1: "Condition will pass"
+ it "raises an error when user cant see the step due to conditions" do
+ sign_in(user2)
+
+ new_wizard_template = wizard_template.dup
+ new_wizard_template['steps'][0]['condition'] = user_condition_template['condition']
+ CustomWizard::Template.save(new_wizard_template, skip_jobs: true)
+
+ put '/w/super-mega-fun-wizard/steps/step_1.json'
+ expect(response.status).to eq(403)
+ end
+
+ it "returns an updated wizard when condition doesnt pass" do
+ new_template = wizard_template.dup
+ new_template['steps'][1]['condition'] = wizard_field_condition_template['condition']
+ CustomWizard::Template.save(new_template, skip_jobs: true)
+
+ put '/w/super-mega-fun-wizard/steps/step_1.json', params: {
+ fields: {
+ step_1_field_1: "Condition wont pass"
+ }
}
- }
- expect(response.status).to eq(200)
- expect(response.parsed_body['final']).to eq(false)
+ expect(response.status).to eq(200)
+ expect(response.parsed_body['wizard']['start']).to eq("step_3")
+ end
- put '/w/super-mega-fun-wizard/steps/step_2.json'
- expect(response.status).to eq(200)
- expect(response.parsed_body['final']).to eq(false)
+ it "returns the correct final step when the conditional final step and last step are the same" do
+ new_template = wizard_template.dup
+ new_template['steps'][0]['condition'] = user_condition_template['condition']
+ new_template['steps'][2]['condition'] = wizard_field_condition_template['condition']
+ CustomWizard::Template.save(new_template, skip_jobs: true)
- put '/w/super-mega-fun-wizard/steps/step_3.json'
- expect(response.status).to eq(200)
- expect(response.parsed_body['final']).to eq(true)
- end
-
- it "returns the correct final step when the conditional final step and last step are different" do
- new_template = wizard_template.dup
- new_template['steps'][2]['condition'] = wizard_field_condition_template['condition']
- CustomWizard::Template.save(new_template, skip_jobs: true)
-
- put '/w/super-mega-fun-wizard/steps/step_1.json', params: {
- fields: {
- step_1_field_1: "Condition will not pass"
+ put '/w/super-mega-fun-wizard/steps/step_1.json', params: {
+ fields: {
+ step_1_field_1: "Condition will pass"
+ }
}
- }
- expect(response.status).to eq(200)
- expect(response.parsed_body['final']).to eq(false)
+ expect(response.status).to eq(200)
+ expect(response.parsed_body['final']).to eq(false)
- put '/w/super-mega-fun-wizard/steps/step_2.json'
- expect(response.status).to eq(200)
- expect(response.parsed_body['final']).to eq(true)
- end
+ put '/w/super-mega-fun-wizard/steps/step_2.json'
+ expect(response.status).to eq(200)
+ expect(response.parsed_body['final']).to eq(false)
- it "returns the correct final step when the conditional final step is determined in the same action" do
- new_template = wizard_template.dup
- new_template['steps'][1]['condition'] = wizard_field_condition_template['condition']
- new_template['steps'][2]['condition'] = wizard_field_condition_template['condition']
- CustomWizard::Template.save(new_template, skip_jobs: true)
+ put '/w/super-mega-fun-wizard/steps/step_3.json'
+ expect(response.status).to eq(200)
+ expect(response.parsed_body['final']).to eq(true)
+ end
- put '/w/super-mega-fun-wizard/steps/step_1.json', params: {
- fields: {
- step_1_field_1: "Condition will not pass"
+ it "returns the correct final step when the conditional final step and last step are different" do
+ new_template = wizard_template.dup
+ new_template['steps'][2]['condition'] = wizard_field_condition_template['condition']
+ CustomWizard::Template.save(new_template, skip_jobs: true)
+
+ put '/w/super-mega-fun-wizard/steps/step_1.json', params: {
+ fields: {
+ step_1_field_1: "Condition will not pass"
+ }
}
- }
- expect(response.status).to eq(200)
- expect(response.parsed_body['final']).to eq(true)
- end
+ expect(response.status).to eq(200)
+ expect(response.parsed_body['final']).to eq(false)
- it "excludes the non-included conditional fields from the submissions" do
- new_template = wizard_template.dup
- new_template['steps'][1]['fields'][0]['condition'] = wizard_field_condition_template['condition']
- CustomWizard::Template.save(new_template, skip_jobs: true)
+ put '/w/super-mega-fun-wizard/steps/step_2.json'
+ expect(response.status).to eq(200)
+ expect(response.parsed_body['final']).to eq(true)
+ end
- put '/w/super-mega-fun-wizard/steps/step_1.json', params: {
- fields: {
- step_1_field_1: "Condition will pass"
+ it "returns the correct final step when the conditional final step is determined in the same action" do
+ new_template = wizard_template.dup
+ new_template['steps'][1]['condition'] = wizard_field_condition_template['condition']
+ new_template['steps'][2]['condition'] = wizard_field_condition_template['condition']
+ CustomWizard::Template.save(new_template, skip_jobs: true)
+
+ put '/w/super-mega-fun-wizard/steps/step_1.json', params: {
+ fields: {
+ step_1_field_1: "Condition will not pass"
+ }
}
- }
+ expect(response.status).to eq(200)
+ expect(response.parsed_body['final']).to eq(true)
+ end
- put '/w/super-mega-fun-wizard/steps/step_2.json', params: {
- fields: {
- step_2_field_1: "1995-04-23"
+ it "excludes the non-included conditional fields from the submissions" do
+ new_template = wizard_template.dup
+ new_template['steps'][1]['fields'][0]['condition'] = wizard_field_condition_template['condition']
+ CustomWizard::Template.save(new_template, skip_jobs: true)
+
+ put '/w/super-mega-fun-wizard/steps/step_1.json', params: {
+ fields: {
+ step_1_field_1: "Condition will pass"
+ }
}
- }
- put '/w/super-mega-fun-wizard/steps/step_1.json', params: {
- fields: {
- step_1_field_1: "Condition will not pass"
+ put '/w/super-mega-fun-wizard/steps/step_2.json', params: {
+ fields: {
+ step_2_field_1: "1995-04-23"
+ }
}
- }
- wizard_id = response.parsed_body['wizard']['id']
- wizard = CustomWizard::Wizard.create(wizard_id, user)
- submission = wizard.current_submission
- expect(submission.fields.keys).not_to include("step_2_field_1")
+ put '/w/super-mega-fun-wizard/steps/step_1.json', params: {
+ fields: {
+ step_1_field_1: "Condition will not pass"
+ }
+ }
+
+ wizard_id = response.parsed_body['wizard']['id']
+ wizard = CustomWizard::Wizard.create(wizard_id, user)
+ submission = wizard.current_submission
+ expect(submission.fields.keys).not_to include("step_2_field_1")
+ end
end
end
diff --git a/spec/requests/custom_wizard/wizard_controller_spec.rb b/spec/requests/custom_wizard/wizard_controller_spec.rb
index 26f90040..f6211b55 100644
--- a/spec/requests/custom_wizard/wizard_controller_spec.rb
+++ b/spec/requests/custom_wizard/wizard_controller_spec.rb
@@ -2,29 +2,12 @@
require_relative '../../plugin_helper'
describe CustomWizard::WizardController do
- fab!(:user) {
- Fabricate(
- :user,
- username: 'angus',
- email: "angus@email.com",
- trust_level: TrustLevel[3]
- )
- }
-
- let(:permitted_json) {
- JSON.parse(
- File.open(
- "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard/permitted.json"
- ).read
- )
- }
+ fab!(:user) { Fabricate(:user, username: 'angus', email: "angus@email.com", trust_level: TrustLevel[3]) }
+ let(:wizard_template) { get_wizard_fixture("wizard") }
+ let(:permitted_json) { get_wizard_fixture("wizard/permitted") }
before do
- CustomWizard::Template.save(
- JSON.parse(File.open(
- "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
- ).read),
- skip_jobs: true)
+ CustomWizard::Template.save(wizard_template, skip_jobs: true)
@template = CustomWizard::Template.find("super_mega_fun_wizard")
sign_in(user)
end
diff --git a/spec/serializers/custom_wizard/basic_wizard_serializer_spec.rb b/spec/serializers/custom_wizard/basic_wizard_serializer_spec.rb
index bf575827..6694e979 100644
--- a/spec/serializers/custom_wizard/basic_wizard_serializer_spec.rb
+++ b/spec/serializers/custom_wizard/basic_wizard_serializer_spec.rb
@@ -4,13 +4,10 @@ require_relative '../../plugin_helper'
describe CustomWizard::BasicWizardSerializer do
fab!(:user) { Fabricate(:user) }
+ let(:template) { get_wizard_fixture("wizard") }
it 'should return basic wizard attributes' do
- CustomWizard::Template.save(
- JSON.parse(File.open(
- "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
- ).read),
- skip_jobs: true)
+ CustomWizard::Template.save(template, skip_jobs: true)
json = CustomWizard::BasicWizardSerializer.new(
CustomWizard::Builder.new("super_mega_fun_wizard", user).build,
scope: Guardian.new(user)
diff --git a/spec/serializers/custom_wizard/custom_field_serializer_spec.rb b/spec/serializers/custom_wizard/custom_field_serializer_spec.rb
index 4f5ffd72..2cf92f52 100644
--- a/spec/serializers/custom_wizard/custom_field_serializer_spec.rb
+++ b/spec/serializers/custom_wizard/custom_field_serializer_spec.rb
@@ -4,12 +4,7 @@ require_relative '../../plugin_helper'
describe CustomWizard::CustomFieldSerializer do
fab!(:user) { Fabricate(:user) }
-
- let(:custom_field_json) {
- JSON.parse(File.open(
- "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/custom_field/custom_fields.json"
- ).read)
- }
+ let(:custom_field_json) { get_wizard_fixture("custom_field/custom_fields") }
it 'should return custom field attributes' do
custom_field_json['custom_fields'].each do |field_json|
diff --git a/spec/serializers/custom_wizard/pro/authentication_serializer_spec.rb b/spec/serializers/custom_wizard/pro/authentication_serializer_spec.rb
new file mode 100644
index 00000000..53dd74c2
--- /dev/null
+++ b/spec/serializers/custom_wizard/pro/authentication_serializer_spec.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require_relative '../../../plugin_helper'
+
+describe CustomWizard::ProAuthenticationSerializer do
+ fab!(:user) { Fabricate(:user) }
+
+ it 'should return pro authentication attributes' do
+ auth = CustomWizard::ProAuthentication.new(OpenStruct.new(key: '1234', auth_at: Time.now, auth_by: user.id))
+ serialized = described_class.new(auth, root: false).as_json
+
+ expect(serialized[:active]).to eq(true)
+ expect(serialized[:client_id]).to eq(auth.client_id)
+ expect(serialized[:auth_by]).to eq(auth.auth_by)
+ expect(serialized[:auth_at]).to eq(auth.auth_at)
+ end
+end
diff --git a/spec/serializers/custom_wizard/pro/subscription_serializer_spec.rb b/spec/serializers/custom_wizard/pro/subscription_serializer_spec.rb
new file mode 100644
index 00000000..a775863e
--- /dev/null
+++ b/spec/serializers/custom_wizard/pro/subscription_serializer_spec.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+require_relative '../../../plugin_helper'
+
+describe CustomWizard::ProSubscriptionSerializer do
+ it 'should return pro subscription attributes' do
+ sub = CustomWizard::ProSubscription.new(OpenStruct.new(type: 'community', updated_at: Time.now))
+ serialized = described_class.new(sub, root: false).as_json
+
+ expect(serialized[:active]).to eq(true)
+ expect(serialized[:type]).to eq('community')
+ expect(serialized[:updated_at]).to eq(sub.updated_at)
+ end
+end
diff --git a/spec/serializers/custom_wizard/pro_serializer_spec.rb b/spec/serializers/custom_wizard/pro_serializer_spec.rb
new file mode 100644
index 00000000..45c1956e
--- /dev/null
+++ b/spec/serializers/custom_wizard/pro_serializer_spec.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+require_relative '../../plugin_helper'
+
+describe CustomWizard::ProSerializer do
+ it 'should return pro attributes' do
+ pro = CustomWizard::Pro.new
+ serialized = described_class.new(pro, root: false)
+
+ expect(serialized.server).to eq(pro.server)
+ expect(serialized.authentication.class).to eq(CustomWizard::ProAuthentication)
+ expect(serialized.subscription.class).to eq(CustomWizard::ProSubscription)
+ end
+end
diff --git a/spec/serializers/custom_wizard/wizard_field_serializer_spec.rb b/spec/serializers/custom_wizard/wizard_field_serializer_spec.rb
index 1fa9671c..349b21f8 100644
--- a/spec/serializers/custom_wizard/wizard_field_serializer_spec.rb
+++ b/spec/serializers/custom_wizard/wizard_field_serializer_spec.rb
@@ -4,13 +4,10 @@ require_relative '../../plugin_helper'
describe CustomWizard::FieldSerializer do
fab!(:user) { Fabricate(:user) }
+ let(:template) { get_wizard_fixture("wizard") }
before do
- CustomWizard::Template.save(
- JSON.parse(File.open(
- "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
- ).read),
- skip_jobs: true)
+ CustomWizard::Template.save(template, skip_jobs: true)
@wizard = CustomWizard::Builder.new("super_mega_fun_wizard", user).build
end
diff --git a/spec/serializers/custom_wizard/wizard_serializer_spec.rb b/spec/serializers/custom_wizard/wizard_serializer_spec.rb
index 2052639a..fe36d5a2 100644
--- a/spec/serializers/custom_wizard/wizard_serializer_spec.rb
+++ b/spec/serializers/custom_wizard/wizard_serializer_spec.rb
@@ -5,19 +5,11 @@ require_relative '../../plugin_helper'
describe CustomWizard::WizardSerializer do
fab!(:user) { Fabricate(:user) }
fab!(:category) { Fabricate(:category) }
-
- let(:similar_topics_validation) {
- JSON.parse(File.open(
- "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/field/validation/similar_topics.json"
- ).read)
- }
+ let(:template) { get_wizard_fixture("wizard") }
+ let(:similar_topics_validation) { get_wizard_fixture("field/validation/similar_topics") }
before do
- CustomWizard::Template.save(
- JSON.parse(File.open(
- "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
- ).read),
- skip_jobs: true)
+ CustomWizard::Template.save(template, skip_jobs: true)
@template = CustomWizard::Template.find('super_mega_fun_wizard')
end
diff --git a/spec/serializers/custom_wizard/wizard_step_serializer_spec.rb b/spec/serializers/custom_wizard/wizard_step_serializer_spec.rb
index 35ce0fd2..53afa8e5 100644
--- a/spec/serializers/custom_wizard/wizard_step_serializer_spec.rb
+++ b/spec/serializers/custom_wizard/wizard_step_serializer_spec.rb
@@ -4,22 +4,8 @@ require_relative '../../plugin_helper'
describe CustomWizard::StepSerializer do
fab!(:user) { Fabricate(:user) }
-
- let(:wizard_template) {
- JSON.parse(
- File.open(
- "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
- ).read
- )
- }
-
- let(:required_data_json) {
- JSON.parse(
- File.open(
- "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/step/required_data.json"
- ).read
- )
- }
+ let(:wizard_template) { get_wizard_fixture("wizard") }
+ let(:required_data_json) { get_wizard_fixture("step/required_data") }
before do
CustomWizard::Template.save(wizard_template, skip_jobs: true)
|