discourse/lib/validators/form_template_yaml_validator.rb
Juan David Martínez Cubillos 72f9714ddc
FEATURE: Implement tag group selection in dropdown and multi-select for topic creation and preview when using Form Templates (#32108)
Adds support for a tag-chooser in form templates. It supports single tag
and multi tags. The source of the displayed tags has to be a tag_group
name.

---------

Co-authored-by: Joffrey JAFFEUX <j.jaffeux@gmail.com>
2025-04-17 08:38:03 -05:00

84 lines
2.4 KiB
Ruby

# frozen_string_literal: true
class FormTemplateYamlValidator < ActiveModel::Validator
RESERVED_KEYWORDS = %w[title body category category_id tags]
ALLOWED_TYPES = %w[checkbox dropdown input multi-select textarea upload tag-chooser]
HTML_SANITIZATION_OPTIONS = {
elements: ["a"],
attributes: {
"a" => %w[href target],
},
protocols: {
"a" => {
"href" => %w[http https mailto],
},
},
}
def validate(record)
begin
yaml = Psych.safe_load(record.template)
unless yaml.is_a?(Array)
record.errors.add(:template, I18n.t("form_templates.errors.invalid_yaml"))
return
end
existing_ids = []
yaml.each do |field|
check_missing_fields(record, field)
check_allowed_types(record, field)
check_ids(record, field, existing_ids)
check_descriptions_html(record, field)
end
rescue Psych::SyntaxError
record.errors.add(:template, I18n.t("form_templates.errors.invalid_yaml"))
end
end
def check_allowed_types(record, field)
if !ALLOWED_TYPES.include?(field["type"])
record.errors.add(
:template,
I18n.t(
"form_templates.errors.invalid_type",
type: field["type"],
valid_types: ALLOWED_TYPES.join(", "),
),
)
end
end
def check_missing_fields(record, field)
if field["type"].blank?
record.errors.add(:template, I18n.t("form_templates.errors.missing_type"))
end
record.errors.add(:template, I18n.t("form_templates.errors.missing_id")) if field["id"].blank?
end
def check_descriptions_html(record, field)
description = field.dig("attributes", "description")
return if description.blank?
sanitized_html = Sanitize.fragment(description, HTML_SANITIZATION_OPTIONS)
is_safe_html = sanitized_html == Loofah.html5_fragment(description).to_s
unless is_safe_html
record.errors.add(:template, I18n.t("form_templates.errors.unsafe_description"))
end
end
def check_ids(record, field, existing_ids)
if RESERVED_KEYWORDS.include?(field["id"])
record.errors.add(:template, I18n.t("form_templates.errors.reserved_id", id: field["id"]))
end
if existing_ids.include?(field["id"])
record.errors.add(:template, I18n.t("form_templates.errors.duplicate_ids"))
end
existing_ids << field["id"] if field["id"].present?
end
end