summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorerdgeist <erdgeist@erdgeist.org>2026-07-21 23:17:56 +0200
committererdgeist <erdgeist@erdgeist.org>2026-07-21 23:17:56 +0200
commit8f3f58e3010fa7c923b77b7d2050175890b58f9b (patch)
treeece76f69129b8b6c3c59b0b71f69e9fc59950d30
parent18114a978b7c037da5194c48f860435e261f9a0b (diff)
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.
-rw-r--r--app/helpers/content_helper.rb2
-rw-r--r--app/models/asset.rb2
-rw-r--r--app/views/content/_headline_image.html.erb42
-rw-r--r--config/locales/de.yml2
-rw-r--r--config/locales/en.yml2
-rw-r--r--public/stylesheets/ccc.css57
-rw-r--r--test/models/helpers/content_helper_test.rb46
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
54 54
55 def headline_image 55 def headline_image
56 @headline_asset = @page.headline_asset 56 @headline_asset = @page.headline_asset
57 render :partial => 'content/headline_image' if @headline_asset || @page.assets.images.any? 57 render :partial => 'content/headline_image' if @headline_asset || @page.assets.images.any? || @page.assets.pdfs.any?
58 end 58 end
59 59
60 # Returns the published_at attribute of a page if it is not nil, otherwise 60 # 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
10 scope :images, -> { where(:upload_content_type => IMAGE_CONTENT_TYPES) } 10 scope :images, -> { where(:upload_content_type => IMAGE_CONTENT_TYPES) }
11 scope :documents, -> { where(:upload_content_type => ["application/pdf", "text/plain", "text/rtf"]) } 11 scope :documents, -> { where(:upload_content_type => ["application/pdf", "text/plain", "text/rtf"]) }
12 scope :audio, -> { where(:upload_content_type => ["audio/mpeg", "audio/x-m4a", "audio/wav", "audio/x-wav"]) } 12 scope :audio, -> { where(:upload_content_type => ["audio/mpeg", "audio/x-m4a", "audio/wav", "audio/x-wav"]) }
13 scope :pdfs, -> { where(:upload_content_type => PDF_CONTENT_TYPE) }
14
13 scope :headline_eligible, -> { where(:upload_content_type => IMAGE_CONTENT_TYPES + [PDF_CONTENT_TYPE]) } 15 scope :headline_eligible, -> { where(:upload_content_type => IMAGE_CONTENT_TYPES + [PDF_CONTENT_TYPE]) }
14 16
15 validates :license_key, inclusion: { in: -> { AssetLicense.keys } }, allow_blank: true 17 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 @@
1<% gallery_images = @page.assets.images %> 1<% gallery_images = @page.assets.images %>
2<% image_headline = @headline_asset if @headline_asset&.image? %>
3<% other_pdfs = @page.assets.pdfs.where.not(id: @headline_asset&.id) %>
2 4
3<% if @headline_asset %> 5<% if @headline_asset&.pdf? %>
6 <%= link_to @headline_asset.upload.url, :class => "headline_document_card", :target => "_blank", :rel => "noopener" do %>
7 <%= image_tag @headline_asset.upload.url(:medium), :alt => "", :class => "headline_document_card_thumb" %>
8 <div class="headline_document_card_info">
9 <%= icon("file-text", library: "tabler", "aria-hidden": true) %>
10 <span class="headline_document_card_title"><%= @headline_asset.name %></span>
11 </div>
12 <% end %>
13<% end %>
14
15<% if image_headline %>
4 <%= link_to( 16 <%= link_to(
5 image_tag(@headline_asset.upload.url(:headline)), 17 image_tag(image_headline.upload.url(:headline)),
6 @headline_asset.upload.url, 18 image_headline.upload.url,
7 :class => "glightbox", 19 :class => "glightbox",
8 :data => { :gallery => "page-#{@page.node.id}", 20 :data => { :gallery => "page-#{@page.node.id}",
9 :glightbox => glightbox_data(@headline_asset, @headline_asset.name), 21 :glightbox => glightbox_data(image_headline, image_headline.name),
10 :"credit-selector" => (@headline_asset.show_credit? ? "#credit_for_asset_#{@headline_asset.id}" : nil) } 22 :"credit-selector" => (image_headline.show_credit? ? "#credit_for_asset_#{image_headline.id}" : nil) }
11 ) %> 23 ) %>
12 <% if gallery_images.size > 1 %> 24 <% if gallery_images.size > 1 %>
13 <div class="right"><%= "#{gallery_images.size} #{t(:images)}" %></div> 25 <div class="right"><%= "#{gallery_images.size} #{t(:images)}" %></div>
@@ -22,10 +34,26 @@
22<% end %> 34<% end %>
23 35
24<% gallery_images.each do |image| %> 36<% gallery_images.each do |image| %>
25 <% next if image == @headline_asset %> 37 <% next if image == image_headline %>
26 <% next if !@headline_asset && image == gallery_images.first %> 38 <% next if !image_headline && image == gallery_images.first %>
27 <%= link_to "", image.upload.url, :class => "glightbox", :style => "display: none", 39 <%= link_to "", image.upload.url, :class => "glightbox", :style => "display: none",
28 :data => { :gallery => "page-#{@page.node.id}", 40 :data => { :gallery => "page-#{@page.node.id}",
29 :glightbox => glightbox_data(image, image.name), 41 :glightbox => glightbox_data(image, image.name),
30 :"credit-selector" => (image.show_credit? ? "#credit_for_asset_#{image.id}" : nil) } %> 42 :"credit-selector" => (image.show_credit? ? "#credit_for_asset_#{image.id}" : nil) } %>
31<% end %> 43<% end %>
44
45<% if other_pdfs.any? %>
46 <div class="related_documents">
47 <div class="related_documents_label"><%= t(:related_documents) %></div>
48 <ul class="related_documents_list">
49 <% other_pdfs.each do |pdf| %>
50 <li>
51 <%= link_to pdf.upload.url, :class => "related_document_link", :target => "_blank", :rel => "noopener" do %>
52 <%= icon("file-text", library: "tabler", "aria-hidden": true) %>
53 <span><%= pdf.name %></span>
54 <% end %>
55 </li>
56 <% end %>
57 </ul>
58 </div>
59<% 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:
130 photo: "Foto %{name}" 130 photo: "Foto %{name}"
131 by: "von %{creator}" 131 by: "von %{creator}"
132 licensed_under: "Lizenziert unter %{license}" 132 licensed_under: "Lizenziert unter %{license}"
133
134 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:
82 photo: "Photo %{name}" 82 photo: "Photo %{name}"
83 by: "by %{creator}" 83 by: "by %{creator}"
84 licensed_under: "Licensed under %{license}" 84 licensed_under: "Licensed under %{license}"
85
86 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 {
775.glightbox-clean .gslide-desc { 775.glightbox-clean .gslide-desc {
776 color: CanvasText !important; 776 color: CanvasText !important;
777} 777}
778
779.headline_document_card {
780 display: flex;
781 gap: 1rem;
782 align-items: flex-start;
783 border: 1px solid color-mix(in srgb, CanvasText, #808080 25%);
784 border-radius: 6px;
785 padding: 1rem;
786 text-decoration: none;
787 color: inherit;
788}
789
790.headline_document_card:hover {
791 border-color: CanvasText;
792}
793
794.headline_document_card_thumb {
795 max-width: 120px;
796 max-height: 160px;
797 border: 1px solid color-mix(in srgb, CanvasText, #808080 25%);
798 flex-shrink: 0;
799}
800
801.headline_document_card_info {
802 display: flex;
803 align-items: center;
804 gap: 0.5rem;
805}
806
807.headline_document_card_title {
808 font-weight: bold;
809}
810
811.related_documents {
812 margin-top: 1rem;
813}
814
815.related_documents_label {
816 font-style: italic;
817 font-family: Georgia;
818 font-size: 0.9rem;
819 color: color-mix(in srgb, CanvasText, #808080);
820 margin-bottom: 0.3rem;
821}
822
823.related_documents_list {
824 list-style: none;
825 padding: 0;
826 margin: 0;
827}
828
829.related_document_link {
830 display: inline-flex;
831 align-items: center;
832 gap: 0.4rem;
833 text-decoration: none;
834}
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
79 79
80 I18n.with_locale(:en) { assert_match t(:open_gallery), headline_image } 80 I18n.with_locale(:en) { assert_match t(:open_gallery), headline_image }
81 end 81 end
82
83 test "headline_image renders a document card for a PDF headline, not a lightbox image" do
84 node = Node.root.children.create!(:slug => "headline_image_pdf_test")
85 asset = Asset.create!(:name => "Expert Opinion", :upload_content_type => "application/pdf")
86 node.draft.assets << asset
87 node.draft.related_assets.find_by(:asset_id => asset.id).update!(:headline => true)
88 @page = node.draft
89
90 result = headline_image
91
92 assert_match "headline_document_card", result
93 assert_match "Expert Opinion", result
94 assert_no_match "data-gallery", result
95 end
96
97 test "headline_image lists other attached PDFs below the headline" do
98 node = Node.root.children.create!(:slug => "headline_image_multi_pdf_test")
99 headline_pdf = Asset.create!(:name => "Main Filing", :upload_content_type => "application/pdf")
100 other_pdf = Asset.create!(:name => "Supplementary Exhibit", :upload_content_type => "application/pdf")
101 node.draft.assets << headline_pdf
102 node.draft.assets << other_pdf
103 node.draft.related_assets.find_by(:asset_id => headline_pdf.id).update!(:headline => true)
104 @page = node.draft
105
106 result = headline_image
107
108 assert_match "Main Filing", result
109 assert_match "Supplementary Exhibit", result
110 assert_match "headline_document_card", result
111 assert_match "related_documents_list", result
112 end
113
114 test "headline_image lists attached PDFs even with no headline chosen" do
115 node = Node.root.children.create!(:slug => "headline_image_pdf_no_headline_test")
116 pdf_a = Asset.create!(:name => "Document A", :upload_content_type => "application/pdf")
117 pdf_b = Asset.create!(:name => "Document B", :upload_content_type => "application/pdf")
118 node.draft.assets << pdf_a
119 node.draft.assets << pdf_b
120 @page = node.draft
121
122 result = headline_image
123
124 assert_no_match "headline_document_card", result
125 assert_match "Document A", result
126 assert_match "Document B", result
127 end
82end 128end