0
0
Fork 1
Spiegel von https://github.com/paviliondev/discourse-custom-wizard.git synchronisiert 2024-11-22 09:20:29 +01:00

move guest toggle to permitted attribute

Dieser Commit ist enthalten in:
Angus McLeod 2023-02-07 12:46:17 +01:00
Ursprung 2c84f019bb
Commit 8f8c6d50c6
23 geänderte Dateien mit 199 neuen und 66 gelöschten Zeilen

Datei anzeigen

@ -80,7 +80,6 @@ class CustomWizard::AdminWizardController < CustomWizard::AdminController
:prompt_completion, :prompt_completion,
:restart_on_revisit, :restart_on_revisit,
:resume_on_revisit, :resume_on_revisit,
:allow_guests,
:theme_id, :theme_id,
permitted: mapped_params, permitted: mapped_params,
steps: [ steps: [

Datei anzeigen

@ -9,8 +9,7 @@ class CustomWizard::WizardSerializer < CustomWizard::BasicWizardSerializer
:completed, :completed,
:required, :required,
:permitted, :permitted,
:resume_on_revisit, :resume_on_revisit
:allow_guests
has_many :steps, serializer: ::CustomWizard::StepSerializer, embed: :objects has_many :steps, serializer: ::CustomWizard::StepSerializer, embed: :objects
has_one :user, serializer: ::BasicUserSerializer, embed: :objects has_one :user, serializer: ::BasicUserSerializer, embed: :objects

Datei anzeigen

@ -116,6 +116,9 @@ export default Component.extend({
groupEnabled: computed("options.groupSelection", "inputType", function () { groupEnabled: computed("options.groupSelection", "inputType", function () {
return this.optionEnabled("groupSelection"); return this.optionEnabled("groupSelection");
}), }),
guestGroup: computed("options.guestGroup", "inputType", function () {
return this.optionEnabled("guestGroup");
}),
userEnabled: computed("options.userSelection", "inputType", function () { userEnabled: computed("options.userSelection", "inputType", function () {
return this.optionEnabled("userSelection"); return this.optionEnabled("userSelection");
}), }),
@ -126,7 +129,26 @@ export default Component.extend({
return this.connector === "is"; return this.connector === "is";
}), }),
groups: alias("site.groups"), @discourseComputed("site.groups", "guestGroup")
groups(groups, guestGroup) {
let result = groups;
if (!guestGroup) {
return result;
}
let guestIndex;
result.forEach((r, index) => {
if (r.id === 0) {
r.name = I18n.t("admin.wizard.selector.label.users");
guestIndex = index;
}
});
result.splice(guestIndex, 0, {
id: -1,
name: I18n.t("admin.wizard.selector.label.guests"),
});
return result;
},
categories: alias("site.categories"), categories: alias("site.categories"),
showComboBox: or( showComboBox: or(
"showWizardField", "showWizardField",

Datei anzeigen

@ -32,6 +32,7 @@ export default Component.extend({
pairConnector: options.pairConnector || null, pairConnector: options.pairConnector || null,
outputConnector: options.outputConnector || null, outputConnector: options.outputConnector || null,
context: options.context || null, context: options.context || null,
guestGroup: options.guestGroup || false,
}; };
let inputTypes = ["key", "value", "output"]; let inputTypes = ["key", "value", "output"];

Datei anzeigen

@ -56,7 +56,7 @@ export default SingleSelectComponent.extend(Subscription, {
return attributes; return attributes;
}, },
@discourseComputed("feature", "attribute", "wizard.allow_guests") @discourseComputed("feature", "attribute", "wizard.allowGuests")
content(feature, attribute, allowGuests) { content(feature, attribute, allowGuests) {
return this.contentList(feature, attribute, allowGuests) return this.contentList(feature, attribute, allowGuests)
.map((value) => { .map((value) => {

Datei anzeigen

@ -15,7 +15,6 @@ const wizard = {
prompt_completion: null, prompt_completion: null,
restart_on_revisit: null, restart_on_revisit: null,
resume_on_revisit: null, resume_on_revisit: null,
allow_guests: null,
theme_id: null, theme_id: null,
permitted: null, permitted: null,
}, },

Datei anzeigen

@ -5,8 +5,16 @@ import wizardSchema from "../lib/wizard-schema";
import { Promise } from "rsvp"; import { Promise } from "rsvp";
import { ajax } from "discourse/lib/ajax"; import { ajax } from "discourse/lib/ajax";
import { popupAjaxError } from "discourse/lib/ajax-error"; import { popupAjaxError } from "discourse/lib/ajax-error";
import discourseComputed from "discourse-common/utils/decorators";
const GUEST_GROUP_ID = -1;
const CustomWizardAdmin = EmberObject.extend({ const CustomWizardAdmin = EmberObject.extend({
@discourseComputed("permitted.@each")
allowGuests(permitted) {
return permitted.filter((p) => p.output === GUEST_GROUP_ID).length;
},
save(opts) { save(opts) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let wizard = this.buildJson(this, "wizard"); let wizard = this.buildJson(this, "wizard");

Datei anzeigen

@ -4,13 +4,7 @@ import Route from "@ember/routing/route";
export default Route.extend({ export default Route.extend({
beforeModel() { beforeModel() {
const wizard = getCachedWizard(); const wizard = getCachedWizard();
if ( if (wizard && wizard.permitted && !wizard.completed && wizard.start) {
wizard &&
(wizard.user || wizard.allow_guests) &&
wizard.permitted &&
!wizard.completed &&
wizard.start
) {
this.replaceWith("customWizardStep", wizard.start); this.replaceWith("customWizardStep", wizard.start);
} }
}, },
@ -26,7 +20,7 @@ export default Route.extend({
const wizardId = model.get("id"); const wizardId = model.get("id");
const user = model.get("user"); const user = model.get("user");
const name = model.get("name"); const name = model.get("name");
const requiresLogin = !user && !model.get("allow_guests"); const requiresLogin = !user && !permitted;
const notPermitted = !permitted; const notPermitted = !permitted;
const props = { const props = {

Datei anzeigen

@ -7,12 +7,7 @@ export default Route.extend({
const wizard = getCachedWizard(); const wizard = getCachedWizard();
this.set("wizard", wizard); this.set("wizard", wizard);
if ( if (!wizard || !wizard.permitted || wizard.completed) {
!wizard ||
(!wizard.user && !wizard.allow_guests) ||
!wizard.permitted ||
wizard.completed
) {
this.replaceWith("customWizard"); this.replaceWith("customWizard");
} }
}, },

Datei anzeigen

@ -140,22 +140,13 @@
context="wizard" context="wizard"
inputTypes="assignment,validation" inputTypes="assignment,validation"
groupSelection="output" groupSelection="output"
guestGroup=true
userFieldSelection="key" userFieldSelection="key"
textSelection="value" textSelection="value"
inputConnector="and" inputConnector="and"
)}} )}}
</div> </div>
</div> </div>
<div class="setting full">
<div class="setting-label">
<label>{{i18n "admin.wizard.allow_guests"}}</label>
</div>
<div class="setting-value">
{{input type="checkbox" checked=wizard.allow_guests}}
<span>{{i18n "admin.wizard.allow_guests_label"}}</span>
</div>
</div>
{{/wizard-subscription-container}} {{/wizard-subscription-container}}
</div> </div>

Datei anzeigen

@ -105,8 +105,6 @@ en:
restart_on_revisit_label: "Clear submissions on each visit." restart_on_revisit_label: "Clear submissions on each visit."
resume_on_revisit: "Resume" resume_on_revisit: "Resume"
resume_on_revisit_label: "Ask the user if they want to resume on each visit." resume_on_revisit_label: "Ask the user if they want to resume on each visit."
allow_guests: "Guests"
allow_guests_label: "Allow guests to use the wizard (disables user-specific features)."
theme_id: "Theme" theme_id: "Theme"
no_theme: "Select a Theme (optional)" no_theme: "Select a Theme (optional)"
save: "Save Changes" save: "Save Changes"
@ -219,6 +217,8 @@ en:
list: "list" list: "list"
custom_field: "custom field" custom_field: "custom field"
value: "value" value: "value"
users: "users"
guests: "users and guests"
placeholder: placeholder:
text: "Enter text" text: "Enter text"

Datei anzeigen

@ -54,7 +54,7 @@ en:
after_time: "After time setting is invalid." after_time: "After time setting is invalid."
liquid_syntax_error: "Liquid syntax error in %{attribute}: %{message}" liquid_syntax_error: "Liquid syntax error in %{attribute}: %{message}"
subscription: "%{type} %{property} is subscription only" subscription: "%{type} %{property} is subscription only"
allow_guests: "%{object_id} is not permitted when allow_guests is enabled" not_permitted_for_guests: "%{object_id} is not permitted when guests can access the wizard"
site_settings: site_settings:
custom_wizard_enabled: "Enable custom wizards." custom_wizard_enabled: "Enable custom wizards."

Datei anzeigen

@ -136,7 +136,7 @@ class CustomWizard::Action
params[:archetype] = Archetype.private_message params[:archetype] = Archetype.private_message
poster = @wizard.allow_guests ? Discourse.system_user : user poster = user || Discourse.system_user
creator = PostCreator.new(poster, params) creator = PostCreator.new(poster, params)
post = creator.create post = creator.create

Datei anzeigen

@ -24,12 +24,6 @@ class CustomWizard::Subscription
standard: ['*'], standard: ['*'],
business: ['*'], business: ['*'],
community: ['*'] community: ['*']
},
allow_guests: {
none: [],
standard: ['*'],
business: ['*'],
community: []
} }
}, },
step: { step: {

Datei anzeigen

@ -82,8 +82,12 @@ class CustomWizard::TemplateValidator
end end
def validate_action(action) def validate_action(action)
if @data[:allow_guests] && CustomWizard::Action::REQUIRES_USER.include?(action[:type]) guests_permitted = @data[:permitted] && @data[:permitted].any? do |m|
errors.add :base, I18n.t("wizard.validation.allow_guests", object_id: action[:id]) m[:output] === CustomWizard::Wizard::GUEST_GROUP_ID
end
if guests_permitted && CustomWizard::Action::REQUIRES_USER.include?(action[:type])
errors.add :base, I18n.t("wizard.validation.not_permitted_for_guests", object_id: action[:id])
end end
end end

Datei anzeigen

@ -20,7 +20,6 @@ class CustomWizard::Wizard
:prompt_completion, :prompt_completion,
:restart_on_revisit, :restart_on_revisit,
:resume_on_revisit, :resume_on_revisit,
:allow_guests,
:permitted, :permitted,
:steps, :steps,
:step_ids, :step_ids,
@ -37,6 +36,7 @@ class CustomWizard::Wizard
attr_reader :all_step_ids attr_reader :all_step_ids
GUEST_ID_PREFIX ||= "guest" GUEST_ID_PREFIX ||= "guest"
GUEST_GROUP_ID = -1
def initialize(attrs = {}, user = nil, guest_id = nil) def initialize(attrs = {}, user = nil, guest_id = nil)
if user if user
@ -55,7 +55,6 @@ class CustomWizard::Wizard
@prompt_completion = cast_bool(attrs['prompt_completion']) @prompt_completion = cast_bool(attrs['prompt_completion'])
@restart_on_revisit = cast_bool(attrs['restart_on_revisit']) @restart_on_revisit = cast_bool(attrs['restart_on_revisit'])
@resume_on_revisit = cast_bool(attrs['resume_on_revisit']) @resume_on_revisit = cast_bool(attrs['resume_on_revisit'])
@allow_guests = cast_bool(attrs['allow_guests'])
@after_signup = cast_bool(attrs['after_signup']) @after_signup = cast_bool(attrs['after_signup'])
@after_time = cast_bool(attrs['after_time']) @after_time = cast_bool(attrs['after_time'])
@after_time_scheduled = attrs['after_time_scheduled'] @after_time_scheduled = attrs['after_time_scheduled']
@ -212,9 +211,8 @@ class CustomWizard::Wizard
def permitted? def permitted?
return nil unless actor_id return nil unless actor_id
return true if allow_guests return true if user && (user.admin? || permitted.blank?)
return false unless user return false if !user && permitted.blank?
return true if user.admin? || permitted.blank?
mapper = CustomWizard::Mapper.new( mapper = CustomWizard::Mapper.new(
inputs: permitted, inputs: permitted,
@ -228,22 +226,22 @@ class CustomWizard::Wizard
return true if mapper.blank? return true if mapper.blank?
mapper.all? do |m| mapper.all? do |m|
if m[:type] === 'assignment' if !user
[*m[:result]].include?(Group::AUTO_GROUPS[:everyone]) || m[:type] === 'assignment' && [*m[:result]].include?(GUEST_GROUP_ID)
GroupUser.exists?(group_id: m[:result], user_id: user.id)
elsif m[:type] === 'validation'
m[:result]
else else
true if m[:type] === 'assignment'
[*m[:result]].include?(Group::AUTO_GROUPS[:everyone]) ||
GroupUser.exists?(group_id: m[:result], user_id: user.id)
elsif m[:type] === 'validation'
m[:result]
else
true
end
end end
end end
end end
def can_access? def can_access?
return nil unless actor_id
return true if allow_guests
return false unless user
return true if user.admin
permitted? && (multiple_submissions || !completed?) permitted? && (multiple_submissions || !completed?)
end end

Datei anzeigen

@ -18,6 +18,7 @@ describe CustomWizard::Action do
let(:api_test_endpoint) { get_wizard_fixture("endpoints/test_endpoint") } let(:api_test_endpoint) { get_wizard_fixture("endpoints/test_endpoint") }
let(:api_test_endpoint_body) { get_wizard_fixture("endpoints/test_endpoint_body") } let(:api_test_endpoint_body) { get_wizard_fixture("endpoints/test_endpoint_body") }
let(:api_test_no_authorization) { get_wizard_fixture("api/no_authorization") } let(:api_test_no_authorization) { get_wizard_fixture("api/no_authorization") }
let(:guests_permitted) { get_wizard_fixture("wizard/guests_permitted") }
def update_template(template) def update_template(template)
CustomWizard::Template.save(template, skip_jobs: true) CustomWizard::Template.save(template, skip_jobs: true)
@ -302,8 +303,8 @@ describe CustomWizard::Action do
expect(post.exists?).to eq(true) expect(post.exists?).to eq(true)
end end
it "send_message works with allow_guests enabled" do it "send_message works with guests are permitted" do
wizard_template["allow_guests"] = true wizard_template["permitted"] = guests_permitted["permitted"]
wizard_template.delete("actions") wizard_template.delete("actions")
wizard_template['actions'] = [send_message] wizard_template['actions'] = [send_message]
update_template(wizard_template) update_template(wizard_template)

12
spec/fixtures/actions/route_to.json gevendort Normale Datei
Datei anzeigen

@ -0,0 +1,12 @@
{
"id": "route_to",
"type": "route_to",
"url": [
{
"type": "assignment",
"output": "https://google.com",
"output_type": "text",
"output_connector": "set"
}
]
}

12
spec/fixtures/wizard/guests_permitted.json gevendort Normale Datei
Datei anzeigen

@ -0,0 +1,12 @@
{
"permitted": [
{
"type": "assignment",
"output_type": "group",
"output_connector": "set",
"output": [
-1
]
}
]
}

Datei anzeigen

@ -7,11 +7,21 @@ describe CustomWizard::StepsController do
let(:wizard_field_condition_template) { get_wizard_fixture("condition/wizard_field_condition") } let(:wizard_field_condition_template) { get_wizard_fixture("condition/wizard_field_condition") }
let(:user_condition_template) { get_wizard_fixture("condition/user_condition") } let(:user_condition_template) { get_wizard_fixture("condition/user_condition") }
let(:permitted_json) { get_wizard_fixture("wizard/permitted") } let(:permitted_json) { get_wizard_fixture("wizard/permitted") }
let(:route_to_template) { get_wizard_fixture("actions/route_to") }
let(:guests_permitted) { get_wizard_fixture("wizard/guests_permitted") }
before do before do
CustomWizard::Template.save(wizard_template, skip_jobs: true) CustomWizard::Template.save(wizard_template, skip_jobs: true)
end end
def guest_template
temp = wizard_template.dup
temp["permitted"] = guests_permitted["permitted"]
temp.delete("actions")
temp["actions"] = [route_to_template]
temp
end
context "with guest" do context "with guest" do
it "does not perform a step update" do it "does not perform a step update" do
put '/w/super-mega-fun-wizard/steps/step_1.json', params: { put '/w/super-mega-fun-wizard/steps/step_1.json', params: {
@ -22,13 +32,10 @@ describe CustomWizard::StepsController do
expect(response.status).to eq(403) expect(response.status).to eq(403)
end end
context "with allow_guests enabled" do context "with guests permitted" do
before do before do
enable_subscription("standard") enable_subscription("standard")
new_template = wizard_template.dup result = CustomWizard::Template.save(guest_template, skip_jobs: true)
new_template["allow_guests"] = true
new_template.delete("actions")
result = CustomWizard::Template.save(new_template, skip_jobs: true)
end end
it "performs a step update" do it "performs a step update" do
@ -44,6 +51,62 @@ describe CustomWizard::StepsController do
wizard = CustomWizard::Wizard.create(wizard_id, nil, cookies[:custom_wizard_guest_id]) wizard = CustomWizard::Wizard.create(wizard_id, nil, cookies[:custom_wizard_guest_id])
expect(wizard.current_submission.fields['step_1_field_1']).to eq("Text input") expect(wizard.current_submission.fields['step_1_field_1']).to eq("Text input")
end end
context "raises an error" do
it "when the wizard doesnt exist" do
put '/w/not-super-mega-fun-wizard/steps/step_1.json'
expect(response.status).to eq(400)
end
it "when the user cant access the wizard" do
enable_subscription("standard")
new_template = guest_template.dup
new_template["permitted"] = permitted_json["permitted"]
CustomWizard::Template.save(new_template, skip_jobs: true)
put '/w/super-mega-fun-wizard/steps/step_1.json'
expect(response.status).to eq(403)
end
it "when the step doesnt exist" do
put '/w/super-mega-fun-wizard/steps/step_10.json'
expect(response.status).to eq(400)
end
end
it "works if the step has no fields" do
put '/w/super-mega-fun-wizard/steps/step_1.json'
expect(response.status).to eq(200)
expect(response.parsed_body['wizard']['start']).to eq("step_2")
end
it "returns an updated wizard when condition passes" do
new_template = guest_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 will pass"
}
}
expect(response.status).to eq(200)
expect(response.parsed_body['wizard']['start']).to eq("step_2")
end
it "runs completion actions if guest has completed wizard" do
new_template = guest_template.dup
## route_to action
new_template['actions'].last['run_after'] = 'wizard_completion'
CustomWizard::Template.save(new_template, skip_jobs: true)
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
end end
end end

Datei anzeigen

@ -122,4 +122,44 @@ acceptance("Wizard | Guest access", function (needs) {
await visit("/w/wizard"); await visit("/w/wizard");
assert.ok(query(".wizard-column"), true); assert.ok(query(".wizard-column"), true);
}); });
test("Applies the wizard body class", async function (assert) {
await visit("/w/wizard");
assert.ok($("body.custom-wizard").length);
});
test("Applies the body background color", async function (assert) {
await visit("/w/wizard");
assert.ok($("body")[0].style.background);
});
test("Renders the wizard form", async function (assert) {
await visit("/w/wizard");
assert.ok(exists(".wizard-column-contents .wizard-step"), true);
assert.ok(exists(".wizard-footer img"), true);
});
test("Renders the first step", async function (assert) {
await visit("/w/wizard");
assert.strictEqual(
query(".wizard-step-title p").textContent.trim(),
"Text"
);
assert.strictEqual(
query(".wizard-step-description p").textContent.trim(),
"Text inputs!"
);
assert.strictEqual(
query(".wizard-step-description p").textContent.trim(),
"Text inputs!"
);
assert.strictEqual(count(".wizard-step-form .wizard-field"), 6);
assert.ok(exists(".wizard-step-footer .wizard-progress"), true);
assert.ok(exists(".wizard-step-footer .wizard-buttons"), true);
});
test("Removes the wizard body class when navigating away", async function (assert) {
await visit("/");
assert.strictEqual($("body.custom-wizard").length, 0);
});
}); });

Datei anzeigen

@ -6,7 +6,7 @@ export default {
submission_last_updated_at: "2022-03-15T21:11:01+01:00", submission_last_updated_at: "2022-03-15T21:11:01+01:00",
theme_id: 2, theme_id: 2,
required: false, required: false,
permitted: true, permitted: false,
uncategorized_category_id: 1, uncategorized_category_id: 1,
categories: [], categories: [],
subscribed: false, subscribed: false,

Datei anzeigen

@ -7,9 +7,10 @@ import { cloneJSON } from "discourse-common/lib/object";
const wizardNoUser = cloneJSON(wizardJson); const wizardNoUser = cloneJSON(wizardJson);
const wizardGuest = cloneJSON(wizardJson); const wizardGuest = cloneJSON(wizardJson);
wizardGuest.allow_guests = true; wizardGuest.permitted = true;
const wizard = cloneJSON(wizardJson); const wizard = cloneJSON(wizardJson);
wizard.user = cloneJSON(userJson); wizard.user = cloneJSON(userJson);
wizard.permitted = true;
const wizardNotPermitted = cloneJSON(wizard); const wizardNotPermitted = cloneJSON(wizard);
wizardNotPermitted.permitted = false; wizardNotPermitted.permitted = false;