Spiegel von
https://github.com/paviliondev/discourse-custom-wizard.git
synchronisiert 2024-11-23 01:40:29 +01:00
375 Zeilen
9,3 KiB
Ruby
375 Zeilen
9,3 KiB
Ruby
require_dependency 'wizard/step'
|
|
require_dependency 'wizard/field'
|
|
require_dependency 'wizard/step_updater'
|
|
require_dependency 'wizard/builder'
|
|
|
|
UserHistory.actions[:custom_wizard_step] = 1000
|
|
|
|
class CustomWizard::Wizard
|
|
include ActiveModel::SerializerSupport
|
|
|
|
attr_accessor :id,
|
|
:name,
|
|
:background,
|
|
:theme_id,
|
|
:save_submissions,
|
|
:multiple_submissions,
|
|
:after_time,
|
|
:after_time_scheduled,
|
|
:after_signup,
|
|
:required,
|
|
:prompt_completion,
|
|
:restart_on_revisit,
|
|
:permitted,
|
|
:needs_categories,
|
|
:needs_groups,
|
|
:steps,
|
|
:actions,
|
|
:user
|
|
|
|
def initialize(attrs = {}, user=nil)
|
|
@user = user
|
|
|
|
@id = attrs['id']
|
|
@name = attrs['name']
|
|
@background = attrs['background']
|
|
@save_submissions = attrs['save_submissions'] || false
|
|
@multiple_submissions = attrs['multiple_submissions'] || false
|
|
@prompt_completion = attrs['prompt_completion'] || false
|
|
@restart_on_revisit = attrs['restart_on_revisit'] || false
|
|
@after_signup = attrs['after_signup']
|
|
@after_time = attrs['after_time']
|
|
@after_time_scheduled = attrs['after_time_scheduled']
|
|
@required = attrs['required'] || false
|
|
@permitted = attrs['permitted'] || nil
|
|
@needs_categories = false
|
|
@needs_groups = false
|
|
@theme_id = attrs['theme_id']
|
|
|
|
if attrs['theme'].present?
|
|
theme = ::Theme.find_by(name: attrs['theme'])
|
|
@theme_id = theme.id if theme
|
|
end
|
|
|
|
@first_step = nil
|
|
@steps = []
|
|
@actions = []
|
|
end
|
|
|
|
def create_step(step_name)
|
|
::Wizard::Step.new(step_name)
|
|
end
|
|
|
|
def append_step(step)
|
|
step = create_step(step) if step.is_a?(String)
|
|
|
|
yield step if block_given?
|
|
|
|
last_step = @steps.last
|
|
|
|
@steps << step
|
|
|
|
# If it's the first step
|
|
if @steps.size == 1
|
|
@first_step = step
|
|
step.index = 0
|
|
elsif last_step.present?
|
|
last_step.next = step
|
|
step.previous = last_step
|
|
step.index = last_step.index + 1
|
|
end
|
|
end
|
|
|
|
def start
|
|
return nil if !@user
|
|
|
|
if unfinished? && last_completed_step = ::UserHistory.where(
|
|
acting_user_id: @user.id,
|
|
action: ::UserHistory.actions[:custom_wizard_step],
|
|
context: @id,
|
|
subject: @steps.map(&:id)
|
|
).order("created_at").last
|
|
|
|
step_id = last_completed_step.subject
|
|
last_index = @steps.index { |s| s.id == step_id }
|
|
@steps[last_index + 1]
|
|
else
|
|
@first_step
|
|
end
|
|
end
|
|
|
|
def create_updater(step_id, fields)
|
|
step = @steps.find { |s| s.id == step_id }
|
|
wizard = self
|
|
CustomWizard::StepUpdater.new(@user, wizard, step, fields)
|
|
end
|
|
|
|
def unfinished?
|
|
return nil if !@user
|
|
|
|
most_recent = ::UserHistory.where(
|
|
acting_user_id: @user.id,
|
|
action: ::UserHistory.actions[:custom_wizard_step],
|
|
context: @id,
|
|
).distinct.order('updated_at DESC').first
|
|
|
|
if most_recent && most_recent.subject == "reset"
|
|
false
|
|
elsif most_recent
|
|
last_finished_step = most_recent.subject
|
|
last_step = CustomWizard::Wizard.step_ids(@id).last
|
|
last_finished_step != last_step
|
|
else
|
|
true
|
|
end
|
|
end
|
|
|
|
def completed?
|
|
return nil if !@user
|
|
|
|
steps = CustomWizard::Wizard.step_ids(@id)
|
|
|
|
history = ::UserHistory.where(
|
|
acting_user_id: @user.id,
|
|
action: ::UserHistory.actions[:custom_wizard_step],
|
|
context: @id
|
|
)
|
|
|
|
if @after_time
|
|
history = history.where("updated_at > ?", @after_time_scheduled)
|
|
end
|
|
|
|
completed = history.distinct.order(:subject).pluck(:subject)
|
|
|
|
(steps - completed).empty?
|
|
end
|
|
|
|
def permitted?
|
|
return false unless user
|
|
return true if user.admin? || permitted.blank?
|
|
|
|
mapper = CustomWizard::Mapper.new(
|
|
inputs: permitted,
|
|
user: user,
|
|
opts: {
|
|
with_type: true,
|
|
multiple: true
|
|
}
|
|
).perform
|
|
|
|
return true if mapper.blank?
|
|
|
|
mapper.all? do |m|
|
|
if m[:type] === 'assignment'
|
|
GroupUser.exists?(group_id: m[:result], user_id: user.id)
|
|
elsif m[:type] === 'validation'
|
|
m[:result]
|
|
else
|
|
true
|
|
end
|
|
end
|
|
end
|
|
|
|
def can_access?
|
|
return true if user.admin
|
|
return permitted? && (multiple_submissions || !completed?)
|
|
end
|
|
|
|
def reset
|
|
::UserHistory.create(
|
|
action: ::UserHistory.actions[:custom_wizard_step],
|
|
acting_user_id: @user.id,
|
|
context: @id,
|
|
subject: "reset"
|
|
)
|
|
end
|
|
|
|
def categories
|
|
@categories ||= ::Site.new(Guardian.new(@user)).categories
|
|
end
|
|
|
|
def groups
|
|
@groups ||= ::Site.new(Guardian.new(@user)).groups
|
|
end
|
|
|
|
def submissions
|
|
Array.wrap(PluginStore.get("#{id}_submissions", @user.id))
|
|
end
|
|
|
|
def self.filter_records(filter)
|
|
PluginStoreRow.where("
|
|
plugin_name = 'custom_wizard' AND
|
|
(value::json ->> '#{filter}')::boolean IS TRUE
|
|
")
|
|
end
|
|
|
|
def self.after_signup(user)
|
|
if (records = filter_records('after_signup')).any?
|
|
result = false
|
|
|
|
records
|
|
.sort_by { |record| record.value['permitted'].present? ? 0 : 1 }
|
|
.each do |record|
|
|
wizard = self.new(JSON.parse(record.value), user)
|
|
|
|
if wizard.permitted?
|
|
result = wizard
|
|
break
|
|
end
|
|
end
|
|
|
|
result
|
|
else
|
|
false
|
|
end
|
|
end
|
|
|
|
def self.prompt_completion(user)
|
|
if (records = filter_records('prompt_completion')).any?
|
|
records.reduce([]) do |result, record|
|
|
wizard = CustomWizard::Wizard.new(::JSON.parse(record.value), user)
|
|
|
|
if wizard.permitted? && !wizard.completed?
|
|
result.push(id: wizard.id, name: wizard.name)
|
|
end
|
|
|
|
result
|
|
end
|
|
else
|
|
false
|
|
end
|
|
end
|
|
|
|
def self.restart_on_revisit
|
|
if (records = filter_records('restart_on_revisit')).any?
|
|
records.first.key
|
|
else
|
|
false
|
|
end
|
|
end
|
|
|
|
def self.steps(wizard_id)
|
|
wizard = PluginStore.get('custom_wizard', wizard_id)
|
|
wizard ? wizard['steps'] : nil
|
|
end
|
|
|
|
def self.step_ids(wizard_id)
|
|
steps = self.steps(wizard_id)
|
|
return [] if !steps
|
|
steps.map { |s| s['id'] }.flatten.uniq
|
|
end
|
|
|
|
def self.field_ids(wizard_id, step_id)
|
|
steps = self.steps(wizard_id)
|
|
return [] if !steps
|
|
step = steps.select { |s| s['id'] === step_id }.first
|
|
if step && fields = step['fields']
|
|
fields.map { |f| f['id'] }
|
|
else
|
|
[]
|
|
end
|
|
end
|
|
|
|
def self.add_wizard(obj)
|
|
wizard = obj.is_a?(String) ? ::JSON.parse(json) : obj
|
|
PluginStore.set('custom_wizard', wizard["id"], wizard)
|
|
end
|
|
|
|
def self.find(wizard_id)
|
|
PluginStore.get('custom_wizard', wizard_id)
|
|
end
|
|
|
|
def self.list(user=nil)
|
|
PluginStoreRow.where(plugin_name: 'custom_wizard').order(:id)
|
|
.reduce([]) do |result, record|
|
|
attrs = JSON.parse(record.value)
|
|
|
|
if attrs.present? &&
|
|
attrs.is_a?(Hash) &&
|
|
attrs['id'].present? &&
|
|
attrs['name'].present?
|
|
|
|
result.push(self.new(attrs, user))
|
|
end
|
|
|
|
result
|
|
end
|
|
end
|
|
|
|
def self.save(wizard)
|
|
existing_wizard = self.create(wizard[:id])
|
|
|
|
wizard[:steps].each do |step|
|
|
if step[:raw_description]
|
|
step[:description] = PrettyText.cook(step[:raw_description])
|
|
end
|
|
end
|
|
|
|
wizard = wizard.slice!(:create)
|
|
|
|
ActiveRecord::Base.transaction do
|
|
PluginStore.set('custom_wizard', wizard[:id], wizard)
|
|
|
|
if wizard[:after_time]
|
|
Jobs.cancel_scheduled_job(:set_after_time_wizard, wizard_id: wizard[:id])
|
|
enqueue_at = Time.parse(wizard[:after_time_scheduled]).utc
|
|
Jobs.enqueue_at(enqueue_at, :set_after_time_wizard, wizard_id: wizard[:id])
|
|
end
|
|
|
|
if existing_wizard && existing_wizard.after_time && !wizard[:after_time]
|
|
Jobs.cancel_scheduled_job(:set_after_time_wizard, wizard_id: wizard[:id])
|
|
Jobs.enqueue(:clear_after_time_wizard, wizard_id: wizard[:id])
|
|
end
|
|
end
|
|
|
|
wizard[:id]
|
|
end
|
|
|
|
def self.remove(wizard_id)
|
|
wizard = self.create(wizard_id)
|
|
|
|
ActiveRecord::Base.transaction do
|
|
if wizard.after_time
|
|
Jobs.cancel_scheduled_job(:set_after_time_wizard)
|
|
Jobs.enqueue(:clear_after_time_wizard, wizard_id: wizard.id)
|
|
end
|
|
|
|
PluginStore.remove('custom_wizard', wizard.id)
|
|
end
|
|
end
|
|
|
|
def self.exists?(wizard_id)
|
|
PluginStoreRow.exists?(plugin_name: 'custom_wizard', key: wizard_id)
|
|
end
|
|
|
|
def self.create(wizard_id, user = nil)
|
|
if wizard = self.find(wizard_id)
|
|
self.new(wizard.to_h, user)
|
|
else
|
|
false
|
|
end
|
|
end
|
|
|
|
def self.set_submission_redirect(user, wizard_id, url)
|
|
PluginStore.set("#{wizard_id.underscore}_submissions", user.id, [{ redirect_to: url }])
|
|
end
|
|
|
|
def self.set_wizard_redirect(wizard_id, user)
|
|
wizard = self.create(wizard_id, user)
|
|
|
|
if wizard.permitted?
|
|
user.custom_fields['redirect_to_wizard'] = wizard_id
|
|
user.save_custom_fields(true)
|
|
else
|
|
false
|
|
end
|
|
end
|
|
|
|
def self.register_styles
|
|
full_path = "#{Rails.root}/plugins/discourse-custom-wizard/assets/stylesheets/wizard/wizard_custom.scss"
|
|
DiscoursePluginRegistry.register_asset(full_path, {}, "wizard_custom")
|
|
Stylesheet::Importer.register_import("wizard_custom") do
|
|
import_files(DiscoursePluginRegistry.stylesheets["wizard_custom"])
|
|
end
|
|
end
|
|
end
|