diff --git a/app/controllers/custom_wizard/admin/wizard.rb b/app/controllers/custom_wizard/admin/wizard.rb
index 955deb3f..993d0e6c 100644
--- a/app/controllers/custom_wizard/admin/wizard.rb
+++ b/app/controllers/custom_wizard/admin/wizard.rb
@@ -145,6 +145,8 @@ class CustomWizard::AdminWizardController < CustomWizard::AdminController
custom_fields: mapped_params,
visible: mapped_params,
required: mapped_params,
+ poster: mapped_params,
+ guest_email: mapped_params,
recipient: mapped_params,
categories: mapped_params,
mute_remainder: mapped_params,
diff --git a/assets/javascripts/discourse/components/wizard-custom-action.js.es6 b/assets/javascripts/discourse/components/wizard-custom-action.js.es6
index b9329617..1f4c914a 100644
--- a/assets/javascripts/discourse/components/wizard-custom-action.js.es6
+++ b/assets/javascripts/discourse/components/wizard-custom-action.js.es6
@@ -5,6 +5,7 @@ import { computed } from "@ember/object";
import UndoChanges from "../mixins/undo-changes";
import Component from "@ember/component";
import I18n from "I18n";
+import { WIZARD_USER } from "./wizard-user-chooser";
export default Component.extend(UndoChanges, {
componentType: "action",
diff --git a/assets/javascripts/discourse/components/wizard-mapper-selector.js.es6 b/assets/javascripts/discourse/components/wizard-mapper-selector.js.es6
index eb9e735a..5d67b8f7 100644
--- a/assets/javascripts/discourse/components/wizard-mapper-selector.js.es6
+++ b/assets/javascripts/discourse/components/wizard-mapper-selector.js.es6
@@ -121,6 +121,9 @@ export default Component.extend({
guestGroup: computed("options.guestGroup", "inputType", function () {
return this.optionEnabled("guestGroup");
}),
+ includeMessageableGroups: computed("options.includeMessageableGroups", "inputType", function () {
+ return this.optionEnabled("includeMessageableGroups");
+ }),
userEnabled: computed("options.userSelection", "inputType", function () {
return this.optionEnabled("userSelection");
}),
@@ -352,6 +355,17 @@ export default Component.extend({
return result;
},
+ @discourseComputed("includeMessageableGroups", "options.userLimit")
+ userOptions(includeMessageableGroups, userLimit) {
+ const opts = {
+ includeMessageableGroups
+ }
+ if (userLimit) {
+ opts.maximum = userLimit
+ }
+ return opts;
+ },
+
optionEnabled(type) {
const options = this.options;
if (!options) {
diff --git a/assets/javascripts/discourse/components/wizard-mapper.js.es6 b/assets/javascripts/discourse/components/wizard-mapper.js.es6
index ec58e3f2..16e2e570 100644
--- a/assets/javascripts/discourse/components/wizard-mapper.js.es6
+++ b/assets/javascripts/discourse/components/wizard-mapper.js.es6
@@ -33,6 +33,8 @@ export default Component.extend({
outputConnector: options.outputConnector || null,
context: options.context || null,
guestGroup: options.guestGroup || false,
+ includeMessageableGroups: options.includeMessageableGroups || false,
+ userLimit: options.userLimit || null
};
let inputTypes = ["key", "value", "output"];
diff --git a/assets/javascripts/discourse/components/wizard-user-chooser.js.es6 b/assets/javascripts/discourse/components/wizard-user-chooser.js.es6
new file mode 100644
index 00000000..d52c733d
--- /dev/null
+++ b/assets/javascripts/discourse/components/wizard-user-chooser.js.es6
@@ -0,0 +1,55 @@
+import UserChooserComponent from "select-kit/components/user-chooser";
+
+export const WIZARD_USER = "wizard-user";
+
+export default UserChooserComponent.extend({
+ pluginApiIdentifiers: ["wizard-user-chooser"],
+ classNames: ["user-chooser", "wizard-user-chooser"],
+ classNameBindings: ["selectKit.options.fullWidthWrap:full-width-wrap"],
+ valueProperty: "id",
+ nameProperty: "name",
+
+ modifyComponentForRow() {
+ return "wizard-user-chooser/wizard-user-chooser-row";
+ },
+
+ modifyNoSelection() {
+ return this.defaultItem(WIZARD_USER, I18n.t("admin.wizard.action.poster.wizard_user"));
+ },
+
+ selectKitOptions: {
+ fullWidthWrap: false,
+ autoWrap: false,
+ },
+
+ search() {
+ const superPromise = this._super(...arguments);
+ if (!superPromise) {
+ return;
+ }
+ return superPromise.then((results) => {
+ console.log(results)
+ if (!results || results.length === 0) {
+ return;
+ }
+ return results.map((item) => {
+ const reconstructed = {};
+ if (item.username) {
+ reconstructed.id = item.username;
+ if (item.username.includes("@")) {
+ reconstructed.isEmail = true;
+ } else {
+ reconstructed.isUser = true;
+ reconstructed.name = item.name;
+ reconstructed.showUserStatus = this.showUserStatus;
+ }
+ } else if (item.name) {
+ reconstructed.id = item.name;
+ reconstructed.name = item.full_name;
+ reconstructed.isGroup = true;
+ }
+ return { ...item, ...reconstructed };
+ });
+ });
+ },
+});
diff --git a/assets/javascripts/discourse/components/wizard-user-chooser/wizard-user-chooser-row.js b/assets/javascripts/discourse/components/wizard-user-chooser/wizard-user-chooser-row.js
new file mode 100644
index 00000000..125bccca
--- /dev/null
+++ b/assets/javascripts/discourse/components/wizard-user-chooser/wizard-user-chooser-row.js
@@ -0,0 +1,5 @@
+import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row";
+
+export default SelectKitRowComponent.extend({
+ classNames: ["user-row", "wizard-user-chooser-row"],
+});
diff --git a/assets/javascripts/discourse/lib/wizard-schema.js.es6 b/assets/javascripts/discourse/lib/wizard-schema.js.es6
index 710a3594..0c76fa53 100644
--- a/assets/javascripts/discourse/lib/wizard-schema.js.es6
+++ b/assets/javascripts/discourse/lib/wizard-schema.js.es6
@@ -100,6 +100,8 @@ const action = {
custom_fields: null,
skip_redirect: null,
suppress_notifications: null,
+ poster: 'wizard-user',
+ guest_email: null,
add_event: null,
add_location: null,
},
@@ -111,6 +113,8 @@ const action = {
skip_redirect: null,
custom_fields: null,
required: null,
+ poster: 'wizard-user',
+ guest_email: null,
recipient: null,
suppress_notifications: null,
},
@@ -184,6 +188,7 @@ const action = {
"custom_fields",
"required",
"recipient",
+ "poster",
"profile_updates",
"group",
"url",
diff --git a/assets/javascripts/discourse/templates/components/wizard-custom-action.hbs b/assets/javascripts/discourse/templates/components/wizard-custom-action.hbs
index 80152674..1116d79f 100644
--- a/assets/javascripts/discourse/templates/components/wizard-custom-action.hbs
+++ b/assets/javascripts/discourse/templates/components/wizard-custom-action.hbs
@@ -98,6 +98,48 @@
{{/if}}
+
+
+
+
+
+
+
+ {{wizard-mapper
+ inputs=this.action.poster
+ property="poster"
+ onUpdate=(action "mappedFieldUpdated")
+ options=(hash
+ textSelection='key,value'
+ wizardFieldSelection=true
+ userSelection="output"
+ outputDefaultSelection="user"
+ userLimit="1"
+ context="action"
+ )
+ }}
+
+
+
+
+
+
+
+
+
+ {{wizard-mapper
+ inputs=this.action.guest_email
+ property="guest_email"
+ onUpdate=(action "mappedFieldUpdated")
+ options=(hash
+ textSelection="key,value"
+ wizardFieldSelection=true
+ outputPlaceholder="admin.wizard.action.guest_email.placeholder"
+ context="action"
+ )
+ }}
+
+
{{/if}}
{{#if publicTopicFields}}
@@ -217,6 +259,7 @@
userSelection="output"
outputDefaultSelection="user"
context="action"
+ includeMessageableGroups="true"
)
}}
diff --git a/assets/javascripts/discourse/templates/components/wizard-mapper-selector.hbs b/assets/javascripts/discourse/templates/components/wizard-mapper-selector.hbs
index 80669aa4..3971cd26 100644
--- a/assets/javascripts/discourse/templates/components/wizard-mapper-selector.hbs
+++ b/assets/javascripts/discourse/templates/components/wizard-mapper-selector.hbs
@@ -66,12 +66,12 @@
{{/if}}
{{#if showUser}}
- {{email-group-user-chooser
+ {{wizard-user-chooser
placeholderKey=placeholderKey
value=value
autocomplete="discourse"
onChange=(action "changeUserValue")
- options=(hash includeMessageableGroups="true")
+ options=userOptions
}}
{{/if}}
\ No newline at end of file
diff --git a/assets/javascripts/discourse/templates/components/wizard-user-chooser/wizard-user-chooser-row.hbs b/assets/javascripts/discourse/templates/components/wizard-user-chooser/wizard-user-chooser-row.hbs
new file mode 100644
index 00000000..8ff700b0
--- /dev/null
+++ b/assets/javascripts/discourse/templates/components/wizard-user-chooser/wizard-user-chooser-row.hbs
@@ -0,0 +1,20 @@
+{{#if this.item.isUser}}
+ {{avatar this.item imageSize="tiny"}}
+
+ {{format-username this.item.id}}
+ {{this.item.name}}
+
+ {{#if (and this.item.showUserStatus this.item.status)}}
+
+ {{/if}}
+ {{decorate-username-selector this.item.id}}
+{{else if this.item.isGroup}}
+ {{d-icon "users"}}
+
+ {{this.item.id}}
+ {{this.item.full_name}}
+
+{{else}}
+ {{d-icon "envelope"}}
+ {{this.item.id}}
+{{/if}}
\ No newline at end of file
diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml
index 71d5eeac..3e7feba1 100644
--- a/config/locales/client.en.yml
+++ b/config/locales/client.en.yml
@@ -344,6 +344,12 @@ en:
include: "Include Fields"
title: "Title"
post: "Post"
+ poster:
+ label: "Poster"
+ wizard_user: "Wizard user"
+ guest_email:
+ label: "Guest email"
+ placeholder: "Field for guest email"
topic_attr: "Topic Attribute"
interpolate_fields: "Insert wizard fields using the field_id in w{}. Insert user fields using field key in u{}."
diff --git a/lib/custom_wizard/action.rb b/lib/custom_wizard/action.rb
index 7972f92c..d539d785 100644
--- a/lib/custom_wizard/action.rb
+++ b/lib/custom_wizard/action.rb
@@ -7,13 +7,14 @@ class CustomWizard::Action
:result
REQUIRES_USER = %w[
- create_topic
update_profile
open_composer
watch_categories
add_to_group
]
+ WIZARD_USER = 'wizard-user'
+
def initialize(opts)
@wizard = opts[:wizard]
@action = opts[:action]
@@ -69,7 +70,7 @@ class CustomWizard::Action
end
if params[:title].present? && params[:raw].present?
- creator = PostCreator.new(user, params)
+ creator = PostCreator.new(topic_poster, params)
post = creator.create
if creator.errors.present?
@@ -138,8 +139,7 @@ class CustomWizard::Action
params[:archetype] = Archetype.private_message
- poster = user || Discourse.system_user
- creator = PostCreator.new(poster, params)
+ creator = PostCreator.new(topic_poster, params)
post = creator.create
if creator.errors.present?
@@ -653,6 +653,45 @@ class CustomWizard::Action
params
end
+ def topic_poster
+ @topic_poster ||= begin
+ poster_id = CustomWizard::Mapper.new(
+ inputs: action['poster'],
+ data: mapper_data,
+ user: user,
+ ).perform
+ poster_id = [*poster_id].first if poster_id.present?
+
+ if poster_id.blank? || poster_id === WIZARD_USER
+ poster = user || guest_user
+ else
+ poster = User.find_by_username(poster_id)
+ end
+
+ poster || Discourse.system_user
+ end
+ end
+
+ def guest_user
+ @guest_user ||= begin
+ return nil unless action['guest_email']
+
+ email = CustomWizard::Mapper.new(
+ inputs: action['guest_email'],
+ data: mapper_data,
+ ).perform
+
+ if email&.match(/@/)
+ User.create!(
+ email: email,
+ username: UserNameSuggester.suggest(email),
+ name: User.suggest_name(email),
+ staged: true,
+ )
+ end
+ end
+ end
+
def new_group_params
params = {}
diff --git a/lib/custom_wizard/field.rb b/lib/custom_wizard/field.rb
index e8e7bbf9..c0ebaae3 100644
--- a/lib/custom_wizard/field.rb
+++ b/lib/custom_wizard/field.rb
@@ -31,7 +31,6 @@ class CustomWizard::Field
:step
REQUIRES_USER = %w[
- composer
upload
]
diff --git a/spec/components/custom_wizard/action_spec.rb b/spec/components/custom_wizard/action_spec.rb
index 4ce6d842..13f1b13f 100644
--- a/spec/components/custom_wizard/action_spec.rb
+++ b/spec/components/custom_wizard/action_spec.rb
@@ -46,7 +46,7 @@ describe CustomWizard::Action do
update_template(wizard_template)
end
- context 'creating a topic' do
+ describe '#create_topic' do
it "works" do
wizard = CustomWizard::Builder.new(@template[:id], user).build
wizard.create_updater(
@@ -160,6 +160,43 @@ describe CustomWizard::Action do
expect(action.result.success?).to eq(true)
expect(TopicCustomField.exists?(name: custom_field_name)).to eq(true)
end
+
+ it "allows poster to be set" do
+ wizard_template[:actions][0]["poster"] = [
+ {
+ "type": "assignment",
+ "output_type": "user",
+ "output_connector": "set",
+ "output": [
+ "angus1"
+ ]
+ }
+ ]
+ update_template(wizard_template)
+
+ wizard = CustomWizard::Builder.new(@template[:id], user).build
+ wizard.create_updater(
+ wizard.steps.first.id,
+ step_1_field_1: "Topic Title",
+ step_1_field_2: "topic body"
+ ).update
+ wizard.create_updater(wizard.steps.second.id, {}).update
+ wizard.create_updater(wizard.steps.last.id,
+ step_3_field_3: category.id
+ ).update
+
+ topic = Topic.where(
+ title: "Topic Title",
+ category_id: category.id
+ )
+ expect(topic.exists?).to eq(true)
+ post = Post.find_by(
+ topic_id: topic.pluck(:id),
+ raw: "topic body"
+ )
+ expect(post.present?).to eq(true)
+ expect(post.user.username).to eq('angus1')
+ end
end
it 'updates a profile' do
@@ -234,6 +271,7 @@ describe CustomWizard::Action do
context "standard subscription actions" do
before do
enable_subscription("standard")
+ Jobs.run_immediately!
end
it 'watches tags' do
@@ -333,61 +371,117 @@ describe CustomWizard::Action do
expect(user2.reload.notifications.count).to eq(1)
end
- it "send_message works when guests are permitted" do
- wizard_template["permitted"] = guests_permitted["permitted"]
- wizard_template.delete("actions")
- wizard_template['actions'] = [send_message]
- update_template(wizard_template)
+ context "with a guest" do
+ describe "#create_topic" do
+ it "creates a staged guest poster if guest_email is set" do
+ Jobs.run_immediately!
- User.create(username: 'angus1', email: "angus1@email.com")
+ wizard_template["permitted"] = guests_permitted["permitted"]
+ wizard_template[:steps][0][:fields] << {
+ "id": "step_1_field_5",
+ "label": "Guest Email",
+ "type": "text",
+ "min_length": "3",
+ }.as_json
+ create_topic["run_after"] = "step_3"
+ create_topic["guest_email"] = [
+ {
+ "type": "assignment",
+ "output": "step_1_field_5",
+ "output_type": "wizard_field",
+ "output_connector": "set"
+ }
+ ]
+ create_topic["category"] = [
+ {
+ "type": "assignment",
+ "output": "step_3_field_3",
+ "output_type": "wizard_field",
+ "output_connector": "set"
+ }
+ ]
+ wizard_template.delete("actions")
+ wizard_template[:actions] = [create_topic]
- wizard = CustomWizard::Builder.new(wizard_template["id"], nil, CustomWizard::Wizard.generate_guest_id).build
- wizard.create_updater(wizard.steps[0].id, {}).update
- updater = wizard.create_updater(wizard.steps[1].id, {})
- updater.update
+ update_template(wizard_template)
- topic = Topic.where(archetype: Archetype.private_message, title: "Message title")
- post = Post.where(topic_id: topic.pluck(:id))
+ wizard = CustomWizard::Builder.new(
+ @template[:id],
+ nil,
+ CustomWizard::Wizard.generate_guest_id
+ ).build
+ wizard.create_updater(
+ wizard.steps.first.id,
+ step_1_field_5: "guest@email.com"
+ ).update
+ wizard.create_updater(wizard.steps.second.id, {}).update
+ wizard.create_updater(wizard.steps.last.id,
+ step_3_field_3: category.id
+ ).update
- expect(topic.exists?).to eq(true)
- expect(topic.first.topic_allowed_users.first.user.username).to eq('angus1')
- expect(topic.first.topic_allowed_users.second.user.username).to eq(Discourse.system_user.username)
- expect(post.exists?).to eq(true)
- end
+ topic = Topic.where(category_id: category.id).first
+ expect(topic.present?).to eq(true)
+ expect(topic.posts.first.user.staged).to eq(true)
+ expect(topic.posts.first.user.primary_email.email).to eq('guest@email.com')
+ end
+ end
- it "send_message works when guests are permitted and the target is an email address" do
- Jobs.run_immediately!
+ describe "#send_message" do
+ it "works" do
+ wizard_template["permitted"] = guests_permitted["permitted"]
+ wizard_template.delete("actions")
+ wizard_template['actions'] = [send_message]
+ update_template(wizard_template)
- wizard_template["permitted"] = guests_permitted["permitted"]
- wizard_template.delete("actions")
+ wizard = CustomWizard::Builder.new(wizard_template["id"], nil, CustomWizard::Wizard.generate_guest_id).build
+ wizard.create_updater(wizard.steps[0].id, {}).update
+ updater = wizard.create_updater(wizard.steps[1].id, {})
+ updater.update
- send_message["recipient"] = [
- {
- "type": "assignment",
- "output": "step_1_field_1",
- "output_type": "wizard_field",
- "output_connector": "set"
- }
- ]
+ topic = Topic.where(archetype: Archetype.private_message, title: "Message title")
+ post = Post.where(topic_id: topic.pluck(:id))
- wizard_template['actions'] = [send_message]
- update_template(wizard_template)
+ expect(topic.exists?).to eq(true)
+ expect(topic.first.topic_allowed_users.first.user.username).to eq(user1.username)
+ expect(topic.first.topic_allowed_users.second.user.username).to eq(Discourse.system_user.username)
+ expect(post.exists?).to eq(true)
+ end
- NotificationEmailer.expects(:process_notification).once
+ it "works when the target is an email address" do
+ Jobs.run_immediately!
- wizard = CustomWizard::Builder.new(wizard_template["id"], nil, CustomWizard::Wizard.generate_guest_id).build
- wizard.create_updater(wizard.steps[0].id, step_1_field_1: "guest@email.com").update
- updater = wizard.create_updater(wizard.steps[1].id, {})
- updater.update
+ wizard_template["permitted"] = guests_permitted["permitted"]
+ wizard_template.delete("actions")
- topic = Topic.where(archetype: Archetype.private_message, title: "Message title")
- post = Post.where(topic_id: topic.pluck(:id))
+ send_message["recipient"] = [
+ {
+ "type": "assignment",
+ "output": "step_1_field_1",
+ "output_type": "wizard_field",
+ "output_connector": "set"
+ }
+ ]
- expect(topic.exists?).to eq(true)
- expect(topic.first.topic_allowed_users.first.user.staged).to eq(true)
- expect(topic.first.topic_allowed_users.first.user.primary_email.email).to eq('guest@email.com')
- expect(topic.first.topic_allowed_users.second.user.username).to eq(Discourse.system_user.username)
- expect(post.exists?).to eq(true)
+ wizard_template['actions'] = [send_message]
+ update_template(wizard_template)
+
+ NotificationEmailer.expects(:process_notification).once
+
+ wizard = CustomWizard::Builder.new(wizard_template["id"], nil, CustomWizard::Wizard.generate_guest_id).build
+ wizard.create_updater(wizard.steps[0].id, step_1_field_1: "guest@email.com").update
+ updater = wizard.create_updater(wizard.steps[1].id, {})
+ updater.update
+
+ topic = Topic.where(archetype: Archetype.private_message, title: "Message title")
+ post = Post.where(topic_id: topic.pluck(:id))
+
+ expect(topic.exists?).to eq(true)
+ expect(topic.first.topic_allowed_users.first.user.staged).to eq(true)
+ expect(topic.first.topic_allowed_users.first.user.primary_email.email).to eq('guest@email.com')
+ expect(topic.first.topic_allowed_users.second.user.username).to eq(Discourse.system_user.username)
+ expect(post.exists?).to eq(true)
+ end
+ end
end
end
@@ -528,7 +622,6 @@ describe CustomWizard::Action do
wizard.create_updater(wizard.steps.second.id, {}).update
wizard.create_updater(wizard.steps.last.id, step_3_field_3: category.id)
.update
- User.create(username: 'angus1', email: 'angus1@email.com')
wizard.create_updater(wizard.steps[0].id, {}).update
wizard.create_updater(wizard.steps[1].id, {}).update
topic = Topic.where(title: 'Topic Title', category_id: category.id)
diff --git a/spec/components/custom_wizard/template_validator_spec.rb b/spec/components/custom_wizard/template_validator_spec.rb
index fe61be91..38288409 100644
--- a/spec/components/custom_wizard/template_validator_spec.rb
+++ b/spec/components/custom_wizard/template_validator_spec.rb
@@ -155,9 +155,6 @@ describe CustomWizard::TemplateValidator do
validator = CustomWizard::TemplateValidator.new(template)
expect(validator.perform).to eq(false)
errors = validator.errors.to_a
- expect(errors).to include(
- I18n.t("wizard.validation.not_permitted_for_guests", object_id: "action_1")
- )
expect(errors).to include(
I18n.t("wizard.validation.not_permitted_for_guests", object_id: "step_2_field_7")
)