diff --git a/app/views/comfy/admin/cms/fragments/_form_fragment_attachments.html.haml b/app/views/comfy/admin/cms/fragments/_form_fragment_attachments.html.haml index 615605f04..f683d005e 100644 --- a/app/views/comfy/admin/cms/fragments/_form_fragment_attachments.html.haml +++ b/app/views/comfy/admin/cms/fragments/_form_fragment_attachments.html.haml @@ -4,9 +4,19 @@ - variant = attachment.variant(Comfy::Cms::File::VARIANT_SIZE[:thumb]) - thumb = image_tag(url_for(variant), size: "200x150") .fragment-attachment.btn-group.btn-group-sm.mb-1 - - filename = attachment.filename.to_s - - truncated_filename = truncate(filename, length: 40, omission: "...#{filename.last(10)}") - = link_to truncated_filename, attachment, data: {toggle: "page-file-popover", content: thumb}, class: "btn btn-light text-truncate", target: "_blank" + :ruby + filename = attachment.filename.to_s + truncated_filename = truncate(filename, length: 40, omission: "...#{filename.last(10)}") + cms_page_file_link_tag = [ + "{{ cms:page_file_link #{fragment_id}", + (", filename: \"#{filename}\"" if multiple), + (", as: image" if attachment.image?), + " }}".html_safe + ].compact.join + = link_to truncated_filename, attachment, + data: {toggle: "page-file-popover", content: thumb}, + class: "btn btn-light text-truncate", target: "_blank", + ondragstart: "event.dataTransfer.setData('text/plain', '#{cms_page_file_link_tag}');" = check_box_tag "#{object_name}[fragments_attributes][#{index}][file_ids_destroy][]", attachment.id, false, id: dom_id(attachment) %label.btn.btn-light{for: dom_id(attachment)} %i.fas.fa-fw.fa-times diff --git a/lib/comfortable_mexican_sofa/content.rb b/lib/comfortable_mexican_sofa/content.rb index 31cf95c65..4ef1a7e57 100644 --- a/lib/comfortable_mexican_sofa/content.rb +++ b/lib/comfortable_mexican_sofa/content.rb @@ -25,6 +25,7 @@ module ComfortableMexicanSofa::Content require_relative "content/tags/snippet" require_relative "content/tags/asset" require_relative "content/tags/file_link" +require_relative "content/tags/page_file_link" require_relative "content/tags/helper" require_relative "content/tags/partial" require_relative "content/tags/template" diff --git a/lib/comfortable_mexican_sofa/content/tags/file.rb b/lib/comfortable_mexican_sofa/content/tags/file.rb index eb4ba3981..fa324a639 100644 --- a/lib/comfortable_mexican_sofa/content/tags/file.rb +++ b/lib/comfortable_mexican_sofa/content/tags/file.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require "comfortable_mexican_sofa/content/tags/file_content.rb" + # File tag allows attaching of file to the page. This controls how files are # uploaded and then displayed on the page. Example tag: # {{cms:file identifier, as: link, label: "My File"}} @@ -12,14 +14,15 @@ # class ComfortableMexicanSofa::Content::Tag::File < ComfortableMexicanSofa::Content::Tag::Fragment + include ComfortableMexicanSofa::Content::Tag::FileContent + include ActionView::Helpers::OutputSafetyHelper + # @type ["url", "link", "image"] attr_reader :as # @type [{String => String}] attr_reader :variant_attrs - delegate :rails_blob_path, to: "Rails.application.routes.url_helpers" - # @param (see ComfortableMexicanSofa::Content::Tag#initialize) def initialize(context:, params: [], source: nil) super @@ -28,25 +31,6 @@ def initialize(context:, params: [], source: nil) @variant_attrs = options.slice("resize", "gravity", "crop") end - def content(file = attachment) - return "" unless file - - if @variant_attrs.present? && attachment.image? - file = file.variant(@variant_attrs) - end - - url = rails_blob_path(file, only_path: true) - - case @as - when "link" - "#{label}" - when "image" - "#{label}" - else - url - end - end - def form_field(object_name, view, index) name = "#{object_name}[fragments_attributes][#{index}][files]" input = view.send(:file_field_tag, name, id: nil, class: "form-control") @@ -58,24 +42,26 @@ def form_field(object_name, view, index) locals: { object_name: object_name, index: index, - attachments: fragment.attachments + attachments: fragment.attachments, + fragment_id: identifier, + multiple: false } ) end - yield [input, attachments_partial].join.html_safe + yield safe_join([input, attachments_partial], "") end protected # @return [ActiveStorage::Blob] - def attachment + def file fragment.attachments.first end # @return [String] def label - @label || attachment&.filename + @label || file&.filename end end diff --git a/lib/comfortable_mexican_sofa/content/tags/file_content.rb b/lib/comfortable_mexican_sofa/content/tags/file_content.rb new file mode 100644 index 000000000..87c78a42d --- /dev/null +++ b/lib/comfortable_mexican_sofa/content/tags/file_content.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +# A mixin for tags that returns the file as their content. +module ComfortableMexicanSofa::Content::Tag::FileContent + + # @param [ActiveStorage::Blob] file + # @param ["link", "image", "url"] as + # @param [{String => String}] variant_attrs ImageMagick variant attributes + # @param [String] label alt text for `as: "image"`, link text for `as: "link"` + # @return [String] + def content(file: self.file, as: self.as, variant_attrs: self.variant_attrs, label: self.label) + return "" unless file + + if variant_attrs.present? && attachment.image? + file = file.variant(variant_attrs) + end + + url = rails_blob_path(file) + + case as + when "link" + "#{label}" + when "image" + "#{label}" + else + url + end + end + + # @param [ActiveStorage::Blob] + # @return [String] + def rails_blob_path(blob) + Rails.application.routes.url_helpers.rails_blob_path(blob, only_path: true) + end + +end diff --git a/lib/comfortable_mexican_sofa/content/tags/file_link.rb b/lib/comfortable_mexican_sofa/content/tags/file_link.rb index e6fe5331f..552c316e4 100644 --- a/lib/comfortable_mexican_sofa/content/tags/file_link.rb +++ b/lib/comfortable_mexican_sofa/content/tags/file_link.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require "comfortable_mexican_sofa/content/tags/file_content.rb" + # This is how you link previously uploaded file to anywhere. Good example may be # a header image you want to use on the layout level. # {{cms:file_link id, as: image}} @@ -12,9 +14,16 @@ # class ComfortableMexicanSofa::Content::Tag::FileLink < ComfortableMexicanSofa::Content::Tag - attr_reader :identifier, :as, :variant_attrs + include ComfortableMexicanSofa::Content::Tag::FileContent + + # @return [String] A {Comfy::Cms::Site#files} ID. + attr_reader :identifier + + # @type ["url", "link", "image"] + attr_reader :as - delegate :rails_blob_path, to: "Rails.application.routes.url_helpers" + # @type [{String => String}] + attr_reader :variant_attrs def initialize(context:, params: [], source: nil) super @@ -29,32 +38,20 @@ def initialize(context:, params: [], source: nil) end end - def file - @file ||= context.site.files.detect { |f| f.id == identifier.to_i } + # @return [Comfy::Cms::File] + def file_record + @file_record ||= context.site.files.detect { |f| f.id == identifier.to_i } end - def label - @file.label.present? ? @file.label : @file.attachment.filename + # @return [ActiveStorage::Blob] + def file + file_record&.attachment end - def content - return "" unless file&.attachment - - attachment = file.attachment - if @variant_attrs.present? && attachment.image? - attachment = attachment.variant(@variant_attrs) - end - - url = rails_blob_path(attachment, only_path: true) - - case @as - when "link" - "#{label}" - when "image" - "#{label}" - else - url - end + # @return [String] + def label + return "" if file_record.nil? + file_record.label.presence || file.filename.to_s end end diff --git a/lib/comfortable_mexican_sofa/content/tags/files.rb b/lib/comfortable_mexican_sofa/content/tags/files.rb index 48ec68bff..09f9bcb96 100644 --- a/lib/comfortable_mexican_sofa/content/tags/files.rb +++ b/lib/comfortable_mexican_sofa/content/tags/files.rb @@ -5,16 +5,11 @@ # class ComfortableMexicanSofa::Content::Tag::Files < ComfortableMexicanSofa::Content::Tag::File - # @param (see ComfortableMexicanSofa::Content::Tag#initialize) - def initialize(context:, params: [], source: nil) - super - end - def content return "" if fragment.attachments.blank? fragment.attachments.collect do |attachment| - super(attachment) + super(file: attachment, label: attachment.filename) end.join(" ") end @@ -29,12 +24,14 @@ def form_field(object_name, view, index) locals: { object_name: object_name, index: index, - attachments: fragment.attachments + attachments: fragment.attachments, + fragment_id: identifier, + multiple: true } ) end - yield [input, attachments_partial].join.html_safe + yield safe_join([input, attachments_partial], "") end end diff --git a/lib/comfortable_mexican_sofa/content/tags/page_file_link.rb b/lib/comfortable_mexican_sofa/content/tags/page_file_link.rb new file mode 100644 index 000000000..7e21883ee --- /dev/null +++ b/lib/comfortable_mexican_sofa/content/tags/page_file_link.rb @@ -0,0 +1,80 @@ +# frozen_string_literal: true + +require "comfortable_mexican_sofa/content/tags/file_content.rb" + +# This tag allows you to link page-level files from withing the page. +# +# E.g. if your layout has: +# +# {{ cms:file graphic }} +# {{ cms:files attachments }} +# +# You can link to the files from an individual page like so: +# +# {{ cms:page_file_link graphic }} +# {{ cms:page_file_link attachments, filename: "cat.jpg" }} +# +# `as` - url (default) | link | image - how file gets rendered out +# `label` - attach label attribute to link or image tag +# `resize` - imagemagick option. For example: "100x50>" +# `gravity` - imagemagick option. For example: "center" +# `crop` - imagemagick option. For example: "100x50+0+0" +# +class ComfortableMexicanSofa::Content::Tag::PageFileLink < ComfortableMexicanSofa::Content::Tag + + include ComfortableMexicanSofa::Content::Tag::FileContent + + # @return [String] A `cms:file(s)` identifier. + attr_reader :identifier + + # @type ["url", "link", "image"] + attr_reader :as + + # @type [{String => String}] + attr_reader :variant_attrs + + # @return [String] Used to refer to a file in a {{ cms:files }} tag. + attr_reader :filename + + # @param (see ComfortableMexicanSofa::Content::Tag#initialize) + def initialize(context:, params: [], source: nil) + super + + options = params.extract_options! + @identifier = params[0] + @as = options["as"] || "url" + @variant_attrs = options.slice("resize", "gravity", "crop") + @filename = options["filename"] + + unless @identifier.present? + raise Error, "Missing identifier for page file link tag" + end + end + + # @return [Comfy::Cms::Fragment] + def fragment + @fragment ||= context.fragments.detect { |f| f.identifier == identifier } + end + + # @return [ActiveStorage::Blob] + def file + @file ||= if fragment.nil? + nil + elsif filename.nil? + fragment.attachments.first + else + fragment.attachments.detect { |a| a.filename.to_s == filename } + end + end + + # @return [String] + def label + return if file.nil? + file.filename.to_s + end + +end + +ComfortableMexicanSofa::Content::Renderer.register_tag( + :page_file_link, ComfortableMexicanSofa::Content::Tag::PageFileLink +) diff --git a/test/lib/content/renderer_test.rb b/test/lib/content/renderer_test.rb index bf3991215..4b6012599 100644 --- a/test/lib/content/renderer_test.rb +++ b/test/lib/content/renderer_test.rb @@ -25,22 +25,23 @@ class TestBlockTag < ComfortableMexicanSofa::Content::Block end DEFAULT_REGISTERED_TAGS = { - "wysiwyg" => ComfortableMexicanSofa::Content::Tag::Wysiwyg, - "text" => ComfortableMexicanSofa::Content::Tag::Text, - "textarea" => ComfortableMexicanSofa::Content::Tag::TextArea, - "markdown" => ComfortableMexicanSofa::Content::Tag::Markdown, - "datetime" => ComfortableMexicanSofa::Content::Tag::Datetime, - "date" => ComfortableMexicanSofa::Content::Tag::Date, - "number" => ComfortableMexicanSofa::Content::Tag::Number, - "checkbox" => ComfortableMexicanSofa::Content::Tag::Checkbox, - "file" => ComfortableMexicanSofa::Content::Tag::File, - "files" => ComfortableMexicanSofa::Content::Tag::Files, - "snippet" => ComfortableMexicanSofa::Content::Tag::Snippet, - "asset" => ComfortableMexicanSofa::Content::Tag::Asset, - "file_link" => ComfortableMexicanSofa::Content::Tag::FileLink, - "helper" => ComfortableMexicanSofa::Content::Tag::Helper, - "partial" => ComfortableMexicanSofa::Content::Tag::Partial, - "template" => ComfortableMexicanSofa::Content::Tag::Template + "wysiwyg" => ComfortableMexicanSofa::Content::Tag::Wysiwyg, + "text" => ComfortableMexicanSofa::Content::Tag::Text, + "textarea" => ComfortableMexicanSofa::Content::Tag::TextArea, + "markdown" => ComfortableMexicanSofa::Content::Tag::Markdown, + "datetime" => ComfortableMexicanSofa::Content::Tag::Datetime, + "date" => ComfortableMexicanSofa::Content::Tag::Date, + "number" => ComfortableMexicanSofa::Content::Tag::Number, + "checkbox" => ComfortableMexicanSofa::Content::Tag::Checkbox, + "file" => ComfortableMexicanSofa::Content::Tag::File, + "files" => ComfortableMexicanSofa::Content::Tag::Files, + "snippet" => ComfortableMexicanSofa::Content::Tag::Snippet, + "asset" => ComfortableMexicanSofa::Content::Tag::Asset, + "file_link" => ComfortableMexicanSofa::Content::Tag::FileLink, + "page_file_link" => ComfortableMexicanSofa::Content::Tag::PageFileLink, + "helper" => ComfortableMexicanSofa::Content::Tag::Helper, + "partial" => ComfortableMexicanSofa::Content::Tag::Partial, + "template" => ComfortableMexicanSofa::Content::Tag::Template }.freeze setup do diff --git a/test/lib/content/tags/file_link_test.rb b/test/lib/content/tags/file_link_test.rb index e2874b382..ba6887a5c 100644 --- a/test/lib/content/tags/file_link_test.rb +++ b/test/lib/content/tags/file_link_test.rb @@ -49,15 +49,15 @@ def test_init_without_identifier def test_file tag = ComfortableMexicanSofa::Content::Tag::FileLink.new(context: @page, params: [@file.id]) - assert tag.file.is_a?(Comfy::Cms::File) + assert_instance_of Comfy::Cms::File, tag.file_record tag = ComfortableMexicanSofa::Content::Tag::FileLink.new(context: @page, params: ["invalid"]) - assert_nil tag.file + assert_nil tag.file_record end def test_content tag = ComfortableMexicanSofa::Content::Tag::FileLink.new(context: @page, params: [@file.id]) - out = rails_blob_path(tag.file.attachment, only_path: true) + out = rails_blob_path(tag.file, only_path: true) assert_equal out, tag.content assert_equal out, tag.render end @@ -67,7 +67,7 @@ def test_content_as_link context: @page, params: [@file.id, { "as" => "link" }] ) - url = rails_blob_path(tag.file.attachment, only_path: true) + url = rails_blob_path(tag.file, only_path: true) out = "default file" assert_equal out, tag.content assert_equal out, tag.render @@ -78,7 +78,7 @@ def test_content_as_image context: @page, params: [@file.id, { "as" => "image" }] ) - url = rails_blob_path(tag.file.attachment, only_path: true) + url = rails_blob_path(tag.file, only_path: true) out = "default file" assert_equal out, tag.content assert_equal out, tag.render diff --git a/test/lib/content/tags/page_file_link_test.rb b/test/lib/content/tags/page_file_link_test.rb new file mode 100644 index 000000000..5e0ebf1c8 --- /dev/null +++ b/test/lib/content/tags/page_file_link_test.rb @@ -0,0 +1,64 @@ +# frozen_string_literal: true + +require_relative "../../../test_helper" + +class ContentTagsPageFileLinkTest < ActiveSupport::TestCase + + delegate :rails_blob_path, to: "Rails.application.routes.url_helpers" + + setup do + @page = comfy_cms_pages(:default) + @file = comfy_cms_files(:default) + end + + # -- Tests ------------------------------------------------------------------- + + def test_init + tag = ComfortableMexicanSofa::Content::Tag::PageFileLink.new(context: @page, params: ["123"]) + assert_equal "123", tag.identifier + assert_equal "url", tag.as + end + + def test_init_with_params + tag = ComfortableMexicanSofa::Content::Tag::PageFileLink.new( + context: @page, + params: [ + "123", { + "as" => "image", + "resize" => "100x100", + "gravity" => "center", + "crop" => "100x100+0+0" + } + ] + ) + assert_equal "123", tag.identifier + assert_equal "image", tag.as + assert_equal ({ + "resize" => "100x100", + "gravity" => "center", + "crop" => "100x100+0+0" + }), tag.variant_attrs + end + + def test_init_without_identifier + message = "Missing identifier for page file link tag" + assert_exception_raised ComfortableMexicanSofa::Content::Tag::Error, message do + ComfortableMexicanSofa::Content::Tag::PageFileLink.new(context: @page) + end + end + + def test_content + fragment = comfy_cms_fragments(:file) + tag = ComfortableMexicanSofa::Content::Tag::PageFileLink.new(context: @page, params: [fragment.identifier]) + out = rails_blob_path(tag.file, only_path: true) + assert_equal out, tag.content + assert_equal out, tag.render + end + + def test_content_when_not_found + tag = ComfortableMexicanSofa::Content::Tag::PageFileLink.new(context: @page, params: ["invalid"]) + assert_equal "", tag.content + assert_equal "", tag.render + end + +end