# frozen_string_literal: true require "discourse_subscription_client" class CustomWizard::Subscription PRODUCT_HIERARCHY = %w[community standard business] def self.attributes { wizard: { required: { none: [], standard: ["*"], business: ["*"], community: ["*"], }, permitted: { none: [], standard: ["*"], business: ["*"], community: ["*", "!#{CustomWizard::Wizard::GUEST_GROUP_ID}"], }, restart_on_revisit: { none: [], standard: ["*"], business: ["*"], community: ["*"], }, after_time_groups: { none: [], standard: [], business: ["*"], community: [], }, }, step: { condition: { none: [], standard: ["*"], business: ["*"], community: ["*"], }, required_data: { none: [], standard: ["*"], business: ["*"], community: ["*"], }, permitted_params: { none: [], standard: ["*"], business: ["*"], community: ["*"], }, }, field: { condition: { none: [], standard: ["*"], business: ["*"], community: ["*"], }, type: { none: %w[text textarea text_only date time date_time number checkbox dropdown upload], standard: ["*"], business: ["*"], community: ["*"], }, realtime_validations: { none: [], standard: ["*"], business: ["*"], community: ["*"], }, }, action: { type: { none: %w[create_topic update_profile open_composer route_to], standard: %w[ create_topic update_profile open_composer route_to send_message watch_categories watch_tags add_to_group ], business: ["*"], community: ["*"], }, }, custom_field: { klass: { none: %w[topic post], standard: %w[topic post], business: ["*"], community: ["*"], }, type: { none: %w[string boolean integer], standard: %w[string boolean integer], business: ["*"], community: ["*"], }, }, api: { all: { none: [], standard: [], business: ["*"], community: ["*"], }, }, } end attr_accessor :product_id, :product_slug def initialize(update = false) ::DiscourseSubscriptionClient::Subscriptions.update if update result = ::DiscourseSubscriptionClient.find_subscriptions("discourse-custom-wizard") if result&.any? ids_and_slugs = result.subscriptions.map do |subscription| { id: subscription.product_id, slug: result.products[subscription.product_id] } end id_and_slug = ids_and_slugs .sort { |a, b| PRODUCT_HIERARCHY.index(b[:slug]) - PRODUCT_HIERARCHY.index(a[:slug]) } .first @product_id = id_and_slug[:id] @product_slug = id_and_slug[:slug] end @product_slug ||= ENV["CUSTOM_WIZARD_PRODUCT_SLUG"] end def includes?(feature, attribute, value = nil) attributes = self.class.attributes[feature] ## Attribute is not part of a subscription return true unless attributes.present? && attributes.key?(attribute) values = attributes[attribute][type] ## Subscription type does not support the attribute. return false if values.blank? ## Value is an exception for the subscription type if (exceptions = get_exceptions(values)).any? value = mapped_output(value) if CustomWizard::Mapper.mapped_value?(value) value = [*value].map(&:to_s) return false if (exceptions & value).length > 0 end ## Subscription type supports all values of the attribute. return true if values.include?("*") ## Subscription type supports some values of the attributes. values.include?(value) end def type return :none unless subscribed? return :business if business? return :standard if standard? :community if community? end def subscribed? standard? || business? || community? end def standard? product_slug === "standard" end def business? product_slug === "business" end def community? product_slug === "community" end # TODO candidate for removal once code that depends on it externally is no longer used. def self.client_installed? defined?(DiscourseSubscriptionClient) == "constant" && DiscourseSubscriptionClient.class == Module end def self.subscribed? new.subscribed? end def self.business? new.business? end def self.community? new.community? end def self.standard? new.standard? end def self.type new.type end def self.includes?(feature, attribute, value) new.includes?(feature, attribute, value) end protected def get_exceptions(values) values.reduce([]) do |result, value| result << value.split("!").last if value.start_with?("!") result end end def mapped_output(value) value .reduce([]) do |result, v| ## We can only validate mapped assignment values at the moment result << v["output"] if v.is_a?(Hash) && v["type"] === "assignment" result end .flatten end end