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"
- ""
- 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"
+ ""
+ 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"
- ""
- 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 = ""
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