Spiegel von
https://github.com/paviliondev/discourse-custom-wizard.git
synchronisiert 2024-11-24 10:20:28 +01:00
269 Zeilen
6,1 KiB
Ruby
269 Zeilen
6,1 KiB
Ruby
# frozen_string_literal: true
|
|
class CustomWizard::Mapper
|
|
attr_accessor :inputs, :data, :user
|
|
|
|
USER_FIELDS = %w[name username date_of_birth title locale trust_level email]
|
|
|
|
USER_OPTION_FIELDS = %w[email_level email_messages_level email_digests]
|
|
|
|
PROFILE_FIELDS = %w[location website bio_raw]
|
|
|
|
def self.user_fields
|
|
USER_FIELDS + USER_OPTION_FIELDS + PROFILE_FIELDS
|
|
end
|
|
|
|
OPERATORS = {
|
|
equal: "==",
|
|
not_equal: "!=",
|
|
greater: ">",
|
|
less: "<",
|
|
greater_or_equal: ">=",
|
|
less_or_equal: "<=",
|
|
regex: "=~",
|
|
is: {
|
|
present: "present?",
|
|
true: "==",
|
|
false: "==",
|
|
},
|
|
}
|
|
|
|
def initialize(params)
|
|
@inputs = params[:inputs] || {}
|
|
@data = params[:data] ? params[:data].with_indifferent_access : {}
|
|
@user = params[:user]
|
|
@opts = params[:opts] || {}
|
|
end
|
|
|
|
def perform
|
|
multiple = @opts[:multiple]
|
|
perform_result = multiple ? [] : nil
|
|
|
|
inputs.each do |input|
|
|
input_type = input["type"]
|
|
pairs = input["pairs"]
|
|
|
|
if (input_type === "conditional" && validate_pairs(pairs)) || input_type === "assignment"
|
|
output = input["output"]
|
|
output_type = input["output_type"]
|
|
|
|
result = build_result(map_field(output, output_type), input_type)
|
|
|
|
if multiple
|
|
perform_result.push(result)
|
|
else
|
|
perform_result = result
|
|
break
|
|
end
|
|
end
|
|
|
|
if input_type === "validation"
|
|
result = build_result(validate_pairs(pairs), input_type)
|
|
|
|
if multiple
|
|
perform_result.push(result)
|
|
else
|
|
perform_result = result
|
|
break
|
|
end
|
|
end
|
|
|
|
if input_type === "association"
|
|
result = build_result(map_pairs(pairs), input_type)
|
|
|
|
if multiple
|
|
perform_result.push(result)
|
|
else
|
|
perform_result = result
|
|
break
|
|
end
|
|
end
|
|
end
|
|
|
|
perform_result
|
|
end
|
|
|
|
def build_result(result, type)
|
|
if @opts[:with_type]
|
|
{ type: type, result: result }
|
|
else
|
|
result
|
|
end
|
|
end
|
|
|
|
def validate_pairs(pairs)
|
|
pairs.all? do |pair|
|
|
connector = pair["connector"]
|
|
operator = map_operator(connector)
|
|
key = map_field(pair["key"], pair["key_type"])
|
|
value = cast_value(map_field(pair["value"], pair["value_type"]), key, connector)
|
|
begin
|
|
validation_result(key, value, operator)
|
|
rescue NoMethodError
|
|
#
|
|
end
|
|
end
|
|
end
|
|
|
|
def cast_value(value, key, connector)
|
|
if connector == "regex"
|
|
Regexp.new(value)
|
|
else
|
|
if key.is_a?(String)
|
|
value.to_s
|
|
elsif key.is_a?(Integer)
|
|
value.to_i
|
|
else
|
|
value
|
|
end
|
|
end
|
|
end
|
|
|
|
def validation_result(key, value, operator)
|
|
result = nil
|
|
|
|
if operator.is_a?(Hash) && (operator = operator[value.to_sym]).present?
|
|
if value == "present"
|
|
result = key.public_send(operator)
|
|
elsif %w[true false].include?(value)
|
|
result = bool(key).public_send(operator, bool(value))
|
|
end
|
|
elsif [key, value, operator].all? { |i| !i.nil? }
|
|
result = key.public_send(operator, value)
|
|
else
|
|
result = false
|
|
end
|
|
|
|
if operator == "=~"
|
|
result.nil? ? false : true
|
|
else
|
|
result
|
|
end
|
|
end
|
|
|
|
def map_pairs(pairs)
|
|
result = []
|
|
|
|
pairs.each do |pair|
|
|
key = map_field(pair["key"], pair["key_type"])
|
|
value = map_field(pair["value"], pair["value_type"])
|
|
|
|
result.push(key: key, value: value) if key && value
|
|
end
|
|
|
|
result
|
|
end
|
|
|
|
def map_operator(connector)
|
|
OPERATORS[connector.to_sym] || "=="
|
|
end
|
|
|
|
def map_field(value, type)
|
|
method = "map_#{type}"
|
|
|
|
if self.respond_to?(method)
|
|
self.send(method, value)
|
|
else
|
|
value
|
|
end
|
|
end
|
|
|
|
def map_text(value)
|
|
interpolate(value)
|
|
end
|
|
|
|
def map_wizard_field(value)
|
|
data && !data.key?("submitted_at") && data[value]
|
|
end
|
|
|
|
def map_wizard_action(value)
|
|
data && !data.key?("submitted_at") && data[value]
|
|
end
|
|
|
|
def map_user_field(value)
|
|
return nil unless user
|
|
|
|
if value.include?(User::USER_FIELD_PREFIX)
|
|
user.custom_fields[value]
|
|
elsif PROFILE_FIELDS.include?(value)
|
|
user.user_profile.send(value)
|
|
elsif USER_FIELDS.include?(value)
|
|
user.send(value)
|
|
elsif USER_OPTION_FIELDS.include?(value)
|
|
user.user_option.send(value)
|
|
elsif value.include?("avatar")
|
|
get_avatar_url(value)
|
|
else
|
|
nil
|
|
end
|
|
end
|
|
|
|
def map_user_field_options(value)
|
|
if value.include?(User::USER_FIELD_PREFIX)
|
|
if field = UserField.find_by(id: value.split("_").last)
|
|
field.user_field_options.map(&:value)
|
|
end
|
|
end
|
|
end
|
|
|
|
def interpolate(string, opts = { user: true, wizard: true, value: true, template: false })
|
|
return string if string.blank? || string.frozen?
|
|
|
|
string.gsub!(/u\{(.*?)\}/) { |match| map_user_field($1) || "" } if opts[:user] && @user.present?
|
|
|
|
string.gsub!(/w\{(.*?)\}/) { |match| recurse(data, [*$1.split(".")]) || "" } if opts[:wizard]
|
|
|
|
if opts[:value]
|
|
string.gsub!(/v\{(.*?)\}/) do |match|
|
|
attrs = $1.split(":")
|
|
key = attrs.first
|
|
format = attrs.last if attrs.length > 1
|
|
result = ""
|
|
|
|
if key == "time"
|
|
time_format = format.present? ? format : "%B %-d, %Y"
|
|
result = Time.now.strftime(time_format)
|
|
end
|
|
|
|
result
|
|
end
|
|
end
|
|
|
|
if opts[:template] #&& CustomWizard::Subscription.subscribed?
|
|
template = Liquid::Template.parse(string)
|
|
string = template.render(data)
|
|
end
|
|
|
|
string
|
|
end
|
|
|
|
def recurse(data, keys)
|
|
return nil if data.nil?
|
|
k = keys.shift
|
|
result = data[k]
|
|
|
|
if keys.empty?
|
|
result.is_a?(Hash) ? "" : result
|
|
else
|
|
self.recurse(result, keys)
|
|
end
|
|
end
|
|
|
|
def bool(value)
|
|
ActiveRecord::Type::Boolean.new.cast(value)
|
|
end
|
|
|
|
def get_avatar_url(value)
|
|
parts = value.split(".")
|
|
valid_sizes = Discourse.avatar_sizes.to_a
|
|
|
|
if value === "avatar" || parts.size === 1 || valid_sizes.exclude?(parts.last.to_i)
|
|
user.small_avatar_url
|
|
else
|
|
user.avatar_template_url.gsub("{size}", parts.last)
|
|
end
|
|
end
|
|
|
|
def self.mapped_value?(value)
|
|
value.is_a?(Array) && value.all? { |v| v.is_a?(Hash) && v.key?("type") }
|
|
end
|
|
end
|