app/models/effective/form_inputs/article_editor.rb



# frozen_string_literal: true

module Effective
  module FormInputs
    class ArticleEditor < Effective::FormInput

      # https://imperavi.com/article/docs/settings/
      def self.defaults
        {
           # This is overridden by the active_storage() method below
          active_storage: nil,

          # This is overridden by the css() method below
          css: ['/assets/article_editor/arx-frame.min.css'],

          # This is overridden by the custom_css() method below
          custom: {
            css: ['application.css', '/assets/effective_bootstrap_article_editor.css']
          },

          # The rest of these are just normal Hash options
          align: {
            'left': 'text-left',
            'center': 'text-center',
            'right': 'text-right',
            'justify': false
          },
          cellcolors: {
            'primary': { title: 'Primary', classname: 'table-primary' },
            'secondary': { title: 'Secondary', classname: 'table-secondary' },
            'active': { title: 'Active', classname: 'table-active' },
            'success': { title: 'Success', classname: 'table-success' },
            'danger': { title: 'Danger', classname: 'table-danger' },
            'warning': { title: 'Warning', classname: 'table-warning' },
            'info': { title: 'Info', classname: 'table-info' },
            'light': { title: 'Light', classname: 'table-light' },
            'dark': { title: 'Dark', classname: 'table-dark' }
          },
          classes: { table: 'table' },
          editor: {
            csscache: true,  # Do not add ?=timestamp to css requests
            https: true      # Embed links use https
          },
          embed: { script: false }, # do not strip out script tag from embeds
          filelink: nil,
          format: ['p', 'h2', 'h3', 'h4', 'h5', 'ul', 'ol'],
          grid: {
            classname: 'row',
            columns: 12,
            gutter: '1px',
            offset: {
              left: '15px',
              right: '15px',
            },
            patterns: {
              '6|6': 'col-md-6|col-md-6',
              '4|4|4': 'col-md-4|col-md-4|col-md-4',
              '3|3|3|3': 'col-md-3|col-md-3|col-md-3|col-md-3',
              '2|2|2|2|2|2': 'col-md-2|col-md-2|col-md-2|col-md-2|col-md-2|col-md-2',
              '3|6|3': 'col-md-3|col-md-6|col-md-3',
              '2|8|2': 'col-md-2|col-md-8|col-md-2',
              '5|7': 'col-md-5|col-md-7',
              '7|5': 'col-md-7|col-md-5',
              '4|8': 'col-md-4|col-md-8',
              '8|4': 'col-md-8|col-md-4',
              '3|9': 'col-md-3|col-md-9',
              '9|3': 'col-md-9|col-md-3',
              '2|10': 'col-md-2|col-md-10',
              '10|2': 'col-md-10|col-md-2',
              '12': 'col-md-12'
            }
          },
          layer: false, # the layer button is confusing for the layperson
          link: { size: 500 }, # truncate after this length
          makebutton: {
            items: {
              primary: {
                title: 'Primary',
                params: { classname: 'btn btn-primary' }
              },
              secondary: {
                title: 'Secondary',
                params: { classname: 'btn btn-secondary' }
              },
              danger: {
                title: 'Danger',
                params: { classname: 'btn btn-danger' }
              },
              primary_large: {
                title: 'Primary (large)',
                params: { classname: 'btn btn-lg btn-primary' }
              },
              secondary_large: {
                title: 'Secondary (large)',
                params: { classname: 'btn btn-lg btn-secondary' }
              },
              danger_large: {
                title: 'Danger (large)',
                params: { classname: 'btn btn-lg btn-danger' }
              }
            }
          },
          outset: false, # tricky to design around
          plugins: ['blockcode', 'carousel', 'cellcolor', 'collapse', 'filelink', 'imageposition', 'imageresize', 'inlineformat', 'listitem', 'makebutton', 'removeformat', 'reorder', 'style'],

          quote: {
            template: '<blockquote class="blockquote text-center"><p class="mb-0"><strong>A well-known quote, contained in a blockquote element.</strong></p></blockquote>'
          },
          styles: {
            table: {
              'bordered': { title: 'Bordered', classname: 'table-bordered' },
              'responsive': { title: 'Responsive', classname: 'table-responsive' },
              'small': { title: 'Small', classname: 'table-sm' },
              'striped': { title: 'Striped', classname: 'table-striped' },
            }
          },
          topbar: {
            undoredo: true,
            shortcuts: true
          },
        }
      end

      def default_mode
        self.class.defaults
      end

      def restricted_mode
        self.class.defaults.merge(
          code: false,
          editor: { csscache: true, https: true, drop: false },
          embed: false,
          image: false,
          plugins: ['blockcode', 'cellcolor', 'inlineformat', 'listitem', 'removeformat', 'reorder', 'style']
        )
      end

      def content
        (defined?(ActionText::RichText) && value.kind_of?(ActionText::RichText)) ? value.body&.to_html : value
      end

      def build_input(&block)
        @builder.super_text_area(name, options[:input].merge(value: content))
      end

      def input_html_options
        { class: 'effective_article_editor form-control', id: unique_id, autocomplete: 'off' }
      end

      def input_js_options
        case mode
        when :default, :admin
          default_mode.merge(active_storage: active_storage, css: css, custom: { css: custom_css })
        when :email
          restricted_mode.merge(active_storage: false, css: css, custom: { css: custom_css })
        when :restricted
          restricted_mode.merge(active_storage: false, css: css, custom: { css: custom_css })
        else
          raise("unexpected mode: #{mode}. Try :default, :email, or :restricted")
        end
      end

      def css
        [asset_path('article_editor/arx-frame.min.css')]
      end

      def custom_css
        [asset_path('application.css'), asset_path('effective_bootstrap_article_editor.css')]
      end

      def active_storage
        return @active_storage unless @active_storage.nil?

        @active_storage = if options.key?(:active_storage)
          options.delete(:active_storage)
        else
          defined?(ActiveStorage).present?
        end
      end

      def mode
        return @mode unless @mode.nil?

        @mode = if options.key?(:mode)
          options.delete(:mode)
        else
          select_mode()
        end
      end

      def select_mode
        EffectiveResources.authorized?(@template, :admin, :effective_article_editor) ? :default : :restricted
      end

    end
  end
end