From 8f3f58e3010fa7c923b77b7d2050175890b58f9b Mon Sep 17 00:00:00 2001 From: erdgeist Date: Tue, 21 Jul 2026 23:17:56 +0200 Subject: Render PDF headlines as document cards, not lightbox images A starred PDF previously ran through the same crop-and-lightbox path a photo does -- exactly the awkward treatment explicit headline designation was meant to avoid. _headline_image.html.erb now branches on @headline_asset.pdf?: a PDF renders as a linked card (a :medium thumbnail, a file icon, its name), no gallery participation at all. The existing image-headline and gallery-fallback logic is otherwise unchanged, now scoped to an image-specific headline only. Other attached PDFs -- headlined or not -- list below as plain links, same reasoning as why non-headline photos still get a gallery trigger: an attached document shouldn't go invisible just because nothing's been starred yet. headline_image's own render guard needed widening to cover a page with PDFs attached but no images and no headline at all -- the one case none of the existing conditions accounted for. --- app/helpers/content_helper.rb | 2 +- app/models/asset.rb | 2 ++ app/views/content/_headline_image.html.erb | 42 ++++++++++++++++++---- config/locales/de.yml | 2 ++ config/locales/en.yml | 2 ++ public/stylesheets/ccc.css | 57 ++++++++++++++++++++++++++++++ test/models/helpers/content_helper_test.rb | 46 ++++++++++++++++++++++++ 7 files changed, 145 insertions(+), 8 deletions(-) diff --git a/app/helpers/content_helper.rb b/app/helpers/content_helper.rb index 459d9e0..7a52d9c 100644 --- a/app/helpers/content_helper.rb +++ b/app/helpers/content_helper.rb @@ -54,7 +54,7 @@ module ContentHelper def headline_image @headline_asset = @page.headline_asset - render :partial => 'content/headline_image' if @headline_asset || @page.assets.images.any? + render :partial => 'content/headline_image' if @headline_asset || @page.assets.images.any? || @page.assets.pdfs.any? end # Returns the published_at attribute of a page if it is not nil, otherwise diff --git a/app/models/asset.rb b/app/models/asset.rb index 9feefa9..ba9c2f0 100644 --- a/app/models/asset.rb +++ b/app/models/asset.rb @@ -10,6 +10,8 @@ class Asset < ApplicationRecord scope :images, -> { where(:upload_content_type => IMAGE_CONTENT_TYPES) } scope :documents, -> { where(:upload_content_type => ["application/pdf", "text/plain", "text/rtf"]) } scope :audio, -> { where(:upload_content_type => ["audio/mpeg", "audio/x-m4a", "audio/wav", "audio/x-wav"]) } + scope :pdfs, -> { where(:upload_content_type => PDF_CONTENT_TYPE) } + scope :headline_eligible, -> { where(:upload_content_type => IMAGE_CONTENT_TYPES + [PDF_CONTENT_TYPE]) } validates :license_key, inclusion: { in: -> { AssetLicense.keys } }, allow_blank: true diff --git a/app/views/content/_headline_image.html.erb b/app/views/content/_headline_image.html.erb index 13a4ac7..4a3dfdc 100644 --- a/app/views/content/_headline_image.html.erb +++ b/app/views/content/_headline_image.html.erb @@ -1,13 +1,25 @@ <% gallery_images = @page.assets.images %> +<% image_headline = @headline_asset if @headline_asset&.image? %> +<% other_pdfs = @page.assets.pdfs.where.not(id: @headline_asset&.id) %> -<% if @headline_asset %> +<% if @headline_asset&.pdf? %> + <%= link_to @headline_asset.upload.url, :class => "headline_document_card", :target => "_blank", :rel => "noopener" do %> + <%= image_tag @headline_asset.upload.url(:medium), :alt => "", :class => "headline_document_card_thumb" %> +
+ <%= icon("file-text", library: "tabler", "aria-hidden": true) %> + <%= @headline_asset.name %> +
+ <% end %> +<% end %> + +<% if image_headline %> <%= link_to( - image_tag(@headline_asset.upload.url(:headline)), - @headline_asset.upload.url, + image_tag(image_headline.upload.url(:headline)), + image_headline.upload.url, :class => "glightbox", :data => { :gallery => "page-#{@page.node.id}", - :glightbox => glightbox_data(@headline_asset, @headline_asset.name), - :"credit-selector" => (@headline_asset.show_credit? ? "#credit_for_asset_#{@headline_asset.id}" : nil) } + :glightbox => glightbox_data(image_headline, image_headline.name), + :"credit-selector" => (image_headline.show_credit? ? "#credit_for_asset_#{image_headline.id}" : nil) } ) %> <% if gallery_images.size > 1 %>
<%= "#{gallery_images.size} #{t(:images)}" %>
@@ -22,10 +34,26 @@ <% end %> <% gallery_images.each do |image| %> - <% next if image == @headline_asset %> - <% next if !@headline_asset && image == gallery_images.first %> + <% next if image == image_headline %> + <% next if !image_headline && image == gallery_images.first %> <%= link_to "", image.upload.url, :class => "glightbox", :style => "display: none", :data => { :gallery => "page-#{@page.node.id}", :glightbox => glightbox_data(image, image.name), :"credit-selector" => (image.show_credit? ? "#credit_for_asset_#{image.id}" : nil) } %> <% end %> + +<% if other_pdfs.any? %> + +<% end %> diff --git a/config/locales/de.yml b/config/locales/de.yml index 21c8ebf..dd22cef 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -130,3 +130,5 @@ de: photo: "Foto %{name}" by: "von %{creator}" licensed_under: "Lizenziert unter %{license}" + + related_documents: "Weitere Dokumente" diff --git a/config/locales/en.yml b/config/locales/en.yml index 1afcf21..9be0152 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -82,3 +82,5 @@ en: photo: "Photo %{name}" by: "by %{creator}" licensed_under: "Licensed under %{license}" + + related_documents: "Related documents" diff --git a/public/stylesheets/ccc.css b/public/stylesheets/ccc.css index 9654594..91f7a06 100644 --- a/public/stylesheets/ccc.css +++ b/public/stylesheets/ccc.css @@ -775,3 +775,60 @@ div.teaser_ruler { .glightbox-clean .gslide-desc { color: CanvasText !important; } + +.headline_document_card { + display: flex; + gap: 1rem; + align-items: flex-start; + border: 1px solid color-mix(in srgb, CanvasText, #808080 25%); + border-radius: 6px; + padding: 1rem; + text-decoration: none; + color: inherit; +} + +.headline_document_card:hover { + border-color: CanvasText; +} + +.headline_document_card_thumb { + max-width: 120px; + max-height: 160px; + border: 1px solid color-mix(in srgb, CanvasText, #808080 25%); + flex-shrink: 0; +} + +.headline_document_card_info { + display: flex; + align-items: center; + gap: 0.5rem; +} + +.headline_document_card_title { + font-weight: bold; +} + +.related_documents { + margin-top: 1rem; +} + +.related_documents_label { + font-style: italic; + font-family: Georgia; + font-size: 0.9rem; + color: color-mix(in srgb, CanvasText, #808080); + margin-bottom: 0.3rem; +} + +.related_documents_list { + list-style: none; + padding: 0; + margin: 0; +} + +.related_document_link { + display: inline-flex; + align-items: center; + gap: 0.4rem; + text-decoration: none; +} diff --git a/test/models/helpers/content_helper_test.rb b/test/models/helpers/content_helper_test.rb index d6c7b43..c69ef4e 100644 --- a/test/models/helpers/content_helper_test.rb +++ b/test/models/helpers/content_helper_test.rb @@ -79,4 +79,50 @@ class ContentHelperTest < ActionView::TestCase I18n.with_locale(:en) { assert_match t(:open_gallery), headline_image } end + + test "headline_image renders a document card for a PDF headline, not a lightbox image" do + node = Node.root.children.create!(:slug => "headline_image_pdf_test") + asset = Asset.create!(:name => "Expert Opinion", :upload_content_type => "application/pdf") + node.draft.assets << asset + node.draft.related_assets.find_by(:asset_id => asset.id).update!(:headline => true) + @page = node.draft + + result = headline_image + + assert_match "headline_document_card", result + assert_match "Expert Opinion", result + assert_no_match "data-gallery", result + end + + test "headline_image lists other attached PDFs below the headline" do + node = Node.root.children.create!(:slug => "headline_image_multi_pdf_test") + headline_pdf = Asset.create!(:name => "Main Filing", :upload_content_type => "application/pdf") + other_pdf = Asset.create!(:name => "Supplementary Exhibit", :upload_content_type => "application/pdf") + node.draft.assets << headline_pdf + node.draft.assets << other_pdf + node.draft.related_assets.find_by(:asset_id => headline_pdf.id).update!(:headline => true) + @page = node.draft + + result = headline_image + + assert_match "Main Filing", result + assert_match "Supplementary Exhibit", result + assert_match "headline_document_card", result + assert_match "related_documents_list", result + end + + test "headline_image lists attached PDFs even with no headline chosen" do + node = Node.root.children.create!(:slug => "headline_image_pdf_no_headline_test") + pdf_a = Asset.create!(:name => "Document A", :upload_content_type => "application/pdf") + pdf_b = Asset.create!(:name => "Document B", :upload_content_type => "application/pdf") + node.draft.assets << pdf_a + node.draft.assets << pdf_b + @page = node.draft + + result = headline_image + + assert_no_match "headline_document_card", result + assert_match "Document A", result + assert_match "Document B", result + end end -- cgit v1.3