Implement custom document file previewer [SCI-3677]

This commit is contained in:
Oleksii Kriuchykhin 2019-07-10 14:37:23 +02:00
parent e1185ff551
commit 64b7a5646a
17 changed files with 135 additions and 73 deletions

View file

@ -21,6 +21,7 @@ RUN curl -sL https://deb.nodesource.com/setup_8.x | bash - && \
default-jre-headless \
unison \
sudo graphviz --no-install-recommends \
poppler-utils \
libfile-mimeinfo-perl && \
apt-get install -y --no-install-recommends -t $(cat /tmp/lsb_release)-backports libreoffice && \
npm install -g yarn && \

View file

@ -18,6 +18,7 @@ RUN curl -sL https://deb.nodesource.com/setup_8.x | bash - && \
netcat \
default-jre-headless \
sudo graphviz --no-install-recommends \
poppler-utils \
libfile-mimeinfo-perl && \
apt-get install -y --no-install-recommends -t stretch-backports libreoffice && \
npm install -g yarn && \

View file

@ -270,7 +270,7 @@
function _uploadedAssetPreview(asset, i) {
var html = '<div class="attachment-placeholder pull-left new">';
html +='<div class="attachment-thumbnail no-shadow %>">';
html +='<div class="attachment-thumbnail no-shadow new %>">';
html +='<i class="fas fa-image"></i>';
html +='</div>';
html +='<div class="attachment-label">' + truncateLongString(asset.name, <%= Constants::FILENAME_TRUNCATION_LENGTH %>);

View file

@ -143,25 +143,6 @@
background-color: rgba(95, 95, 95, .1);
}
.attachment-thumbnail {
display: inline-block;
height: 160px;
margin: 16px 10px 5px;
overflow: hidden;
text-align: center;
img {
border-radius: 5px;
max-height: 100%;
max-width: 100%;
}
i.fas {
font-size: 100px;
line-height: 160px;
}
}
.no-shadow {
box-shadow: none;
}

View file

@ -1442,6 +1442,32 @@ table.dataTable {
}
}
.attachment-thumbnail {
display: inline-block;
height: 160px;
margin: 16px 10px 5px;
overflow: hidden;
text-align: center;
width: 100%;
img {
border-radius: 5px;
max-height: 100%;
max-width: 100%;
}
.fas {
font-size: 100px;
line-height: 160px;
}
&:not(.new) {
background-image: url("/images/medium/processing.gif");
background-position: center;
background-repeat: no-repeat;
}
}
// Image preview modal
.modal-file-preview {
background: transparent;
@ -1484,6 +1510,9 @@ table.dataTable {
.file-preview-container {
align-items: center;
background-image: url("/images/medium/processing.gif");
background-position: center;
background-repeat: no-repeat;
color: $gray-dark;
display: -moz-flex;
display: -webkit-flex;

View file

@ -1,9 +1,12 @@
# frozen_string_literal: true
class Asset < ApplicationRecord
include ActiveStorage::Downloading
include SearchableModel
include DatabaseHelper
include Encryptor
include WopiUtil
include ActiveStorageFileUtil
require 'tempfile'
# Lock duration set to 30 minutes
@ -191,27 +194,25 @@ class Asset < ApplicationRecord
def previewable?
return false unless file.attached?
previewable_document? || previewable_image?
previewable_document?(blob) || previewable_image?
end
def medium_preview
return file.variant(resize: Constants::MEDIUM_PIC_FORMAT) if previewable_image?
'/images/medium/processing.gif'
# file.preview(resize: Constants::MEDIUM_PIC_FORMAT)
file.preview(resize: Constants::MEDIUM_PIC_FORMAT)
end
def large_preview
return file.variant(resize: Constants::LARGE_PIC_FORMAT) if previewable_image?
'/images/large/processing.gif'
# file.preview(resize: Constants::LARGE_PIC_FORMAT)
file.preview(resize: Constants::LARGE_PIC_FORMAT)
end
def file_name
return '' unless file.attached?
file.blob&.filename&.to_s
file.blob&.filename&.sanitized
end
def file_size
@ -221,11 +222,9 @@ class Asset < ApplicationRecord
end
def content_type
file&.blob&.content_type
end
return '' unless file.attached?
def file_size
file&.blob&.byte_size
file&.blob&.content_type
end
def duplicate
@ -455,25 +454,6 @@ class Asset < ApplicationRecord
Rails.root.join('tmp')
end
def previewable_document?
previewable = Constants::PREVIEWABLE_FILE_TYPES.include?(file.content_type)
filename = file.filename.to_s
content_type = file.content_type
extensions = %w(.xlsx .docx .pptx .xls .doc .ppt)
# Mimetype sometimes recognizes Office files as zip files
# In this case we also check the extension of the given file
# Otherwise the conversion should fail if the file is being something else
previewable ||= (content_type == 'application/zip' && extensions.include?(File.extname(filename)))
# Mimetype also sometimes recognizes '.xls' and '.ppt' files as
# application/x-ole-storage (https://github.com/minad/mimemagic/issues/50)
previewable ||= (content_type == 'application/x-ole-storage' && %w(.xls .ppt).include?(File.extname(filename)))
previewable
end
def previewable_image?
file.blob&.content_type =~ %r{^image/#{Regexp.union(Constants::WHITELISTED_IMAGE_TYPES)}}
end

View file

@ -73,7 +73,7 @@ class TinyMceAsset < ApplicationRecord
def file_name
return '' unless image.attached?
image.blob&.filename&.to_s
image.blob&.filename&.sanitized
end
def file_size
@ -83,11 +83,9 @@ class TinyMceAsset < ApplicationRecord
end
def content_type
image&.blob&.content_type
end
return '' unless image.attached?
def file_size
image&.blob&.byte_size
image&.blob&.content_type
end
def preview
@ -140,7 +138,7 @@ class TinyMceAsset < ApplicationRecord
if exists?
order(:id).each do |tiny_mce_asset|
asset_guid = get_guid(tiny_mce_asset.id)
asset_file_name = "rte-#{asset_guid.to_s + File.extname(tiny_mce_asset.image.filename.to_s)}"
asset_file_name = "rte-#{asset_guid.to_s + tiny_mce_asset.image.blob.filename.extension}"
ostream.put_next_entry("#{dir}/#{asset_file_name}")
ostream.print(tiny_mce_asset.image.download)
input_file.close
@ -172,7 +170,7 @@ class TinyMceAsset < ApplicationRecord
tiny_img_clone.transaction do
tiny_img_clone.save!
tiny_img_clone.image.attach(io: image.download, filename: image.filename.to_s)
tiny_img_clone.image.attach(io: image.download, filename: image.filename.sanitized)
end
return false unless tiny_img_clone.persisted?

View file

@ -0,0 +1,51 @@
# frozen_string_literal: true
module ActiveStorage
class Previewer
class LibreofficePreviewer < Previewer
class << self
include ActiveStorageFileUtil
def accept?(blob)
previewable_document?(blob)
end
end
def preview
download_blob_to_tempfile do |input|
work_dir = File.dirname(input.path)
basename = File.basename(input.path, '.*')
preview_file = File.join(work_dir, "#{basename}.png")
Rails.logger.info "Starting preparing document preview for file #{blob.filename.sanitized}..."
begin
success = system(
"#{libreoffice_path} --headless --invisible --convert-to png --outdir #{work_dir} #{input.path}"
)
unless success && File.file?(preview_file)
raise StandardError, "There was an error generating document preview, blob id: #{blob.id}"
end
yield io: File.open(preview_file), filename: "#{blob.filename.base}.png", content_type: 'image/png'
Rails.logger.info "Finished preparing document preview for file #{blob.filename.sanitized}."
ensure
File.delete(preview_file) if File.file?(preview_file)
end
end
end
private
def tempdir
Rails.root.join('tmp')
end
def libreoffice_path
ENV['LIBREOFFICE_PATH'] || 'libreoffice'
end
end
end
end

View file

@ -0,0 +1,23 @@
# frozen_string_literal: true
module ActiveStorageFileUtil
# Method expects instance of ActiveStorage::Blob as argument
def previewable_document?(blob)
previewable = Constants::PREVIEWABLE_FILE_TYPES.include?(blob.content_type)
file_extension = blob.filename.extension
content_type = blob.content_type
extensions = %w(.xlsx .docx .pptx .xls .doc .ppt)
# Mimetype sometimes recognizes Office files as zip files
# In this case we also check the extension of the given file
# Otherwise the conversion should fail if the file is being something else
previewable ||= (content_type == 'application/zip' && extensions.include?(file_extension))
# Mimetype also sometimes recognizes '.xls' and '.ppt' files as
# application/x-ole-storage (https://github.com/minad/mimemagic/issues/50)
previewable ||= (content_type == 'application/x-ole-storage' && %w(.xls .ppt).include?(file_extension))
previewable
end
end

View file

@ -1,15 +1,9 @@
<%= link_to download_asset_path(asset),
class: 'file-preview-link',
id: "modal_link#{asset.id}",
data: { no_turbolink: true, id: true, status: 'asset-present', 'preview-url': asset_file_preview_path(asset) } do %>
<% if display_image_tag && asset.previewable? %>
<%= image_tag asset.medium_preview %>
<p>
<%= truncate(asset.file_name, length: Constants::FILENAME_TRUNCATION_LENGTH) %>
</p>
<% else %>
<span>
<%= truncate(asset.file_name, length: Constants::FILENAME_TRUNCATION_LENGTH) %>
</span>
<% end %>
data: { no_turbolink: true,
id: true,
status: 'asset-present',
'preview-url': asset_file_preview_path(asset) } do %>
<%= render partial: 'shared/asset_placeholder.html.erb', locals: { edit_page: false, asset: asset } %>
<% end %>

View file

@ -10,7 +10,7 @@
<%= truncate(asset.file_name, length: Constants::FILENAME_TRUNCATION_LENGTH) %>
</div>
<div class="spencer-bonnet-modif">
<%= t('protocols.steps.attachments.modified_label') %> <%= l(asset.updated_at, format: :full_date) if asset.updated_at %> <br>
<%= t('assets.placeholder.modified_label') %> <%= l(asset.updated_at, format: :full_date) if asset.updated_at %> <br>
<%= number_to_human_size(asset.file_size) %>
</div>

View file

@ -69,7 +69,7 @@
<div id="new-step-assets-group" class="form-group">
<div class="col-xs-12 attachments edit">
<%= f.nested_fields_for :assets do |ff| %>
<%= render partial: 'steps/attachments/placeholder.html.erb',
<%= render partial: 'shared/asset_placeholder.html.erb',
locals: { edit_page: true, asset: ff.object, ff: ff } %>
<% end %>
</div>

View file

@ -11,7 +11,6 @@
'order-new': assets_count - i,
} do %>
<%= render partial: 'steps/attachments/placeholder.html.erb',
locals: { edit_page: false, asset: asset } %>
<%= render partial: 'shared/asset_placeholder.html.erb', locals: { edit_page: false, asset: asset } %>
<% end %>
</div>

View file

@ -9,7 +9,7 @@ Bundler.require(*Rails.groups)
module Scinote
class Application < Rails::Application
# Initialize configuration defaults for originally generated Rails version.
config.load_defaults 5.1
config.load_defaults 5.2
# Settings in config/environments/* take precedence over those specified here.
# Application configuration should go into files in config/initializers

View file

@ -0,0 +1,4 @@
# frozen_string_literal: true
Rails.application.config.active_storage.previewers = [ActiveStorage::Previewer::PopplerPDFPreviewer,
ActiveStorage::Previewer::LibreofficePreviewer]

View file

@ -1763,7 +1763,6 @@ en:
sort_old: "Oldest first &#8593;"
sort_atoz: "Name &#8595;"
sort_ztoa: "Name &#8593;"
modified_label: "Modified:"
new:
add_step_title: "Add new step"
tab_checklists: "Checklists"
@ -1900,6 +1899,8 @@ en:
add_image: 'Add'
file_name: 'File name'
file_name_placeholder: 'Image'
placeholder:
modified_label: "Modified:"
wopi_supported_text_formats_title: 'Only .docx, .docm, .odt file formats are supported for editing in Word Online.'
wopi_supported_table_formats_title: 'Only .xlsx, .xlsm, .xlsb, .ods file formats are supported for editing in Excel Online.'
wopi_supported_presentation_formats_title: 'Only .pptx, ppsx, .odp file formats are supported for editing in Powerpoint Online.'