##// END OF EJS Templates
Adds a macro for inserting thumbnails in formatted text (#3510)....
Jean-Philippe Lang -
r9830:537be80be26c
parent child
Show More
@@ -61,7 +61,7 class AttachmentsController < ApplicationController
61 end
61 end
62
62
63 def thumbnail
63 def thumbnail
64 if @attachment.thumbnailable? && Setting.thumbnails_enabled? && thumbnail = @attachment.thumbnail
64 if @attachment.thumbnailable? && thumbnail = @attachment.thumbnail(:size => params[:size])
65 if stale?(:etag => thumbnail)
65 if stale?(:etag => thumbnail)
66 send_file thumbnail,
66 send_file thumbnail,
67 :filename => filename_for_content_disposition(@attachment.filename),
67 :filename => filename_for_content_disposition(@attachment.filename),
@@ -171,9 +171,17 class Attachment < ActiveRecord::Base
171
171
172 # Returns the full path the attachment thumbnail, or nil
172 # Returns the full path the attachment thumbnail, or nil
173 # if the thumbnail cannot be generated.
173 # if the thumbnail cannot be generated.
174 def thumbnail
174 def thumbnail(options={})
175 if thumbnailable? && readable?
175 if thumbnailable? && readable?
176 size = Setting.thumbnails_size.to_i
176 size = options[:size].to_i
177 if size > 0
178 # Limit the number of thumbnails per image
179 size = (size / 50) * 50
180 # Maximum thumbnail size
181 size = 800 if size > 800
182 else
183 size = Setting.thumbnails_size.to_i
184 end
177 size = 100 unless size > 0
185 size = 100 unless size > 0
178 target = File.join(self.class.thumbnails_storage_path, "#{id}_#{digest}_#{size}.thumb")
186 target = File.join(self.class.thumbnails_storage_path, "#{id}_#{digest}_#{size}.thumb")
179
187
@@ -264,7 +264,7 RedmineApp::Application.routes.draw do
264 match 'attachments/:id/:filename', :controller => 'attachments', :action => 'show', :id => /\d+/, :filename => /.*/, :via => :get
264 match 'attachments/:id/:filename', :controller => 'attachments', :action => 'show', :id => /\d+/, :filename => /.*/, :via => :get
265 match 'attachments/download/:id/:filename', :controller => 'attachments', :action => 'download', :id => /\d+/, :filename => /.*/, :via => :get
265 match 'attachments/download/:id/:filename', :controller => 'attachments', :action => 'download', :id => /\d+/, :filename => /.*/, :via => :get
266 match 'attachments/download/:id', :controller => 'attachments', :action => 'download', :id => /\d+/, :via => :get
266 match 'attachments/download/:id', :controller => 'attachments', :action => 'download', :id => /\d+/, :via => :get
267 match 'attachments/thumbnail/:id', :controller => 'attachments', :action => 'thumbnail', :id => /\d+/, :via => :get
267 match 'attachments/thumbnail/:id(/:size)', :controller => 'attachments', :action => 'thumbnail', :id => /\d+/, :via => :get, :size => /\d+/
268 resources :attachments, :only => [:show, :destroy]
268 resources :attachments, :only => [:show, :destroy]
269
269
270 resources :groups do
270 resources :groups do
@@ -116,6 +116,24 module Redmine
116 @included_wiki_pages.pop
116 @included_wiki_pages.pop
117 out
117 out
118 end
118 end
119
120 desc "Displays a clickable thumbnail of an attached image. Examples:\n\n<pre>{{thumbnail(image.png)}}\n{{thumbnail(image.png, size=300, title=Thumbnail)}}</pre>"
121 macro :thumbnail do |obj, args|
122 args, options = extract_macro_options(args, :size, :title)
123 filename = args.first
124 raise 'Filename required' unless filename.present?
125 size = options[:size]
126 raise 'Invalid size parameter' unless size.nil? || size.match(/^\d+$/)
127 size = size.to_i
128 size = nil unless size > 0
129 if obj && obj.respond_to?(:attachments) && attachment = Attachment.latest_attach(obj.attachments, filename)
130 title = options[:title] || attachment.title
131 img = image_tag(url_for(:controller => 'attachments', :action => 'thumbnail', :id => attachment, :size => size), :alt => attachment.filename)
132 link_to(img, url_for(:controller => 'attachments', :action => 'show', :id => attachment), :class => 'thumbnail', :title => title)
133 else
134 raise "Attachment #{filename} not found"
135 end
136 end
119 end
137 end
120 end
138 end
121 end
139 end
@@ -262,43 +262,44 class AttachmentsControllerTest < ActionController::TestCase
262 def test_thumbnail
262 def test_thumbnail
263 Attachment.clear_thumbnails
263 Attachment.clear_thumbnails
264 @request.session[:user_id] = 2
264 @request.session[:user_id] = 2
265 with_settings :thumbnails_enabled => '1' do
265
266 get :thumbnail, :id => 16
266 get :thumbnail, :id => 16
267 assert_response :success
267 assert_response :success
268 assert_equal 'image/png', response.content_type
268 assert_equal 'image/png', response.content_type
269 end
270 end
269 end
271
270
272 def test_thumbnail_should_return_404_for_non_image_attachment
271 def test_thumbnail_should_not_exceed_maximum_size
272 Redmine::Thumbnail.expects(:generate).with {|source, target, size| size == 800}
273
273 @request.session[:user_id] = 2
274 @request.session[:user_id] = 2
274 with_settings :thumbnails_enabled => '1' do
275 get :thumbnail, :id => 16, :size => 2000
275 get :thumbnail, :id => 15
276 assert_response 404
277 end
278 end
276 end
279
277
280 def test_thumbnail_should_return_404_if_thumbnails_not_enabled
278 def test_thumbnail_should_round_size
279 Redmine::Thumbnail.expects(:generate).with {|source, target, size| size == 250}
280
281 @request.session[:user_id] = 2
281 @request.session[:user_id] = 2
282 with_settings :thumbnails_enabled => '0' do
282 get :thumbnail, :id => 16, :size => 260
283 get :thumbnail, :id => 16
283 end
284 assert_response 404
284
285 end
285 def test_thumbnail_should_return_404_for_non_image_attachment
286 @request.session[:user_id] = 2
287
288 get :thumbnail, :id => 15
289 assert_response 404
286 end
290 end
287
291
288 def test_thumbnail_should_return_404_if_thumbnail_generation_failed
292 def test_thumbnail_should_return_404_if_thumbnail_generation_failed
289 Attachment.any_instance.stubs(:thumbnail).returns(nil)
293 Attachment.any_instance.stubs(:thumbnail).returns(nil)
290 @request.session[:user_id] = 2
294 @request.session[:user_id] = 2
291 with_settings :thumbnails_enabled => '1' do
295
292 get :thumbnail, :id => 16
296 get :thumbnail, :id => 16
293 assert_response 404
297 assert_response 404
294 end
295 end
298 end
296
299
297 def test_thumbnail_should_be_denied_without_permission
300 def test_thumbnail_should_be_denied_without_permission
298 with_settings :thumbnails_enabled => '1' do
301 get :thumbnail, :id => 16
299 get :thumbnail, :id => 16
302 assert_redirected_to '/login?back_url=http%3A%2F%2Ftest.host%2Fattachments%2Fthumbnail%2F16'
300 assert_redirected_to '/login?back_url=http%3A%2F%2Ftest.host%2Fattachments%2Fthumbnail%2F16'
301 end
302 end
303 end
303 else
304 else
304 puts '(ImageMagick convert not available)'
305 puts '(ImageMagick convert not available)'
@@ -50,6 +50,10 class RoutingAttachmentsTest < ActionController::IntegrationTest
50 { :controller => 'attachments', :action => 'thumbnail', :id => '1' }
50 { :controller => 'attachments', :action => 'thumbnail', :id => '1' }
51 )
51 )
52 assert_routing(
52 assert_routing(
53 { :method => 'get', :path => "/attachments/thumbnail/1/200" },
54 { :controller => 'attachments', :action => 'thumbnail', :id => '1', :size => '200' }
55 )
56 assert_routing(
53 { :method => 'delete', :path => "/attachments/1" },
57 { :method => 'delete', :path => "/attachments/1" },
54 { :controller => 'attachments', :action => 'destroy', :id => '1' }
58 { :controller => 'attachments', :action => 'destroy', :id => '1' }
55 )
59 )
@@ -119,4 +119,24 class Redmine::WikiFormatting::MacrosTest < ActionView::TestCase
119 def test_macro_child_pages_without_wiki_page_should_fail
119 def test_macro_child_pages_without_wiki_page_should_fail
120 assert_match /can be called from wiki pages only/, textilizable("{{child_pages}}")
120 assert_match /can be called from wiki pages only/, textilizable("{{child_pages}}")
121 end
121 end
122
123 def test_macro_thumbnail
124 assert_equal '<p><a href="/attachments/17" class="thumbnail" title="testfile.PNG"><img alt="testfile.PNG" src="/attachments/thumbnail/17" /></a></p>',
125 textilizable("{{thumbnail(testfile.png)}}", :object => Issue.find(14))
126 end
127
128 def test_macro_thumbnail_with_size
129 assert_equal '<p><a href="/attachments/17" class="thumbnail" title="testfile.PNG"><img alt="testfile.PNG" src="/attachments/thumbnail/17/200" /></a></p>',
130 textilizable("{{thumbnail(testfile.png, size=200)}}", :object => Issue.find(14))
131 end
132
133 def test_macro_thumbnail_with_title
134 assert_equal '<p><a href="/attachments/17" class="thumbnail" title="Cool image"><img alt="testfile.PNG" src="/attachments/thumbnail/17" /></a></p>',
135 textilizable("{{thumbnail(testfile.png, title=Cool image)}}", :object => Issue.find(14))
136 end
137
138 def test_macro_thumbnail_with_invalid_filename_should_fail
139 assert_include 'test.png not found',
140 textilizable("{{thumbnail(test.png)}}", :object => Issue.find(14))
141 end
122 end
142 end
General Comments 0
You need to be logged in to leave comments. Login now