0
0
Fork 1
Spiegel von https://github.com/paviliondev/discourse-custom-wizard.git synchronisiert 2024-10-18 11:52:39 +02:00

Merge pull request #296 from paviliondev/add_after_time_targets

FEATURE: Add after time groups
Dieser Commit ist enthalten in:
Angus McLeod 2024-10-17 16:21:44 +02:00 committet von GitHub
Commit 10c30a0c74
Es konnte kein GPG-Schlüssel zu dieser Signatur gefunden werden
GPG-Schlüssel-ID: B5690EEEBB952194
21 geänderte Dateien mit 259 neuen und 15 gelöschten Zeilen

Datei anzeigen

@ -0,0 +1,17 @@
# frozen_string_literal: true
class CustomWizard::UserController < ::Admin::AdminController
before_action :ensure_admin
requires_plugin "discourse-custom-wizard"
def clear_redirect
user = User.find_by(id: params[:id])
if user
user.custom_fields["redirect_to_wizard"] = nil
user.save_custom_fields(true)
render json: success_json
else
render json: failed_json
end
end
end

Datei anzeigen

@ -78,6 +78,7 @@ class CustomWizard::AdminWizardController < CustomWizard::AdminController
:resume_on_revisit, :resume_on_revisit,
:theme_id, :theme_id,
permitted: mapped_params, permitted: mapped_params,
after_time_groups: [],
steps: [ steps: [
:id, :id,
:index, :index,

Datei anzeigen

@ -3,20 +3,32 @@ module Jobs
class SetAfterTimeWizard < ::Jobs::Base class SetAfterTimeWizard < ::Jobs::Base
def execute(args) def execute(args)
if SiteSetting.custom_wizard_enabled if SiteSetting.custom_wizard_enabled
wizard = CustomWizard::Wizard.create(args[:wizard_id]) @wizard = CustomWizard::Wizard.create(args[:wizard_id])
if wizard && wizard.after_time if @wizard && @wizard.after_time
user_ids = [] user_ids = []
User.human_users.each do |user| target_users.each do |user|
user_ids.push(user.id) if CustomWizard::Wizard.set_user_redirect(wizard.id, user) user_ids.push(user.id) if CustomWizard::Wizard.set_after_time_redirect(@wizard.id, user)
end end
CustomWizard::Template.clear_cache_keys CustomWizard::Template.clear_cache_keys
MessageBus.publish "/redirect_to_wizard", wizard.id, user_ids: user_ids MessageBus.publish "/redirect_to_wizard", @wizard.id, user_ids: user_ids
end end
end end
end end
def target_users
users = []
if @wizard.after_time_groups.exists?
@wizard.after_time_groups.each { |group| users += group.users }
else
users = User.human_users
end
users
end
end end
end end

Datei anzeigen

@ -0,0 +1,28 @@
<section class="details">
<h1>{{i18n "admin.wizard.user.label"}}</h1>
<div class="display-row">
<div class="field">{{i18n "admin.wizard.user.redirect.label"}}</div>
<div class="value">
{{#if model.redirect_to_wizard}}
<LinkTo
@route="adminWizardsWizardShow"
@model={{dasherize model.redirect_to_wizard}}
>
{{model.redirect_to_wizard}}
</LinkTo>
{{else}}
&mdash;
{{/if}}
</div>
<div class="controls">
{{#if model.redirect_to_wizard}}
<DButton
@action={{fn model.clearWizardRedirect model}}
@label="admin.wizard.user.redirect.remove_label"
@title="admin.wizard.user.redirect.remove_title"
/>
{{/if}}
</div>
</div>
</section>

Datei anzeigen

@ -12,9 +12,11 @@ import Controller from "@ember/controller";
import copyText from "discourse/lib/copy-text"; import copyText from "discourse/lib/copy-text";
import I18n from "I18n"; import I18n from "I18n";
import { filterValues } from "discourse/plugins/discourse-custom-wizard/discourse/lib/wizard-schema"; import { filterValues } from "discourse/plugins/discourse-custom-wizard/discourse/lib/wizard-schema";
import { action } from "@ember/object";
export default Controller.extend({ export default Controller.extend({
modal: service(), modal: service(),
site: service(),
hasName: notEmpty("wizard.name"), hasName: notEmpty("wizard.name"),
@observes("currentStep") @observes("currentStep")
@ -91,6 +93,24 @@ export default Controller.extend({
return I18n.t(`admin.wizard.error.${errorType}`, errorParams); return I18n.t(`admin.wizard.error.${errorType}`, errorParams);
}, },
setAfterTimeGroupIds() {
const groups = this.site.groups.filter((g) =>
this.wizard.after_time_groups.includes(g.name)
);
this.setProperties({
afterTimeGroupIds: groups.map((g) => g.id),
});
},
@action
setAfterTimeGroups(groupIds) {
const groups = this.site.groups.filter((g) => groupIds.includes(g.id));
this.setProperties({
afterTimeGroupIds: groups.map((g) => g.id),
"wizard.after_time_groups": groups.map((g) => g.name),
});
},
actions: { actions: {
save() { save() {
this.setProperties({ this.setProperties({

Datei anzeigen

@ -2,6 +2,8 @@ import DiscourseURL from "discourse/lib/url";
import { withPluginApi } from "discourse/lib/plugin-api"; import { withPluginApi } from "discourse/lib/plugin-api";
import getUrl from "discourse-common/lib/get-url"; import getUrl from "discourse-common/lib/get-url";
import { observes } from "discourse-common/utils/decorators"; import { observes } from "discourse-common/utils/decorators";
import { popupAjaxError } from "discourse/lib/ajax-error";
import { ajax } from "discourse/lib/ajax";
export default { export default {
name: "custom-wizard-edits", name: "custom-wizard-edits",
@ -105,6 +107,24 @@ export default {
route: "adminWizardsWizard", route: "adminWizardsWizard",
icon: "hat-wizard", icon: "hat-wizard",
}); });
if (api.getCurrentUser()?.admin) {
api.modifyClass("model:admin-user", {
pluginId: "custom-wizard",
clearWizardRedirect(user) {
return ajax(`/admin/users/${user.id}/wizards/clear_redirect`, {
type: "PUT",
})
.then(() => {
user.setProperties({
redirect_to_wizard: null,
});
})
.catch(popupAjaxError);
},
});
}
}); });
}, },
}; };

Datei anzeigen

@ -17,11 +17,13 @@ const wizard = {
resume_on_revisit: null, resume_on_revisit: null,
theme_id: null, theme_id: null,
permitted: null, permitted: null,
after_time_groups: null,
}, },
mapped: ["permitted"], mapped: ["permitted"],
required: ["id"], required: ["id"],
dependent: { dependent: {
after_time: "after_time_scheduled", after_time: "after_time_scheduled",
after_time: "after_time_groups",
}, },
objectArrays: { objectArrays: {
step: { step: {

Datei anzeigen

@ -47,5 +47,6 @@ export default DiscourseRoute.extend({
}; };
controller.setProperties(props); controller.setProperties(props);
controller.setAfterTimeGroupIds();
}, },
}); });

Datei anzeigen

@ -162,6 +162,22 @@
}} }}
</div> </div>
</div> </div>
<div class="setting full">
<div class="setting-label">
<label>{{i18n "admin.wizard.after_time_groups.label"}}</label>
</div>
<div class="setting-value">
<GroupChooser
@content={{this.site.groups}}
@value={{this.afterTimeGroupIds}}
@onChange={{this.setAfterTimeGroups}}
/>
<div class="setting-gutter">
{{i18n "admin.wizard.after_time_groups.description"}}
</div>
</div>
</div>
{{/wizard-subscription-container}} {{/wizard-subscription-container}}
</div> </div>

Datei anzeigen

@ -373,11 +373,16 @@ $error: #ef1700;
} }
.input .select-kit, .input .select-kit,
> .select-kit { > .select-kit:not(.group-chooser) {
max-width: 250px !important; max-width: 250px !important;
min-width: 250px !important; min-width: 250px !important;
} }
.group-chooser {
max-width: 400px !important;
min-width: 400px !important;
}
&.force-final { &.force-final {
padding: 1em; padding: 1em;
background-color: var(--primary-very-low); background-color: var(--primary-very-low);

Datei anzeigen

@ -97,6 +97,9 @@ en:
time: "Time" time: "Time"
done: "Set Time" done: "Set Time"
clear: "Clear" clear: "Clear"
after_time_groups:
label: Time Groups
description: Groups directed to wizard after start time.
required: "Required" required: "Required"
required_label: "Users cannot skip the wizard." required_label: "Users cannot skip the wizard."
prompt_completion: "Prompt" prompt_completion: "Prompt"
@ -587,6 +590,12 @@ en:
community: community:
label: Support label: Support
title: There is a Custom Wizard Community subscription active on this forum. title: There is a Custom Wizard Community subscription active on this forum.
user:
label: Wizards
redirect:
label: Redirect
remove_label: Remove
remove_title: Remove wizard redirect
wizard_js: wizard_js:
group: group:

Datei anzeigen

@ -52,6 +52,7 @@ en:
after_signup: "You can only have one 'after signup' wizard at a time. %{wizard_id} has 'after signup' enabled." after_signup: "You can only have one 'after signup' wizard at a time. %{wizard_id} has 'after signup' enabled."
after_signup_after_time: "You can't use 'after time' and 'after signup' on the same wizard." after_signup_after_time: "You can't use 'after time' and 'after signup' on the same wizard."
after_time: "After time setting is invalid." after_time: "After time setting is invalid."
after_time_group: "After time group does not exist: %{group_name}"
liquid_syntax_error: "Liquid syntax error in %{attribute}: %{message}" liquid_syntax_error: "Liquid syntax error in %{attribute}: %{message}"
subscription: "%{type} %{property} usage is not supported on your subscription" subscription: "%{type} %{property} usage is not supported on your subscription"
not_permitted_for_guests: "%{object_id} is not permitted when guests can access the wizard" not_permitted_for_guests: "%{object_id} is not permitted when guests can access the wizard"

Datei anzeigen

@ -46,4 +46,6 @@ Discourse::Application.routes.append do
post "admin/wizards/manager/import" => "admin_manager#import" post "admin/wizards/manager/import" => "admin_manager#import"
delete "admin/wizards/manager/destroy" => "admin_manager#destroy" delete "admin/wizards/manager/destroy" => "admin_manager#destroy"
end end
put "/admin/users/:id/wizards/clear_redirect" => "custom_wizard/user#clear_redirect"
end end

Datei anzeigen

@ -25,6 +25,12 @@ class CustomWizard::Subscription
business: ["*"], business: ["*"],
community: ["*"], community: ["*"],
}, },
after_time_groups: {
none: [],
standard: [],
business: ["*"],
community: [],
},
}, },
step: { step: {
condition: { condition: {

Datei anzeigen

@ -176,10 +176,10 @@ class CustomWizard::Template
end end
if enqueue_wizard_at if enqueue_wizard_at
Jobs.cancel_scheduled_job(:set_after_time_wizard, wizard_id: wizard_id) self.class.clear_user_wizard_redirect(wizard_id, after_time: true)
Jobs.enqueue_at(enqueue_wizard_at, :set_after_time_wizard, wizard_id: wizard_id) Jobs.enqueue_at(enqueue_wizard_at, :set_after_time_wizard, wizard_id: wizard_id)
elsif old_data && old_data[:after_time] elsif old_data && old_data[:after_time]
clear_user_wizard_redirect(wizard_id, after_time: true) self.class.clear_user_wizard_redirect(wizard_id, after_time: true)
end end
end end
end end

Datei anzeigen

@ -128,6 +128,15 @@ class CustomWizard::TemplateValidator
if invalid_time || active_time.blank? || active_time < Time.now.utc if invalid_time || active_time.blank? || active_time < Time.now.utc
errors.add :base, I18n.t("wizard.validation.after_time") errors.add :base, I18n.t("wizard.validation.after_time")
end end
group_names = @data[:after_time_groups]
if group_names.present?
group_names.each do |group_name|
unless Group.exists?(name: group_name)
errors.add :base, I18n.t("wizard.validation.after_time_group", group_name: group_name)
end
end
end
end end
def validate_liquid_template(object, type) def validate_liquid_template(object, type)

Datei anzeigen

@ -15,6 +15,7 @@ class CustomWizard::Wizard
:multiple_submissions, :multiple_submissions,
:after_time, :after_time,
:after_time_scheduled, :after_time_scheduled,
:after_time_group_names,
:after_signup, :after_signup,
:required, :required,
:prompt_completion, :prompt_completion,
@ -58,6 +59,7 @@ class CustomWizard::Wizard
@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"]
@after_time_group_names = attrs["after_time_groups"]
@required = cast_bool(attrs["required"]) @required = cast_bool(attrs["required"])
@permitted = attrs["permitted"] || nil @permitted = attrs["permitted"] || nil
@theme_id = attrs["theme_id"] @theme_id = attrs["theme_id"]
@ -251,6 +253,10 @@ class CustomWizard::Wizard
permitted?(always_allow_admin: always_allow_admin) && can_submit? permitted?(always_allow_admin: always_allow_admin) && can_submit?
end end
def should_redirect?
can_access?(always_allow_admin: false) && after_time_target?
end
def reset def reset
return nil unless actor_id return nil unless actor_id
@ -329,6 +335,16 @@ class CustomWizard::Wizard
end end
end end
def after_time_groups
@after_time_groups ||= Group.where(name: after_time_group_names)
end
def after_time_target?
return true if after_time_group_names.blank? || !after_time_groups.exists?
return true if after_time_groups.joins(:users).where(users: { username: user.username }).exists?
false
end
def self.create(wizard_id, user = nil, guest_id = nil) def self.create(wizard_id, user = nil, guest_id = nil)
if template = CustomWizard::Template.find(wizard_id) if template = CustomWizard::Template.find(wizard_id)
new(template.to_h, user, guest_id) new(template.to_h, user, guest_id)
@ -372,6 +388,11 @@ class CustomWizard::Wizard
end end
end end
def self.set_after_time_redirect(wizard_id, user)
wizard = self.create(wizard_id, user)
set_user_redirect(wizard_id, user) if wizard.after_time_target?
end
def self.set_user_redirect(wizard_id, user) def self.set_user_redirect(wizard_id, user)
wizard = self.create(wizard_id, user) wizard = self.create(wizard_id, user)

Datei anzeigen

@ -1,7 +1,7 @@
# frozen_string_literal: true # frozen_string_literal: true
# name: discourse-custom-wizard # name: discourse-custom-wizard
# about: Forms for Discourse. Better onboarding, structured posting, data enrichment, automated actions and much more. # about: Forms for Discourse. Better onboarding, structured posting, data enrichment, automated actions and much more.
# version: 2.8.13 # version: 2.9.0
# authors: Angus McLeod, Faizaan Gagan, Robert Barrow, Keegan George, Kaitlin Maddever, Juan Marcos Gutierrez Ramos # authors: Angus McLeod, Faizaan Gagan, Robert Barrow, Keegan George, Kaitlin Maddever, Juan Marcos Gutierrez Ramos
# url: https://github.com/paviliondev/discourse-custom-wizard # url: https://github.com/paviliondev/discourse-custom-wizard
# contact_emails: development@pavilion.tech # contact_emails: development@pavilion.tech
@ -46,6 +46,7 @@ after_initialize do
require_relative "app/controllers/custom_wizard/admin/logs.rb" require_relative "app/controllers/custom_wizard/admin/logs.rb"
require_relative "app/controllers/custom_wizard/admin/manager.rb" require_relative "app/controllers/custom_wizard/admin/manager.rb"
require_relative "app/controllers/custom_wizard/admin/custom_fields.rb" require_relative "app/controllers/custom_wizard/admin/custom_fields.rb"
require_relative "app/controllers/custom_wizard/admin/user.rb"
require_relative "app/controllers/custom_wizard/wizard_client.rb" require_relative "app/controllers/custom_wizard/wizard_client.rb"
require_relative "app/controllers/custom_wizard/wizard.rb" require_relative "app/controllers/custom_wizard/wizard.rb"
require_relative "app/controllers/custom_wizard/steps.rb" require_relative "app/controllers/custom_wizard/steps.rb"
@ -146,6 +147,7 @@ after_initialize do
end end
add_to_serializer(:current_user, :redirect_to_wizard) { object.redirect_to_wizard } add_to_serializer(:current_user, :redirect_to_wizard) { object.redirect_to_wizard }
add_to_serializer(:admin_user_list, :redirect_to_wizard) { object.redirect_to_wizard }
on(:user_approved) do |user| on(:user_approved) do |user|
if wizard = CustomWizard::Wizard.after_signup(user) if wizard = CustomWizard::Wizard.after_signup(user)
@ -168,7 +170,7 @@ after_initialize do
end end
wizard = CustomWizard::Wizard.create(wizard_id, current_user) wizard = CustomWizard::Wizard.create(wizard_id, current_user)
redirect_to "/w/#{wizard_id.dasherize}" if wizard.can_access?(always_allow_admin: false) redirect_to "/w/#{wizard_id.dasherize}" if wizard.should_redirect?
end end
end end
end end

Datei anzeigen

@ -154,6 +154,9 @@ describe CustomWizard::Template do
expect_not_enqueued_with(job: :set_after_time_wizard) do expect_not_enqueued_with(job: :set_after_time_wizard) do
CustomWizard::Template.save(@after_time_template) CustomWizard::Template.save(@after_time_template)
end end
expect(
UserCustomField.exists?(name: "redirect_to_wizard", value: @after_time_template[:id]),
).to eq(false)
end end
end end
end end

Datei anzeigen

@ -47,6 +47,29 @@ describe Jobs::SetAfterTimeWizard do
end end
end end
context "when after_time_groups is set" do
fab!(:group1) { Fabricate(:group) }
fab!(:group_user) { Fabricate(:group_user, group: group1, user: user2) }
before do
enable_subscription("business")
@after_time_template["after_time_groups"] = [group1.name]
CustomWizard::Template.save(@after_time_template.as_json)
end
it "only redirects users in the group" do
messages =
MessageBus.track_publish("/redirect_to_wizard") do
described_class.new.execute(wizard_id: "super_mega_fun_wizard")
end
expect(messages.first.data).to eq("super_mega_fun_wizard")
expect(messages.first.user_ids).to match_array([user2.id])
expect(
UserCustomField.where(name: "redirect_to_wizard", value: "super_mega_fun_wizard").length,
).to eq(1)
end
end
context "when user has completed the wizard" do context "when user has completed the wizard" do
before do before do
@after_time_template[:steps].each do |step| @after_time_template[:steps].each do |step|

Datei anzeigen

@ -78,8 +78,16 @@ describe ApplicationController do
end end
context "when time has passed" do context "when time has passed" do
it "redirects if time has passed" do def run_job!
travel_to Time.now + 4.hours travel_to Time.now + 4.hours
MessageBus.expects(:publish).at_least_once
Jobs::SetAfterTimeWizard.new.execute(
Jobs::SetAfterTimeWizard.jobs.first["args"].first.symbolize_keys,
)
end
it "redirects if time has passed" do
run_job!
get "/" get "/"
expect(response).to redirect_to("/w/super-mega-fun-wizard") expect(response).to redirect_to("/w/super-mega-fun-wizard")
end end
@ -93,7 +101,7 @@ describe ApplicationController do
context "when user is in permitted group" do context "when user is in permitted group" do
it "redirects user" do it "redirects user" do
travel_to Time.now + 4.hours run_job!
get "/" get "/"
expect(response).to redirect_to("/w/super-mega-fun-wizard") expect(response).to redirect_to("/w/super-mega-fun-wizard")
end end
@ -103,7 +111,7 @@ describe ApplicationController do
before { Group.find(13).remove(user) } before { Group.find(13).remove(user) }
it "does not redirect user" do it "does not redirect user" do
travel_to Time.now + 4.hours run_job!
user.trust_level = TrustLevel[2] user.trust_level = TrustLevel[2]
user.save! user.save!
get "/" get "/"
@ -111,7 +119,7 @@ describe ApplicationController do
end end
it "does not redirect if user is an admin" do it "does not redirect if user is an admin" do
travel_to Time.now + 4.hours run_job!
user.trust_level = TrustLevel[2] user.trust_level = TrustLevel[2]
user.admin = true user.admin = true
user.save! user.save!
@ -134,11 +142,49 @@ describe ApplicationController do
end end
it "does not redirect" do it "does not redirect" do
travel_to Time.now + 4.hours run_job!
get "/" get "/"
expect(response).not_to redirect_to("/w/super-mega-fun-wizard") expect(response).not_to redirect_to("/w/super-mega-fun-wizard")
end end
end end
context "when after_time_groups is set" do
fab!(:group)
before do
enable_subscription("business")
@template["after_time_groups"] = [group.name]
CustomWizard::Template.save(@template.as_json)
end
context "when user is in group" do
before { group.add(user) }
it "redirects user" do
run_job!
get "/"
expect(response).to redirect_to("/w/super-mega-fun-wizard")
end
end
context "when user is not in group" do
before { group.remove(user) }
it "does not redirect user" do
run_job!
get "/"
expect(response).to_not redirect_to("/w/super-mega-fun-wizard")
end
it "does not redirect if user is an admin" do
run_job!
user.admin = true
user.save!
get "/"
expect(response).to_not redirect_to("/w/super-mega-fun-wizard")
end
end
end
end end
end end
end end