From 9272e2f41f81126b32552ad63e0aac1b31da0b63 Mon Sep 17 00:00:00 2001 From: MAEDA Go Date: Thu, 20 Aug 2026 19:15:46 +0900 Subject: [PATCH 1/3] Extract the fallback for undetermined content types into Redmine::MimeType.infer_type --- app/controllers/attachments_controller.rb | 7 +------ app/helpers/icons_helper.rb | 8 +------- lib/redmine/mime_type.rb | 10 ++++++++++ test/unit/lib/redmine/mime_type_test.rb | 18 ++++++++++++++++++ 4 files changed, 30 insertions(+), 13 deletions(-) diff --git a/app/controllers/attachments_controller.rb b/app/controllers/attachments_controller.rb index 283803d7b..168b344ed 100644 --- a/app/controllers/attachments_controller.rb +++ b/app/controllers/attachments_controller.rb @@ -294,12 +294,7 @@ class AttachmentsController < ApplicationController end def detect_content_type(attachment, is_thumb = false) - content_type = attachment.content_type - if content_type.blank? || content_type == "application/octet-stream" - content_type = - Redmine::MimeType.of(attachment.filename).presence || - "application/octet-stream" - end + content_type = Redmine::MimeType.infer_type(attachment.content_type, attachment.filename) if is_thumb && !content_type.start_with?("image/") # Thumbnails of non-image files are stored in PNG format diff --git a/app/helpers/icons_helper.rb b/app/helpers/icons_helper.rb index 0e1e03520..6027da0f7 100644 --- a/app/helpers/icons_helper.rb +++ b/app/helpers/icons_helper.rb @@ -160,13 +160,7 @@ module IconsHelper end def icon_for_mime_type(mime, filename = nil) - # Content type detection falls back to application/octet-stream when it - # cannot tell the type from the file contents. In that case only, guess - # the type from the filename, as AttachmentsController#detect_content_type - # does. - if filename.present? && (mime.blank? || mime == 'application/octet-stream') - mime = Redmine::MimeType.of(filename) - end + mime = Redmine::MimeType.infer_type(mime, filename) MIME_TYPE_ICONS[mime] || MIME_TYPE_ICONS[mime.to_s.split('/').first] || diff --git a/lib/redmine/mime_type.rb b/lib/redmine/mime_type.rb index e308d59e0..1b810338a 100644 --- a/lib/redmine/mime_type.rb +++ b/lib/redmine/mime_type.rb @@ -73,6 +73,16 @@ module Redmine end end + # Returns content_type, or the type guessed from name if content_type is + # undetermined, ie. blank or application/octet-stream + def self.infer_type(content_type, name) + if content_type.blank? || content_type == 'application/octet-stream' + of(name).presence || 'application/octet-stream' + else + content_type + end + end + # Returns the css class associated to # the mime type of name def self.css_class_of(name) diff --git a/test/unit/lib/redmine/mime_type_test.rb b/test/unit/lib/redmine/mime_type_test.rb index 934fc037b..50a44d82f 100644 --- a/test/unit/lib/redmine/mime_type_test.rb +++ b/test/unit/lib/redmine/mime_type_test.rb @@ -37,6 +37,24 @@ class Redmine::MimeTypeTest < ActiveSupport::TestCase assert_nil Redmine::MimeType.of('test.unk') end + def test_infer_type + to_test = { + # A determined type is returned as is, even if the filename suggests + # another type + ['text/html', 'not-really-an-image.png'] => 'text/html', + # An undetermined type is resolved from the filename + ['application/octet-stream', 'test.pdf'] => 'application/pdf', + ['', 'test.pdf'] => 'application/pdf', + [nil, 'test.pdf'] => 'application/pdf', + # application/octet-stream is kept when the filename does not help + ['application/octet-stream', 'test.unk'] => 'application/octet-stream', + [nil, 'test.unk'] => 'application/octet-stream', + } + to_test.each do |args, expected| + assert_equal expected, Redmine::MimeType.infer_type(*args) + end + end + def test_css_class_of to_test = { 'test.txt' => 'text-plain', -- 2.55.0