Module: AttachableHelper

Defined in:
app/helpers/attachable_helper.rb

Overview

View helper for the attachments UI: panels, upload grids, and the
upload form rendered for records that carry uploads.

Instance Method Summary collapse

Instance Method Details

#attachable_content(context_object, options = nil) ⇒ String

Renders the uploads grid for a record, without a panel wrapper

Parameters:

  • context_object (ActiveRecord::Base)

    record whose uploads are shown

  • options (Hash, nil) (defaults to: nil)

    rendering options

Options Hash (options):

  • :context_class (Class)

    class used for authorization/routing (default: context_object's class)

  • :controller_path (String)

    controller used for route generation (default: current controller)

  • :disable_delete (Boolean)

    hide delete controls (default: false)

Returns:

  • (String)

    HTML turbo frame with the uploads grid



59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'app/helpers/attachable_helper.rb', line 59

def attachable_content(context_object, options = nil)
  # Use explicit context_class from options if provided, otherwise use the object's class
  context_class = options&.dig(:context_class) || context_object.class
  controller_path = options&.dig(:controller_path) || controller_name
  disable_delete = options&.dig(:disable_delete) || false

  turbo_frame_tag "attachments-frame-#{context_object.try(:id).to_i}" do
    (:div, id: "attachments-#{context_object.try(:id).to_i}", class: 'row row-cols-1 row-cols-sm-2 row-cols-md-3 row-cols-lg-4 row-cols-xl-6 g-4 attachments', data: { context_object_id: context_object.try(:id).to_i }) do
      context_object.uploads.each do |upload|
        render Crm::UploadWrapperComponent.new(
          upload: upload,
          context_object: context_object,
          context_class: context_class,
          controller_path: controller_path,
          disable_delete: disable_delete
        )
      end
    end
  end
end

#attachable_form(context_object, options = {}) ⇒ String

A block, when given, renders into the panel header alongside the
Publication / Upload File buttons — the activity form puts its result
submit buttons there so both sets of actions share one line.

Parameters:

  • context_object (ActiveRecord::Base)

    record the attachments belong to

  • options (Hash) (defaults to: {})

    form configuration

Options Hash (options):

  • :context_class (Class)

    class used for the hidden context field
    (default: context_object's class)

  • :controller_path (String)

    controller used for route generation
    (default: current controller)

  • :attach_url (String)

    explicit upload URL (default: the controller's attach action)

  • :wrapped (Boolean)

    render inside a panel (default: true)

  • :skip_publications (Boolean)

    hide the publications UI (default: false)

  • :mutiple_files_allowed (Boolean)

    allow multiple file selection
    (default: true; key keeps a legacy typo for compatibility)

  • :template_options_for_select (Array)

    options for the template select

  • :category_options_for_select (Array)

    options for the category select

  • :parent_form_id (String)

    DOM id of the wrapping form

  • :small_buttons (Boolean)

    use btn-sm header actions (default: true)

Returns:

  • (String)

    rendered Crm::AttachmentsComponent



99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'app/helpers/attachable_helper.rb', line 99

def attachable_form(context_object, options = {}, &)
  # Use explicit context_class from options if provided, otherwise use the object's class
  context_class = options[:context_class] || context_object.class
  # Use explicit controller_path from options if provided, otherwise use current controller_name
  # This is needed for irregular controllers (e.g., article_trainings vs Article model)
  controller_path = options[:controller_path] || controller_name

  # Generate attach URL - prefer explicit URL from options, otherwise use url_for with controller_path
  attach_url = options[:attach_url].presence || url_for(controller: controller_path, action: :attach, only_path: true)

  # Use the new component system instead of legacy combo_form partial
  render Crm::AttachmentsComponent.new(
    context_object: context_object,
    attach_url: attach_url,
    context_class_name: context_class.name, # Used for hidden field (load_context)
    controller_path: controller_path, # Used by components for route generation
    wrapped: options[:wrapped] != false, # Default to wrapped (true), unless explicitly false
    skip_publications: options[:skip_publications] || false,
    multiple_files_allowed: options[:mutiple_files_allowed] != false, # Handle typo in legacy code
    template_options_for_select: options[:template_options_for_select],
    category_options_for_select: options[:category_options_for_select],
    parent_form_id: options[:parent_form_id],
    # Default keeps btn-sm header actions; the activity form opts out so
    # they match its default-sized result submit buttons.
    small_buttons: options[:small_buttons] != false
  ), &
end

#attachable_panel(context_object, options = {}) ⇒ String

Renders the attachments panel for a record

Parameters:

  • context_object (ActiveRecord::Base)

    record whose uploads are shown

  • options (Hash) (defaults to: {})

    rendering options, forwarded to #attachable_content

Options Hash (options):

  • :wrapped (Boolean)

    false to render the content without the surrounding panel

  • :context_class (Class)

    class used for authorization/routing (default: context_object's class)

  • :controller_path (String)

    controller used for route generation (default: current controller)

  • :disable_delete (Boolean)

    hide delete controls (default: false)

Returns:

  • (String)

    HTML for the attachments panel



42
43
44
45
46
47
48
49
50
# File 'app/helpers/attachable_helper.rb', line 42

def attachable_panel(context_object, options = {})
  if options[:wrapped] == false
    attachable_content(context_object, options)
  else
    simple_panel('Attachments') do
      attachable_content(context_object, options)
    end
  end
end

Renders a delete button for a single upload, wired to the current
controller's remove_attachment action over Turbo.

Parameters:

  • record_type (String, nil)

    class name used as the attachment context;
    falls back to record's class name when nil

  • upload (Upload)

    the upload to remove

  • record (ActiveRecord::Base, nil) (defaults to: nil)

    record the upload is attached to

Returns:

  • (String)

    HTML for the delete button form



14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# File 'app/helpers/attachable_helper.rb', line 14

def removable_link(record_type, upload, record = nil)
  button_to(
        url_for(action: :remove_attachment,
                upload_id: upload.id,
                context_object_id: record.try(:id),
                context_class: record_type || record.class.name),
        method: :delete,
        class: 'btn btn-sm btn-outline-danger w-100',
        form: {
          data: {
            turbo_stream: true,
            turbo_confirm: 'Are you sure you want to delete this attachment?'
          },
          style: 'display: inline;'
        }
      ) do
    fa_icon('trash', text: 'Remove')
  end
end