--- config/configuration.yml.example | 11 ++- extra/pandoc_filter/markdownized_preview.lua | 14 ++++ lib/redmine/configuration.rb | 3 +- lib/redmine/markdownizer.rb | 11 ++- .../functional/attachments_controller_test.rb | 70 +++++++++++++++++++ 5 files changed, 106 insertions(+), 3 deletions(-) create mode 100644 extra/pandoc_filter/markdownized_preview.lua diff --git a/config/configuration.yml.example b/config/configuration.yml.example index 134a9a44c..642ea960f 100644 --- a/config/configuration.yml.example +++ b/config/configuration.yml.example @@ -264,7 +264,16 @@ default: # Larger output is truncated to this limit. # Default: 102400 (100 kilobytes) #markdownized_preview_max_output_size: 102400 - + # + # Controls how embedded images in Office document previews are handled. + # + # Available values: + # render - Extract embedded images and render them in the preview (default) + # replace - Replace images with a markdown placeholder generated by the + # Pandoc Lua filter + # + #markdownized_preview_images: render + # specific configuration options for production environment # that overrides the default ones production: diff --git a/extra/pandoc_filter/markdownized_preview.lua b/extra/pandoc_filter/markdownized_preview.lua new file mode 100644 index 000000000..1626341d5 --- /dev/null +++ b/extra/pandoc_filter/markdownized_preview.lua @@ -0,0 +1,14 @@ +image_count = 0 + +function Image(el) + image_count = image_count + 1 + + local alt = pandoc.utils.stringify(el.caption) + + if alt == "" then + local ext = el.src:match("%.([^.]+)$") or "img" + alt = image_count .. "." .. ext + end + + return pandoc.Str("[🖼️ " .. alt .. "] ") +end \ No newline at end of file diff --git a/lib/redmine/configuration.rb b/lib/redmine/configuration.rb index f479494a5..a1ac03780 100644 --- a/lib/redmine/configuration.rb +++ b/lib/redmine/configuration.rb @@ -31,7 +31,8 @@ module Redmine 'thumbnails_generation_timeout' => 10, 'markdownized_preview_generation_timeout' => 10, 'markdownized_preview_max_source_size' => 10.megabytes, - 'markdownized_preview_max_output_size' => 100.kilobytes + 'markdownized_preview_max_output_size' => 100.kilobytes, + 'markdownized_preview_images' => 'render' } @config = nil diff --git a/lib/redmine/markdownizer.rb b/lib/redmine/markdownizer.rb index 5fe3adfb5..08f94329e 100644 --- a/lib/redmine/markdownizer.rb +++ b/lib/redmine/markdownizer.rb @@ -30,6 +30,8 @@ module Redmine PREVIEW_GENERATION_TIMEOUT = Redmine::Configuration['markdownized_preview_generation_timeout'].to_i MAX_SOURCE_SIZE = Redmine::Configuration['markdownized_preview_max_source_size'].to_i MAX_OUTPUT_SIZE = Redmine::Configuration['markdownized_preview_max_output_size'].to_i + IMAGES_MODE = Redmine::Configuration['markdownized_preview_images'].to_s + LUA_FILTER = Rails.root.join("extra", "pandoc_filter", "markdownized_preview.lua").freeze def self.supports?(filename) markdownizable_extensions.include?(File.extname(filename.to_s).downcase) @@ -47,7 +49,14 @@ module Redmine directory = File.dirname(target) basedir = File.dirname(Attachment.markdownized_previews_storage_path) FileUtils.mkdir_p(directory) - args = [COMMAND, source, "-t", "gfm", "--extract-media=markdownized_previews/#{attachment_id}/"] + args = [COMMAND, source, "-t", "gfm"] + + if IMAGES_MODE == 'placeholder' + args << "--lua-filter=#{LUA_FILTER}" + else + args << "--extract-media=markdownized_previews/#{attachment_id}/" + end + pid = nil output = Tempfile.new('markdownized-preview') diff --git a/test/functional/attachments_controller_test.rb b/test/functional/attachments_controller_test.rb index d63011be3..fdf0991e8 100644 --- a/test/functional/attachments_controller_test.rb +++ b/test/functional/attachments_controller_test.rb @@ -380,6 +380,76 @@ class AttachmentsControllerTest < Redmine::ControllerTest assert_response :not_found end + def test_show_msword_with_replaced_images + skip unless Redmine::Markdownizer.available? + + old = Redmine::Markdownizer::IMAGES_MODE + Redmine::Markdownizer.send(:remove_const, :IMAGES_MODE) + Redmine::Markdownizer.const_set(:IMAGES_MODE, 'placeholder') + + begin + set_tmp_attachments_directory + + a = Attachment.new( + :container => Issue.find(1), + :file => uploaded_test_file( + 'msword.docx', + 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' + ), + :author => User.find(1) + ) + assert a.save + + get(:show, :params => {:id => a.id}) + + assert_response :success + assert_equal 'text/html', @response.media_type + + assert_select 'div.filecontent.wiki', :text => /Redmine is a flexible project management web application/ + assert_select 'div.filecontent.wiki img', 0 + assert_select 'div.filecontent.wiki', :text => /\[🖼️ 1\.png\]/ + children = Dir.children(a.markdownized_preview_directory) + assert_equal ['preview.md'], children + ensure + Redmine::Markdownizer.send(:remove_const, :IMAGES_MODE) + Redmine::Markdownizer.const_set(:IMAGES_MODE, old) + end + end + + def test_show_libreoffice_with_replaced_images + skip unless Redmine::Markdownizer.available? + + old = Redmine::Markdownizer::IMAGES_MODE + Redmine::Markdownizer.send(:remove_const, :IMAGES_MODE) + Redmine::Markdownizer.const_set(:IMAGES_MODE, 'placeholder') + + begin + set_tmp_attachments_directory + a = Attachment.new( + :container => Issue.find(1), + :file => uploaded_test_file( + 'libreoffice-writer.odt', + 'application/vnd.oasis.opendocument.text' + ), + :author => User.find(1) + ) + assert a.save + + get(:show, :params => {:id => a.id}) + + assert_response :success + assert_equal 'text/html', @response.media_type + assert_select 'div.filecontent.wiki', :text => /Redmine is a flexible project management web application/ + assert_select 'div.filecontent.wiki img', 0 + assert_select 'div.filecontent.wiki', :text => /\[🖼️ 1\.png\]/ + children = Dir.children(a.markdownized_preview_directory) + assert_equal ['preview.md'], children + ensure + Redmine::Markdownizer.send(:remove_const, :IMAGES_MODE) + Redmine::Markdownizer.const_set(:IMAGES_MODE, old) + end + end + def test_show_other_with_no_preview @request.session[:user_id] = 2 get(:show, :params => {:id => 6}) -- 2.43.0