From 9ec4364750fb1345af4fcb5a9e74682291921b2f Mon Sep 17 00:00:00 2001 From: MAEDA Go Date: Wed, 18 Feb 2026 12:58:32 +0900 Subject: [PATCH] Show PDF preview for repository files and attachments Patch by Gregor Schmidt (user:schmidt). --- app/assets/stylesheets/application.css | 2 ++ app/assets/stylesheets/scm.css | 30 +++++++++++++++++++ app/controllers/attachments_controller.rb | 2 ++ app/views/attachments/pdf.html.erb | 4 +++ app/views/common/_pdf.html.erb | 8 +++++ app/views/repositories/entry.html.erb | 2 ++ config/locales/en.yml | 1 + .../functional/attachments_controller_test.rb | 11 +++++++ 8 files changed, 60 insertions(+) create mode 100644 app/views/attachments/pdf.html.erb create mode 100644 app/views/common/_pdf.html.erb diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css index 3c01a73ff..0b4838dce 100644 --- a/app/assets/stylesheets/application.css +++ b/app/assets/stylesheets/application.css @@ -2435,6 +2435,8 @@ img { box-shadow: 0 1px 2px rgba(var(--oc-gray-9-rgb), 0.05); } +.pdf-full-view-link { margin: 0 0 0.5em; } + /* tablesort */ th[role=columnheader]:not(.no-sort) { cursor: pointer; diff --git a/app/assets/stylesheets/scm.css b/app/assets/stylesheets/scm.css index 23cc99dd3..be6058bcf 100644 --- a/app/assets/stylesheets/scm.css +++ b/app/assets/stylesheets/scm.css @@ -102,6 +102,36 @@ table.filecontent tr:target td.line-code { background-color:var(--oc-blue-0); } img.filecontent, video.filecontent { max-inline-size: 100%; } +.filecontent.pdf { + inline-size: 100%; + max-block-size: 80vh; + overflow: hidden; + position: relative; +} + +.filecontent.pdf::before { + content: " "; + display: block; + inline-size: 100%; + padding-block-end: 100%; +} + +.filecontent.pdf object, +.filecontent.pdf iframe, +.filecontent.pdf p { + position: absolute; + inset-block-start: 0; + inset-inline-start: 0; + inset-inline-end: 0; +} + +.filecontent.pdf iframe, +.filecontent.pdf object { + border: none; + inline-size: 100%; + block-size: 100%; +} + .previous-filename { font-weight: normal; } diff --git a/app/controllers/attachments_controller.rb b/app/controllers/attachments_controller.rb index 90c3c7070..18795891d 100644 --- a/app/controllers/attachments_controller.rb +++ b/app/controllers/attachments_controller.rb @@ -61,6 +61,8 @@ class AttachmentsController < ApplicationController render :action => 'file' elsif @attachment.is_image? render :action => 'image' + elsif @attachment.is_pdf? + render :action => 'pdf' else render :action => 'other' end diff --git a/app/views/attachments/pdf.html.erb b/app/views/attachments/pdf.html.erb new file mode 100644 index 000000000..441e6155c --- /dev/null +++ b/app/views/attachments/pdf.html.erb @@ -0,0 +1,4 @@ +<%= render :layout => 'layouts/file' do %> + <%= render :partial => 'common/pdf', + :locals => {:path => download_named_attachment_path(@attachment, @attachment.filename)} %> +<% end %> diff --git a/app/views/common/_pdf.html.erb b/app/views/common/_pdf.html.erb new file mode 100644 index 000000000..1e8ba4866 --- /dev/null +++ b/app/views/common/_pdf.html.erb @@ -0,0 +1,8 @@ + +
+ + <%= render :partial => 'common/other' %> + +
diff --git a/app/views/repositories/entry.html.erb b/app/views/repositories/entry.html.erb index b4a221af9..66c2154b9 100644 --- a/app/views/repositories/entry.html.erb +++ b/app/views/repositories/entry.html.erb @@ -10,6 +10,8 @@ <% if Redmine::MimeType.is_type?('image', @path) %> <%= render :partial => 'common/image', :locals => {:path => @raw_url, :alt => @path} %> +<% elsif Redmine::MimeType.of(@path) == 'application/pdf' %> + <%= render :partial => 'common/pdf', :locals => {:path => @raw_url} %> <% elsif Redmine::MimeType.of(@path) == 'text/x-textile' %> <%= render :partial => 'common/markup', :locals => {:markup_text_formatting => 'textile', :markup_text => @content} %> <% elsif Redmine::MimeType.of(@path) == 'text/markdown' %> diff --git a/config/locales/en.yml b/config/locales/en.yml index e99f01f74..b9d82807f 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -726,6 +726,7 @@ en: label_no_preview: No preview available label_no_preview_alternative_html: No preview available. %{link} the file instead. label_no_preview_download: Download + label_open_in_full_view: Open in full view label_change_status: Change status label_history: History label_attachment: File diff --git a/test/functional/attachments_controller_test.rb b/test/functional/attachments_controller_test.rb index 7d856b11d..bc664cea5 100644 --- a/test/functional/attachments_controller_test.rb +++ b/test/functional/attachments_controller_test.rb @@ -239,6 +239,17 @@ class AttachmentsControllerTest < Redmine::ControllerTest assert_select 'img.filecontent', :src => attachments(:attachments_010).filename end + def test_show_pdf + @request.session[:user_id] = 2 + get(:show, :params => {:id => 23}) + assert_response :success + assert_equal 'text/html', @response.media_type + + path = download_named_attachment_path(attachments(:attachments_023), attachments(:attachments_023).filename) + assert_select ".filecontent.pdf object[data='#{path}']" + assert_select '.nodata', :text => 'No preview available' + end + def test_show_other_with_no_preview @request.session[:user_id] = 2 get(:show, :params => {:id => 6}) -- 2.50.1