#9 Active Storage - thumbnails
This commit is contained in:
parent
15384c61e4
commit
187c65248f
1
Gemfile
1
Gemfile
@ -20,6 +20,7 @@
|
||||
source 'https://rubygems.org' do
|
||||
gem 'active_record_union'
|
||||
gem 'activestorage'
|
||||
gem 'image_processing', '~> 1.2'
|
||||
gem 'ox' # Dav4Rack
|
||||
gem 'rake' unless Dir.exist?(File.expand_path('../../redmine_dashboard', __FILE__))
|
||||
gem 'simple_enum'
|
||||
|
||||
@ -322,20 +322,6 @@ class DmsfFilesController < ApplicationController
|
||||
redirect_to trash_dmsf_path(@project)
|
||||
end
|
||||
|
||||
def thumbnail
|
||||
tbnail = @file.thumbnail(size: params[:size])
|
||||
if tbnail
|
||||
if stale?(etag: tbnail)
|
||||
send_file tbnail,
|
||||
filename: filename_for_content_disposition(@file.name),
|
||||
type: @file.last_revision.content_type,
|
||||
disposition: 'inline'
|
||||
end
|
||||
else
|
||||
head :not_found
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def find_file
|
||||
|
||||
@ -34,7 +34,7 @@ module DmsfHelper
|
||||
def self.sanitize_filename(filename)
|
||||
# Get only the filename, not the whole path
|
||||
just_filename = File.basename(filename.gsub('\\\\', '/'))
|
||||
# Replace all non alphanumeric, hyphens or periods with underscore
|
||||
# Replace all non-alphanumeric, hyphens or periods with underscore
|
||||
just_filename.gsub!(/[^\w.\-]/, '_')
|
||||
# Keep the extension if any
|
||||
if !/^[a-zA-Z0-9_.\-]*$/.match?(just_filename) && just_filename =~ /(.[a-zA-Z0-9]+)$/
|
||||
|
||||
@ -546,7 +546,7 @@ class DmsfFile < ApplicationRecord
|
||||
end
|
||||
|
||||
def thumbnailable?
|
||||
Redmine::Thumbnail.convert_available? && (image? || (pdf? && Redmine::Thumbnail.gs_available?))
|
||||
last_revision.file&.variable?
|
||||
end
|
||||
|
||||
def previewable?
|
||||
@ -632,29 +632,6 @@ class DmsfFile < ApplicationRecord
|
||||
nil
|
||||
end
|
||||
|
||||
def thumbnail(options = {})
|
||||
size = options[:size].to_i
|
||||
if size.positive?
|
||||
# Limit the number of thumbnails per image
|
||||
size = (size / 50) * 50
|
||||
# Maximum thumbnail size
|
||||
size = 800 if size > 800
|
||||
else
|
||||
size = Setting.thumbnails_size.to_i
|
||||
end
|
||||
size = 100 unless size.positive?
|
||||
target = File.join(Attachment.thumbnails_storage_path, "#{id}_#{last_revision.digest}_#{size}.thumb")
|
||||
begin
|
||||
Redmine::Thumbnail.generate last_revision.file.download, target, size, pdf?
|
||||
rescue StandardError => e
|
||||
Rails.logger.error do
|
||||
%(An error occured while generating thumbnail for #{last_revision.file&.blob&.filename} to #{target}\n
|
||||
Exception was: #{e.message})
|
||||
end
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def locked_title
|
||||
if locked_for_user?
|
||||
return l(:title_locked_by_user, user: lock.reverse[0].user) if lock.reverse[0].user
|
||||
|
||||
@ -25,12 +25,20 @@
|
||||
<div class="thumbnails">
|
||||
<% end %>
|
||||
<% images.each do |file| %>
|
||||
<div>
|
||||
<div class="thumbnail" title="<%= file.name %>">
|
||||
<% if link_to # Redmine classic %>
|
||||
<%= link_to image_tag(dmsf_thumbnail_path(file), alt: file.title), view_dmsf_file_url(file) %>
|
||||
<% size = Setting.thumbnails_size.to_i %>
|
||||
<%= link_to image_tag(file.last_revision&.file&.variant(resize_to_limit: [size, size]),
|
||||
alt: file.title,
|
||||
style: "max-width: #{size}px; max-height: #{size}px;",
|
||||
loading: 'lazy'),
|
||||
view_dmsf_file_url(file) %>
|
||||
<% else # jQuery gallery %>
|
||||
<%= image_tag(dmsf_thumbnail_path(file),
|
||||
{ :'data-fullsrc' => view_dmsf_file_url(file), alt: file.title }) %>
|
||||
<%= image_tag(file.last_revision&.file&.variant(resize_to_limit: [size, size]),
|
||||
:'data-fullsrc' => view_dmsf_file_url(file),
|
||||
alt: file.title,
|
||||
style: "max-width: #{size}px; max-height: #{size}px;",
|
||||
loading: 'lazy') %>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
@ -241,16 +241,22 @@ module RedmineDmsf
|
||||
{{dmsftn(file_id)}} -- with default height 200 (auto width)
|
||||
{{dmsftn(file_id1 file_id2 file_id3)}} -- multiple thumbnails
|
||||
{{dmsftn(file_id, size=300)}} -- with size 300x300
|
||||
{{dmsftn(file_id, height=300)}} -- with height (auto width)
|
||||
{{dmsftn(file_id, width=300)}} -- with width (auto height)
|
||||
{{dmsftn(file_id, height=300)}} -- with height (default width)
|
||||
{{dmsftn(file_id, width=300)}} -- with width (default height)
|
||||
{{dmsftn(file_id, size=640x480)}} -- with size 640x480}
|
||||
macro :dmsftn do |_obj, args|
|
||||
raise ArgumentError if args.empty? # Requires file id
|
||||
|
||||
args, options = extract_macro_options(args, :size, :width, :height, :title)
|
||||
size = options[:size]
|
||||
width = options[:width]
|
||||
height = options[:height]
|
||||
|
||||
if options[:size].present?
|
||||
width, height = options[:size].split('x')
|
||||
height = width if height.blank?
|
||||
else
|
||||
width = options[:width].presence || Setting.thumbnails_size.to_i
|
||||
height = options[:height].presence || Setting.thumbnails_size.to_i
|
||||
end
|
||||
|
||||
ids = args[0].split
|
||||
html = []
|
||||
ids.each do |id|
|
||||
@ -260,21 +266,17 @@ module RedmineDmsf
|
||||
next
|
||||
end
|
||||
raise ::I18n.t(:notice_not_authorized) unless User.current&.allowed_to?(:view_dmsf_files, file.project)
|
||||
raise ::I18n.t(:error_not_supported_image_format) unless file.image?
|
||||
raise ::I18n.t(:error_not_supported_image_format) unless file&.thumbnailable?
|
||||
|
||||
member = Member.find_by(user_id: User.current.id, project_id: file.project.id)
|
||||
filename = file.last_revision.formatted_name(member)
|
||||
url = static_dmsf_file_url(file, filename: filename)
|
||||
img = if size
|
||||
image_tag(url, alt: filename, title: file.title, size: size)
|
||||
elsif height
|
||||
image_tag(url, alt: filename, title: file.title, width: 'auto', height: height)
|
||||
elsif width
|
||||
image_tag(url, alt: filename, title: file.title, width: width, height: 'auto')
|
||||
else
|
||||
image_tag(url, alt: filename, title: file.title, width: 'auto', height: 200)
|
||||
end
|
||||
html << link_to(img, url,
|
||||
img = image_tag(file.last_revision&.file&.variant(resize_to_limit: [width, height]),
|
||||
alt: filename,
|
||||
style: "max-width: #{width}px; max-height: #{height}px;",
|
||||
loading: 'lazy')
|
||||
html << link_to(img,
|
||||
url,
|
||||
target: '_blank',
|
||||
rel: 'noopener',
|
||||
title: h(file.last_revision.try(:tooltip)),
|
||||
|
||||
@ -254,21 +254,23 @@ class DmsfMacrosTest < RedmineDmsf::Test::HelperTest
|
||||
size = '50%'
|
||||
url = static_dmsf_file_url(@file7, @file7.last_revision.name)
|
||||
text = textilizable("{{dmsf_image(#{@file7.id}, size=#{size})}}")
|
||||
assert text.include?(image_tag(url, alt: @file7.name, title: @file7.title, width: size, height: size)), text
|
||||
# TODO: arguments src and with and height are swapped
|
||||
assert_equal "<p>#{image_tag(url, alt: @file7.name, title: @file7.title, width: size, height: size)}</p>", text
|
||||
# TODO: Swaped parameters src and size
|
||||
# size = '300'
|
||||
# text = textilizable("{{dmsf_image(#{@file7.id}, size=#{size})}}")
|
||||
# assert text.include?(image_tag(url, alt: @file7.name, title: @file7.title, width: size, height: size)), text
|
||||
# TODO: arguments src and with and height are swapped
|
||||
# assert_equal "<p>#{image_tag(url, alt: @file7.name, title: @file7.title, width: size, height: size)}</p>", text
|
||||
# size = '640x480'
|
||||
# text = textilizable("{{dmsf_image(#{@file7.id}, size=#{size})}}")
|
||||
# assert text.include?(image_tag(url, alt: @file7.name, title: @file7.title, width: '640', height: '480')), text
|
||||
# assert_equal "<p>#{image_tag(url, alt: @file7.name, title: @file7.title, width: '640', height: '480')}</p>",
|
||||
# text
|
||||
height = '480'
|
||||
text = textilizable("{{dmsf_image(#{@file7.id}, height=#{height})}}")
|
||||
assert text.include?(image_tag(url, alt: @file7.name, title: @file7.title, width: 'auto', height: height)), text
|
||||
assert_equal "<p>#{image_tag(url, alt: @file7.name, title: @file7.title, width: 'auto', height: height)}</p>",
|
||||
text
|
||||
width = '480'
|
||||
text = textilizable("{{dmsf_image(#{@file7.id}, width=#{height})}}")
|
||||
assert text.include?(image_tag(url, alt: @file7.name, title: @file7.title, width: width, height: 'auto')), text
|
||||
assert_equal "<p>#{image_tag(url, alt: @file7.name, title: @file7.title, width: width, height: 'auto')}</p>",
|
||||
text
|
||||
end
|
||||
|
||||
def test_macro_dmsf_image_no_permissions
|
||||
@ -344,74 +346,86 @@ class DmsfMacrosTest < RedmineDmsf::Test::HelperTest
|
||||
def test_macro_dmsftn
|
||||
text = textilizable("{{dmsftn(#{@file7.id})}}")
|
||||
url = static_dmsf_file_url(@file7, @file7.last_revision.name)
|
||||
img = image_tag(url, alt: @file7.name, title: @file7.title, width: 'auto', height: 200)
|
||||
size = Setting.thumbnails_size.to_i
|
||||
img = image_tag(@file7.last_revision&.file&.variant(resize_to_limit: [size, size]),
|
||||
alt: @file7.name,
|
||||
style: "max-width: #{size}px; max-height: #{size}px;",
|
||||
loading: 'lazy')
|
||||
link = link_to(img,
|
||||
url,
|
||||
target: '_blank',
|
||||
rel: 'noopener',
|
||||
title: h(@file7.last_revision.try(:tooltip)),
|
||||
'data-downloadurl' => "#{@file7.last_revision.content_type}:#{h(@file7.name)}:#{url}")
|
||||
assert text.include?(link), text
|
||||
assert_equal "<p>#{link}</p>", text
|
||||
end
|
||||
|
||||
# {{dmsftn(file_id file_id)}}
|
||||
def test_macro_dmsftn_multiple
|
||||
text = textilizable("{{dmsftn(#{@file7.id} #{@file7.id})}}")
|
||||
url = static_dmsf_file_url(@file7, @file7.last_revision.name)
|
||||
img = image_tag(url, alt: @file7.name, title: @file7.title, width: 'auto', height: 200)
|
||||
img = image_tag(@file7.last_revision&.file&.variant(resize_to_limit: [100, 100]),
|
||||
alt: @file7.name,
|
||||
style: 'max-width: 100px; max-height: 100px;',
|
||||
loading: 'lazy')
|
||||
link = link_to(img,
|
||||
url,
|
||||
target: '_blank',
|
||||
rel: 'noopener',
|
||||
title: h(@file7.last_revision.try(:tooltip)),
|
||||
'data-downloadurl': 'image/gif:test.gif:http://www.example.com/dmsf/files/7/test.gif')
|
||||
assert text.include?(link + link), text
|
||||
assert_equal "<p>#{link}#{link}</p>", text
|
||||
end
|
||||
|
||||
# {{dmsftn(file_id size=300)}}
|
||||
def test_macro_dmsftn_size
|
||||
url = static_dmsf_file_url(@file7, @file7.last_revision.name)
|
||||
size = '300'
|
||||
text = textilizable("{{dmsftn(#{@file7.id}, size=#{size})}}")
|
||||
img = image_tag(url, alt: @file7.name, title: @file7.title, size: size)
|
||||
size = Setting.thumbnails_size.to_i
|
||||
|
||||
# Size
|
||||
text = textilizable("{{dmsftn(#{@file7.id}, size=300)}}")
|
||||
img = image_tag(@file7.last_revision&.file&.variant(resize_to_limit: [300, 300]),
|
||||
alt: @file7.name,
|
||||
style: 'max-width: 300px; max-height: 300px;',
|
||||
loading: 'lazy')
|
||||
link = link_to(img,
|
||||
url,
|
||||
target: '_blank',
|
||||
rel: 'noopener',
|
||||
title: h(@file7.last_revision.try(:tooltip)),
|
||||
'data-downloadurl' => "#{@file7.last_revision.content_type}:#{h(@file7.name)}:#{url}")
|
||||
assert text.include?(link), text
|
||||
# TODO: arguments src and with and height are swapped
|
||||
# size = '640x480'
|
||||
# text = textilizable("{{dmsftn(#{@file7.id}, size=#{size})}}")
|
||||
# img = image_tag(url, alt: @file7.name, title: @file7.title, width: 640, height: 480)
|
||||
# link = link_to(img,
|
||||
# url,
|
||||
# target: '_blank',
|
||||
# rel: 'noopener',
|
||||
# title: h(@file7.last_revision.try(:tooltip)),
|
||||
# 'data-downloadurl' => "#{@file7.last_revision.content_type}:#{h(@file7.name)}:#{url}")
|
||||
# assert text.include?(link), text
|
||||
height = '480'
|
||||
text = textilizable("{{dmsftn(#{@file7.id}, height=#{height})}}")
|
||||
img = image_tag(url, alt: @file7.name, title: @file7.title, width: 'auto', height: 480)
|
||||
link = link_to(img,
|
||||
url,
|
||||
target: '_blank',
|
||||
rel: 'noopener',
|
||||
title: h(@file7.last_revision.try(:tooltip)),
|
||||
'data-downloadurl': 'image/gif:test.gif:http://www.example.com/dmsf/files/7/test.gif')
|
||||
assert text.include?(link), text
|
||||
width = '640'
|
||||
text = textilizable("{{dmsftn(#{@file7.id}, width=#{width})}}")
|
||||
img = image_tag(url, alt: @file7.name, title: @file7.title, width: 640, height: 'auto')
|
||||
assert_equal "<p>#{link.gsub(/redirect\/.*\/#{@file7.name}/, '...')}</p>",
|
||||
text.gsub(/redirect\/.*\/#{@file7.name}/, '...')
|
||||
|
||||
# Height
|
||||
text = textilizable("{{dmsftn(#{@file7.id}, height=480)}}")
|
||||
img = image_tag(@file7.last_revision&.file&.variant(resize_to_limit: [size, 480]),
|
||||
alt: @file7.name,
|
||||
style: "max-width: #{size}px; max-height: 480px;",
|
||||
loading: 'lazy')
|
||||
link = link_to(img,
|
||||
url,
|
||||
target: '_blank',
|
||||
rel: 'noopener',
|
||||
title: h(@file7.last_revision.try(:tooltip)),
|
||||
'data-downloadurl' => "#{@file7.last_revision.content_type}:#{h(@file7.name)}:#{url}")
|
||||
assert text.include?(link), text
|
||||
assert_equal "<p>#{link.gsub(/redirect\/.*\/#{@file7.name}/, '...')}</p>",
|
||||
text.gsub(/redirect\/.*\/#{@file7.name}/, '...')
|
||||
|
||||
# Width
|
||||
text = textilizable("{{dmsftn(#{@file7.id}, width=640)}}")
|
||||
img = image_tag(@file7.last_revision&.file&.variant(resize_to_limit: [640, size]),
|
||||
alt: @file7.name,
|
||||
style: "max-width: 640px; max-height: #{size}px;",
|
||||
loading: 'lazy')
|
||||
link = link_to(img,
|
||||
url,
|
||||
target: '_blank',
|
||||
rel: 'noopener',
|
||||
title: h(@file7.last_revision.try(:tooltip)),
|
||||
'data-downloadurl' => "#{@file7.last_revision.content_type}:#{h(@file7.name)}:#{url}")
|
||||
assert_equal "<p>#{link.gsub(/redirect\/.*\/#{@file7.name}/, '...')}</p>",
|
||||
text.gsub(/redirect\/.*\/#{@file7.name}/, '...')
|
||||
end
|
||||
|
||||
def test_macro_dmsftn_no_permissions
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user