Defect #44358 » 0001c-Store-images-of-markdownized-previews-as-attachments.patch
| app/models/attachment.rb | ||
|---|---|---|
| 19 | 19 | |
| 20 | 20 |
require "digest" |
| 21 | 21 |
require "fileutils" |
| 22 |
require "tmpdir" |
|
| 22 | 23 |
require "zip" |
| 23 | 24 | |
| 24 | 25 |
class Attachment < ApplicationRecord |
| 25 | 26 |
include Redmine::SafeAttributes |
| 26 | 27 |
belongs_to :container, :polymorphic => true |
| 27 | 28 |
belongs_to :author, :class_name => "User" |
| 29 |
MARKDOWNIZED_PREVIEW_IMAGE_PREFIX = 'markdownized_preview_image_' |
|
| 30 |
# Other attachments of this attachment are not images of the preview. |
|
| 31 |
scope :named_as_markdownized_preview_image, lambda {
|
|
| 32 |
where("#{table_name}.filename LIKE ?", "#{sanitize_sql_like(MARKDOWNIZED_PREVIEW_IMAGE_PREFIX)}%")
|
|
| 33 |
} |
|
| 34 |
has_many :markdownized_preview_images, lambda {named_as_markdownized_preview_image.order(:id)},
|
|
| 35 |
:class_name => 'Attachment', :as => :container, :dependent => :destroy, :inverse_of => :container |
|
| 28 | 36 | |
| 29 | 37 |
validates_presence_of :filename, :author |
| 30 | 38 |
validates_length_of :filename, :maximum => 255 |
| ... | ... | |
| 92 | 100 |
File.join(storage_path, 'derived_cache', 'markdownized_previews') |
| 93 | 101 |
end |
| 94 | 102 | |
| 103 |
MARKDOWNIZED_PREVIEW_IMAGE_REFERENCE = /(?<=src="|\]\()#{MARKDOWNIZED_PREVIEW_IMAGE_PREFIX}\d+\.[a-z]+\b/
|
|
| 104 | ||
| 95 | 105 |
before_create :set_content_type, :files_to_final_location |
| 96 | 106 |
before_update :reset_content_type, :if => :filename_changed? |
| 97 | 107 |
after_commit :delete_from_disk, :on => :destroy |
| ... | ... | |
| 247 | 257 |
end |
| 248 | 258 |
end |
| 249 | 259 | |
| 260 |
# The images of a markdownized preview are visible with the previewed |
|
| 261 |
# attachment. They are generated, so they can't be changed separately. |
|
| 262 |
def attachments_visible?(user=User.current) |
|
| 263 |
visible?(user) |
|
| 264 |
end |
|
| 265 | ||
| 266 |
def attachments_editable?(user=User.current) |
|
| 267 |
false |
|
| 268 |
end |
|
| 269 | ||
| 270 |
def attachments_deletable?(user=User.current) |
|
| 271 |
false |
|
| 272 |
end |
|
| 273 | ||
| 250 | 274 |
def image? |
| 251 | 275 |
!!(self.filename =~ /\.(avif|bmp|gif|jpg|jpe|jpeg|png|webp)$/i) |
| 252 | 276 |
end |
| ... | ... | |
| 303 | 327 |
Dir.glob(File.join(markdownized_previews_storage_path, "*.md")).each do |file| |
| 304 | 328 |
File.delete file |
| 305 | 329 |
end |
| 330 |
where(:container_type => name).named_as_markdownized_preview_image.find_each(&:destroy) |
|
| 306 | 331 |
end |
| 307 | 332 | |
| 308 | 333 |
def is_text? |
| ... | ... | |
| 358 | 383 |
return nil unless markdownized_previewable? |
| 359 | 384 | |
| 360 | 385 |
target = markdownized_preview_cache_path |
| 361 |
if Redmine::Markdownizer.convert(diskfile, target) |
|
| 362 |
File.read(target, :mode => "rb") |
|
| 363 |
end |
|
| 386 |
content = File.read(target, :mode => "rb") if File.exist?(target) |
|
| 387 |
return content if content && markdownized_preview_images_complete?(content) |
|
| 388 | ||
| 389 |
generate_markdownized_preview(target) |
|
| 364 | 390 |
rescue => e |
| 365 | 391 |
if logger |
| 366 | 392 |
logger.error( |
| ... | ... | |
| 600 | 626 |
# anymore, thats why this is caught and ignored as well. |
| 601 | 627 |
end |
| 602 | 628 | |
| 629 |
# The cached preview is shared by the attachments with the same file, but |
|
| 630 |
# each of them has its own images: another one may have created the preview |
|
| 631 |
def markdownized_preview_images_complete?(content) |
|
| 632 |
filenames = content.scan(MARKDOWNIZED_PREVIEW_IMAGE_REFERENCE).uniq |
|
| 633 |
(filenames - markdownized_preview_images.map(&:filename)).empty? |
|
| 634 |
end |
|
| 635 | ||
| 636 |
def generate_markdownized_preview(target) |
|
| 637 |
Dir.mktmpdir('markdownized-preview') do |directory|
|
|
| 638 |
next unless Redmine::Markdownizer.convert(diskfile, target, directory) |
|
| 639 | ||
| 640 |
content = File.read(target, :mode => "rb") |
|
| 641 |
# The lock is not held during the conversion, which can take a long time. |
|
| 642 |
# Another request may have stored the images in the meantime: they are |
|
| 643 |
# kept because pages may already display them. |
|
| 644 |
with_lock do |
|
| 645 |
next if markdownized_preview_images_complete?(content) |
|
| 646 | ||
| 647 |
markdownized_preview_images.destroy_all |
|
| 648 |
Dir.children(directory).sort.each do |filename| |
|
| 649 |
File.open(File.join(directory, filename), 'rb') do |file| |
|
| 650 |
# Validations are skipped because these are not uploaded files: |
|
| 651 |
# the size and extension limits are not meant for them |
|
| 652 |
image = markdownized_preview_images.build(:file => file, :filename => filename, :author => author) |
|
| 653 |
image.save(:validate => false) |
|
| 654 |
end |
|
| 655 |
end |
|
| 656 |
end |
|
| 657 |
content |
|
| 658 |
end |
|
| 659 |
end |
|
| 660 | ||
| 603 | 661 |
# Physically deletes the file from the file system |
| 604 | 662 |
def delete_from_disk! |
| 605 | 663 |
FileUtils.rm_f(diskfile) if disk_filename.present? |
| app/views/attachments/markdownized.html.erb | ||
|---|---|---|
| 1 | 1 |
<%= render :layout => 'layouts/file' do %> |
| 2 | 2 |
<%= render :partial => 'common/markup', |
| 3 |
:locals => {:markup_text_formatting => 'common_mark', :markup_text => @content} %>
|
|
| 3 |
:locals => {:markup_text_formatting => 'common_mark', :markup_text => @content,
|
|
| 4 |
:attachments => @attachment.markdownized_preview_images.to_a} %> |
|
| 4 | 5 |
<% end %> |
| app/views/common/_markup.html.erb | ||
|---|---|---|
| 1 | 1 |
<div class="filecontent wiki"> |
| 2 |
<%= Redmine::WikiFormatting.to_html(markup_text_formatting, Redmine::CodesetUtil.to_utf8_by_setting(markup_text)).html_safe %> |
|
| 2 |
<%= Redmine::WikiFormatting.to_html(markup_text_formatting, Redmine::CodesetUtil.to_utf8_by_setting(markup_text), :attachments => local_assigns[:attachments], :view => self, :only_path => true).html_safe %>
|
|
| 3 | 3 |
</div> |
| extra/pandoc_filter/markdownized_preview_images.lua | ||
|---|---|---|
| 1 |
-- Writes the images embedded in the document to the current directory, with |
|
| 2 |
-- names that do not depend on the document. Images that are not embedded in |
|
| 3 |
-- the document are never fetched, and formats that browsers cannot display |
|
| 4 |
-- are replaced with their description. |
|
| 5 |
local extensions = {
|
|
| 6 |
["image/avif"] = "avif", |
|
| 7 |
["image/bmp"] = "bmp", |
|
| 8 |
["image/gif"] = "gif", |
|
| 9 |
["image/jpeg"] = "jpg", |
|
| 10 |
["image/png"] = "png", |
|
| 11 |
["image/webp"] = "webp" |
|
| 12 |
} |
|
| 13 |
local filenames = {}
|
|
| 14 |
local count = 0 |
|
| 15 | ||
| 16 |
function Image(el) |
|
| 17 |
local mime, contents = pandoc.mediabag.lookup(el.src) |
|
| 18 |
local extension = extensions[mime] |
|
| 19 | ||
| 20 |
if not extension then |
|
| 21 |
local description = pandoc.utils.stringify(el.caption) |
|
| 22 |
return pandoc.Str("[" .. (description ~= "" and description or el.src:match("[^/]*$")) .. "]")
|
|
| 23 |
end |
|
| 24 | ||
| 25 |
if not filenames[el.src] then |
|
| 26 |
count = count + 1 |
|
| 27 |
filenames[el.src] = "markdownized_preview_image_" .. count .. "." .. extension |
|
| 28 |
local file = assert(io.open(filenames[el.src], "wb")) |
|
| 29 |
file:write(contents) |
|
| 30 |
file:close() |
|
| 31 |
end |
|
| 32 | ||
| 33 |
el.src = filenames[el.src] |
|
| 34 |
return el |
|
| 35 |
end |
|
| lib/redmine/markdownizer.rb | ||
|---|---|---|
| 30 | 30 |
PREVIEW_GENERATION_TIMEOUT = Redmine::Configuration['markdownized_preview_generation_timeout'].to_i |
| 31 | 31 |
MAX_SOURCE_SIZE = Redmine::Configuration['markdownized_preview_max_source_size'].to_i |
| 32 | 32 |
MAX_OUTPUT_SIZE = Redmine::Configuration['markdownized_preview_max_output_size'].to_i |
| 33 |
IMAGES_FILTER = Rails.root.join('extra', 'pandoc_filter', 'markdownized_preview_images.lua').to_s.freeze
|
|
| 33 | 34 | |
| 34 | 35 |
def self.supports?(filename) |
| 35 | 36 |
markdownizable_extensions.include?(File.extname(filename.to_s).downcase) |
| 36 | 37 |
end |
| 37 | 38 | |
| 38 |
def self.convert(source, target) |
|
| 39 |
def self.convert(source, target, media_directory)
|
|
| 39 | 40 |
return nil unless available? |
| 40 |
return target if File.exist?(target) |
|
| 41 | 41 | |
| 42 | 42 |
if File.size(source) > MAX_SOURCE_SIZE |
| 43 | 43 |
logger.warn("Markdownized preview generation skipped because source file is too large (#{File.size(source)} bytes): #{source}")
|
| ... | ... | |
| 46 | 46 | |
| 47 | 47 |
directory = File.dirname(target) |
| 48 | 48 |
FileUtils.mkdir_p(directory) |
| 49 |
args = [COMMAND, source, "-t", "gfm"]
|
|
| 49 |
args = [COMMAND, File.expand_path(source), "-t", "gfm", *image_options]
|
|
| 50 | 50 |
pid = nil |
| 51 | 51 |
output = Tempfile.new('markdownized-preview')
|
| 52 | 52 | |
| 53 | 53 |
begin |
| 54 | 54 |
Timeout.timeout(PREVIEW_GENERATION_TIMEOUT) do |
| 55 |
pid = Process.spawn(*args, out: output.path) |
|
| 55 |
pid = Process.spawn(*args, chdir: media_directory, out: output.path)
|
|
| 56 | 56 |
_, status = Process.wait2(pid) |
| 57 | 57 |
unless status.success? |
| 58 | 58 |
logger.error("Markdownized preview generation failed (#{status.exitstatus}):\nCommand: #{args.shelljoin}")
|
| ... | ... | |
| 80 | 80 |
output&.unlink |
| 81 | 81 |
end |
| 82 | 82 | |
| 83 |
def self.images_available? |
|
| 84 |
available? && (@pandoc_version <=> [2, 15]) >= 0 |
|
| 85 |
end |
|
| 86 | ||
| 87 |
def self.image_options |
|
| 88 |
images_available? ? ["--sandbox", "--quiet", "--lua-filter=#{IMAGES_FILTER}"] : []
|
|
| 89 |
end |
|
| 90 | ||
| 83 | 91 |
def self.available? |
| 84 | 92 |
return @available if defined?(@available) |
| 85 | 93 | |
| test/functional/attachments_controller_test.rb | ||
|---|---|---|
| 316 | 316 |
assert_select '.nodata', :count => 0 |
| 317 | 317 |
end |
| 318 | 318 | |
| 319 |
def test_show_msword_should_display_images_of_the_document |
|
| 320 |
skip unless Redmine::Markdownizer.images_available? |
|
| 321 | ||
| 322 |
set_tmp_attachments_directory |
|
| 323 |
a = Attachment.create!( |
|
| 324 |
:container => Issue.find(1), |
|
| 325 |
:file => uploaded_test_file( |
|
| 326 |
'msword.docx', |
|
| 327 |
'application/vnd.openxmlformats-officedocument.wordprocessingml.document' |
|
| 328 |
), |
|
| 329 |
:author => User.find(1) |
|
| 330 |
) |
|
| 331 | ||
| 332 |
get(:show, :params => {:id => a.id})
|
|
| 333 | ||
| 334 |
assert_response :success |
|
| 335 |
image = a.markdownized_preview_images.first |
|
| 336 |
assert_select "div.filecontent.wiki img[src=?]", download_named_attachment_path(image, image.filename) |
|
| 337 | ||
| 338 |
get(:download, :params => {:id => image.id})
|
|
| 339 |
assert_response :success |
|
| 340 |
assert_equal 'image/png', @response.media_type |
|
| 341 |
end |
|
| 342 | ||
| 343 |
def test_download_of_markdownized_preview_image_should_require_access_to_the_attachment |
|
| 344 |
skip unless Redmine::Markdownizer.images_available? |
|
| 345 | ||
| 346 |
set_tmp_attachments_directory |
|
| 347 |
a = Attachment.create!( |
|
| 348 |
:container => Issue.find(4), |
|
| 349 |
:file => uploaded_test_file( |
|
| 350 |
'msword.docx', |
|
| 351 |
'application/vnd.openxmlformats-officedocument.wordprocessingml.document' |
|
| 352 |
), |
|
| 353 |
:author => User.find(1) |
|
| 354 |
) |
|
| 355 |
a.markdownized_preview_content |
|
| 356 |
image = a.markdownized_preview_images.first |
|
| 357 | ||
| 358 |
@request.session[:user_id] = nil |
|
| 359 |
get(:download, :params => {:id => image.id})
|
|
| 360 |
assert_response :found |
|
| 361 | ||
| 362 |
@request.session[:user_id] = 2 |
|
| 363 |
get(:download, :params => {:id => image.id})
|
|
| 364 |
assert_response :success |
|
| 365 |
end |
|
| 366 | ||
| 319 | 367 |
def test_show_libreoffice_writer |
| 320 | 368 |
skip unless Redmine::Markdownizer.available? |
| 321 | 369 | |
| test/unit/attachment_test.rb | ||
|---|---|---|
| 692 | 692 |
assert_not File.exist?(preview) |
| 693 | 693 |
end |
| 694 | 694 | |
| 695 |
def docx_attachment |
|
| 696 |
Attachment.create!( |
|
| 697 |
:container => Issue.find(1), |
|
| 698 |
:file => uploaded_test_file( |
|
| 699 |
"msword.docx", |
|
| 700 |
"application/vnd.openxmlformats-officedocument.wordprocessingml.document" |
|
| 701 |
), |
|
| 702 |
:author => User.find(1) |
|
| 703 |
) |
|
| 704 |
end |
|
| 705 |
private :docx_attachment |
|
| 706 | ||
| 707 |
def test_markdownized_preview_content_should_store_images_as_attachments |
|
| 708 |
skip unless Redmine::Markdownizer.images_available? |
|
| 709 | ||
| 710 |
attachment = docx_attachment |
|
| 711 |
content = attachment.markdownized_preview_content |
|
| 712 | ||
| 713 |
assert_include 'src="markdownized_preview_image_1.png"', content |
|
| 714 |
assert_equal attachment.markdownized_preview_cache_path, |
|
| 715 |
File.join(Attachment.markdownized_previews_storage_path, "#{attachment.digest}_#{attachment.filesize}.md")
|
|
| 716 |
assert File.exist?(attachment.markdownized_preview_cache_path) |
|
| 717 |
image = attachment.reload.markdownized_preview_images.first |
|
| 718 |
assert_equal 'markdownized_preview_image_1.png', image.filename |
|
| 719 |
assert_equal 'image/png', image.content_type |
|
| 720 |
assert_equal attachment, image.container |
|
| 721 |
assert_equal attachment.project, image.project |
|
| 722 |
assert image.readable? |
|
| 723 |
end |
|
| 724 | ||
| 725 |
def test_markdownized_preview_content_should_use_the_cache |
|
| 726 |
skip unless Redmine::Markdownizer.images_available? |
|
| 727 | ||
| 728 |
attachment = docx_attachment |
|
| 729 |
content = attachment.markdownized_preview_content |
|
| 730 | ||
| 731 |
Redmine::Markdownizer.expects(:convert).never |
|
| 732 |
assert_equal content, attachment.reload.markdownized_preview_content |
|
| 733 |
assert_equal 1, attachment.markdownized_preview_images.count |
|
| 734 |
end |
|
| 735 | ||
| 736 |
def test_markdownized_preview_content_should_ignore_image_filenames_in_the_text |
|
| 737 |
attachment = docx_attachment |
|
| 738 |
FileUtils.mkdir_p(Attachment.markdownized_previews_storage_path) |
|
| 739 |
File.binwrite(attachment.markdownized_preview_cache_path, 'The image is markdownized_preview_image_7.png') |
|
| 740 | ||
| 741 |
Redmine::Markdownizer.expects(:convert).never |
|
| 742 |
assert_include 'The image is', attachment.markdownized_preview_content |
|
| 743 |
end |
|
| 744 | ||
| 745 |
def test_markdownized_preview_content_should_generate_missing_images |
|
| 746 |
attachment = docx_attachment |
|
| 747 |
FileUtils.mkdir_p(Attachment.markdownized_previews_storage_path) |
|
| 748 |
Redmine::Markdownizer.expects(:convert).twice.returns(nil) |
|
| 749 | ||
| 750 |
['', '<img src="markdownized_preview_image_1.jpg" />'].each do |content| |
|
| 751 |
File.binwrite(attachment.markdownized_preview_cache_path, content) |
|
| 752 |
assert_nil attachment.markdownized_preview_content |
|
| 753 |
end |
|
| 754 |
end |
|
| 755 | ||
| 756 |
def test_markdownized_preview_content_should_keep_images_stored_by_a_concurrent_request |
|
| 757 |
skip unless Redmine::Markdownizer.images_available? |
|
| 758 | ||
| 759 |
attachment = docx_attachment |
|
| 760 |
attachment.markdownized_preview_content |
|
| 761 |
image = attachment.reload.markdownized_preview_images.first |
|
| 762 | ||
| 763 |
# The other request found no cached preview before this one stored it |
|
| 764 |
Attachment.find(attachment.id).send(:generate_markdownized_preview, attachment.markdownized_preview_cache_path) |
|
| 765 |
assert_equal [image], attachment.reload.markdownized_preview_images.to_a |
|
| 766 |
end |
|
| 767 | ||
| 768 |
def test_markdownized_preview_content_should_not_convert_in_a_transaction |
|
| 769 |
skip unless Redmine::Markdownizer.available? |
|
| 770 | ||
| 771 |
attachment = docx_attachment |
|
| 772 |
transactions = Attachment.lease_connection.open_transactions |
|
| 773 |
transactions_during_conversion = nil |
|
| 774 |
Redmine::Markdownizer.expects(:convert).with do |
|
| 775 |
transactions_during_conversion = Attachment.lease_connection.open_transactions |
|
| 776 |
true |
|
| 777 |
end.returns(nil) |
|
| 778 | ||
| 779 |
assert_nil attachment.markdownized_preview_content |
|
| 780 |
assert_equal transactions, transactions_during_conversion |
|
| 781 |
end |
|
| 782 | ||
| 783 |
def test_markdownized_preview_content_should_store_images_for_each_attachment_with_the_same_file |
|
| 784 |
skip unless Redmine::Markdownizer.images_available? |
|
| 785 | ||
| 786 |
attachment = docx_attachment |
|
| 787 |
attachment.markdownized_preview_content |
|
| 788 |
copy = attachment.copy(:container => Issue.find(2)) |
|
| 789 |
copy.save! |
|
| 790 |
assert File.exist?(copy.markdownized_preview_cache_path) |
|
| 791 | ||
| 792 |
assert_include 'markdownized_preview_image_1.png', copy.markdownized_preview_content |
|
| 793 |
image = copy.reload.markdownized_preview_images.first |
|
| 794 |
assert_equal 'markdownized_preview_image_1.png', image.filename |
|
| 795 | ||
| 796 |
attachment.destroy |
|
| 797 |
assert image.reload.readable? |
|
| 798 |
end |
|
| 799 | ||
| 800 |
def test_markdownizer_should_export_images_only_with_the_sandbox |
|
| 801 |
skip unless Redmine::Markdownizer.available? |
|
| 802 | ||
| 803 |
version = Redmine::Markdownizer.instance_variable_get(:@pandoc_version) |
|
| 804 |
begin |
|
| 805 |
Redmine::Markdownizer.instance_variable_set(:@pandoc_version, [2, 14, 2]) |
|
| 806 |
assert_not Redmine::Markdownizer.images_available? |
|
| 807 |
assert_equal [], Redmine::Markdownizer.image_options |
|
| 808 |
Redmine::Markdownizer.instance_variable_set(:@pandoc_version, [2, 15]) |
|
| 809 |
assert Redmine::Markdownizer.images_available? |
|
| 810 |
assert_equal ['--sandbox', '--quiet', "--lua-filter=#{Redmine::Markdownizer::IMAGES_FILTER}"], Redmine::Markdownizer.image_options
|
|
| 811 |
ensure |
|
| 812 |
Redmine::Markdownizer.instance_variable_set(:@pandoc_version, version) |
|
| 813 |
end |
|
| 814 |
end |
|
| 815 | ||
| 816 |
def test_destroy_should_destroy_markdownized_preview_images |
|
| 817 |
skip unless Redmine::Markdownizer.images_available? |
|
| 818 | ||
| 819 |
attachment = docx_attachment |
|
| 820 |
attachment.markdownized_preview_content |
|
| 821 |
image = attachment.reload.markdownized_preview_images.first |
|
| 822 |
diskfile = image.diskfile |
|
| 823 | ||
| 824 |
assert_difference 'Attachment.count', -2 do |
|
| 825 |
attachment.destroy |
|
| 826 |
end |
|
| 827 |
assert_not File.exist?(diskfile) |
|
| 828 |
end |
|
| 829 | ||
| 830 |
def test_clear_markdownized_previews_should_destroy_only_preview_images |
|
| 831 |
skip unless Redmine::Markdownizer.images_available? |
|
| 832 | ||
| 833 |
attachment = docx_attachment |
|
| 834 |
attachment.markdownized_preview_content |
|
| 835 |
image = attachment.reload.markdownized_preview_images.first |
|
| 836 |
other = Attachment.create!( |
|
| 837 |
:container => attachment, |
|
| 838 |
:file => uploaded_test_file("testfile.txt", "text/plain"),
|
|
| 839 |
:filename => 'other.txt', |
|
| 840 |
:author => User.find(1) |
|
| 841 |
) |
|
| 842 | ||
| 843 |
Attachment.clear_markdownized_previews |
|
| 844 | ||
| 845 |
assert_not File.exist?(attachment.markdownized_preview_cache_path) |
|
| 846 |
assert_not Attachment.exists?(image.id) |
|
| 847 |
assert Attachment.exists?(attachment.id) |
|
| 848 |
assert Attachment.exists?(other.id) |
|
| 849 | ||
| 850 |
assert_include 'markdownized_preview_image_1.png', attachment.reload.markdownized_preview_content |
|
| 851 |
assert_equal ['markdownized_preview_image_1.png'], attachment.reload.markdownized_preview_images.map(&:filename) |
|
| 852 |
assert Attachment.exists?(other.id) |
|
| 853 |
end |
|
| 854 | ||
| 855 |
def test_markdownized_preview_images_should_have_the_permissions_of_the_attachment |
|
| 856 |
skip unless Redmine::Markdownizer.images_available? |
|
| 857 | ||
| 858 |
attachment = docx_attachment |
|
| 859 |
attachment.markdownized_preview_content |
|
| 860 |
image = attachment.reload.markdownized_preview_images.first |
|
| 861 | ||
| 862 |
assert image.visible?(User.find(2)) |
|
| 863 |
assert_equal attachment.visible?(User.anonymous), image.visible?(User.anonymous) |
|
| 864 |
assert_not image.editable?(User.find(1)) |
|
| 865 |
assert_not image.deletable?(User.find(1)) |
|
| 866 | ||
| 867 |
Issue.find(1).update_column(:is_private, true) |
|
| 868 |
attachment.reload.container.reload |
|
| 869 |
assert_not image.reload.visible?(User.find(7)) |
|
| 870 |
end |
|
| 871 | ||
| 872 |
def test_markdownized_preview_content_should_not_read_files_referenced_by_a_document |
|
| 873 |
skip unless Redmine::Markdownizer.images_available? |
|
| 874 | ||
| 875 |
attachment = Attachment.create!( |
|
| 876 |
:container => Issue.find(1), |
|
| 877 |
:file => uploaded_test_file( |
|
| 878 |
"libreoffice-writer-linked-image.odt", |
|
| 879 |
"application/vnd.oasis.opendocument.text" |
|
| 880 |
), |
|
| 881 |
:author => User.find(1) |
|
| 882 |
) |
|
| 883 | ||
| 884 |
content = attachment.markdownized_preview_content |
|
| 885 |
assert_include 'hostname', content |
|
| 886 |
assert_not_include 'src=', content |
|
| 887 |
assert_equal 0, attachment.reload.markdownized_preview_images.count |
|
| 888 |
end |
|
| 889 | ||
| 695 | 890 |
if convert_installed? |
| 696 | 891 |
def test_thumbnail_should_generate_the_thumbnail |
| 697 | 892 |
set_fixtures_attachments_directory |