mirror of
https://github.com/scinote-eln/scinote-web.git
synced 2025-10-29 15:36:36 +08:00
Merge branch 'features/office-file-previews' of https://github.com/biosistemika/scinote-web into features/office-file-previews
This commit is contained in:
commit
016fc8a632
25 changed files with 457 additions and 414 deletions
|
|
@ -279,3 +279,10 @@ var HelperModule = (function(){
|
|||
function notTurbolinksPreview() {
|
||||
return !document.documentElement.hasAttribute("data-turbolinks-preview");
|
||||
}
|
||||
|
||||
const windowScrollEvents = {};
|
||||
$(window).scroll(function() {
|
||||
$.each(windowScrollEvents, function(key, scroll_function){
|
||||
scroll_function();
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -330,18 +330,20 @@
|
|||
}
|
||||
|
||||
function initPreviewToggle(){
|
||||
$('.attachment-placeholder').on('click', '.dataPreviewAction', function (e) {
|
||||
var view_mode = $(this).data('preview-type');
|
||||
var asset_id = $(this).data('asset-id');
|
||||
$('.attachments').on('click', '.change-preview-type', function (e) {
|
||||
var viewMode = $(this).data('preview-type');
|
||||
var assetId = $(this).data('asset-id');
|
||||
e.preventDefault();
|
||||
|
||||
e.stopPropagation();
|
||||
$.ajax({
|
||||
url: `/files/${asset_id}/toggle_view_mode`,
|
||||
url: `/files/${assetId}/toggle_view_mode`,
|
||||
type: "PATCH",
|
||||
dataType: "json",
|
||||
data: { asset: { view_mode: view_mode }},
|
||||
data: { asset: { view_mode: viewMode }},
|
||||
success: function (data) {
|
||||
console.log(data);
|
||||
$(`.step-asset[data-asset-id=${assetId}]`).replaceWith(data.html);
|
||||
FilePreviewModal.init();
|
||||
$(`.step-asset[data-asset-id=${assetId}]`).closest('.attachments').trigger('reorder');
|
||||
},
|
||||
error: function (data) {
|
||||
console.log(data);
|
||||
|
|
@ -359,6 +361,7 @@
|
|||
initDeleteStep();
|
||||
TinyMCE.highlight();
|
||||
initPreviewToggle();
|
||||
reorderAttachmentsInit();
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -643,20 +646,50 @@
|
|||
}
|
||||
|
||||
// Reorder attachments
|
||||
global.reorderAttachments = function reorderAtt(elem, stepId, sortType) {
|
||||
var label_value = $("#dd-att-step-" + stepId + "> .dropdown-menu > li > a[data-order=" + sortType + "]").html();
|
||||
$("#dd-att-step-" + stepId + "-label").html(label_value);
|
||||
$('#att-' + stepId + ' a.file-preview-link').each(function(){
|
||||
var elm = $(this)
|
||||
elm.parent().css('order', elm.attr('data-order-' + sortType));
|
||||
});
|
||||
function reorderAttachmentsInit() {
|
||||
$('.attachments-order').on('click', '.change-order', function(e){
|
||||
var orderDropdown = $(this).closest('.attachments-order');
|
||||
var assetsContainer = $(`.attachments[data-step-id=${orderDropdown.data('step-id')}]`)
|
||||
var order = $(this).data('order');
|
||||
e.preventDefault();
|
||||
orderDropdown.find('.selected-order').html($(this).text());
|
||||
assetsContainer.data('order', order);
|
||||
assetsContainer.trigger('reorder');
|
||||
$.post(orderDropdown.data('state-save-path'), {
|
||||
assets: { order: order }
|
||||
});
|
||||
})
|
||||
|
||||
$.post(
|
||||
$(elem).closest('.dropdown-menu').data('stateSavePath'),
|
||||
{ assets: { order: sortType } },
|
||||
null,
|
||||
'json',
|
||||
);
|
||||
$('.attachments').on('reorder', function() {
|
||||
var assets = $(`.attachments[data-step-id=${$(this).data('step-id')}] .step-asset`);
|
||||
var order = $(this).data('order');
|
||||
var sortedAssets = assets.sort(function(a, b) {
|
||||
if (a.dataset.assetOrder == b.dataset.assetOrder) {
|
||||
if (order == 'new') {
|
||||
return b.dataset.assetUpdatedAt - a.dataset.assetUpdatedAt;
|
||||
} if (order == 'old') {
|
||||
return a.dataset.assetUpdatedAt - b.dataset.assetUpdatedAt;
|
||||
} if (order == 'atoz') {
|
||||
return a.dataset.assetFileName.toLowerCase() > b.dataset.assetFileName.toLowerCase() ? 1 : -1;
|
||||
} if (order == 'ztoa') {
|
||||
return b.dataset.assetFileName.toLowerCase() > a.dataset.assetFileName.toLowerCase() ? 1 : -1;
|
||||
}
|
||||
}
|
||||
|
||||
return a.dataset.assetOrder > b.dataset.assetOrder ? 1 : -1
|
||||
})
|
||||
|
||||
$.each(sortedAssets, function(i, element){
|
||||
element.style.order = i
|
||||
})
|
||||
})
|
||||
$('.attachments').trigger('reorder');
|
||||
}
|
||||
|
||||
function initInlineAttachment() {
|
||||
$(document).on('scroll', 'body', function(){
|
||||
console.log(1)
|
||||
})
|
||||
}
|
||||
|
||||
// On init
|
||||
|
|
@ -677,3 +710,61 @@
|
|||
|
||||
global.initHandsOnTable = initHandsOnTable;
|
||||
})(window);
|
||||
|
||||
|
||||
var StepInlineAttachments = (function() {
|
||||
function elementVisible(element) {
|
||||
var elementRect = element.getBoundingClientRect();
|
||||
var elementHeight = $(element).height()
|
||||
return elementRect.top + (elementHeight / 2) >= 0 &&
|
||||
elementRect.bottom <= (window.innerHeight || document.documentElement.clientHeight) + (elementHeight / 2)
|
||||
}
|
||||
|
||||
function showElement(element) {
|
||||
setTimeout(() => {
|
||||
var iframeUrl = $(element).find('.iframe-placeholder').data('iframe-url');
|
||||
if (elementVisible(element) && iframeUrl) {
|
||||
$(element).find('.iframe-placeholder')
|
||||
.replaceWith(`<iframe class="active-iframe-preview" src="${iframeUrl}"></iframe>`);
|
||||
$(element).addClass('active').attr('data-created-at', new Date().getTime());
|
||||
}
|
||||
},500)
|
||||
}
|
||||
|
||||
function hideElement(element) {
|
||||
var iframeUrl = $(element).find('.active-iframe-preview').attr('src');
|
||||
if (!elementVisible(element) && iframeUrl) {
|
||||
$(element).find('iframe')
|
||||
.replaceWith(`<div class="iframe-placeholder" data-iframe-url="${iframeUrl}"></div>`);
|
||||
$(element).removeClass('active').attr('data-created-at', null)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
function checkForAttachmentsState() {
|
||||
$.each($('.inline-attachment-container'), function(i, element){
|
||||
showElement(element)
|
||||
})
|
||||
if ($('.active-iframe-preview').length > 5){
|
||||
let sortedIframes = $('.inline-attachment-container.active').sort(function(a, b) {
|
||||
return +a.dataset.createdAt - +b.dataset.createdAt;
|
||||
})
|
||||
$.each(sortedIframes, function(i, element){
|
||||
if (hideElement(element)) return false
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
init: () => {
|
||||
windowScrollEvents['StepInlineAttachments'] = StepInlineAttachments.scrollEvent;
|
||||
},
|
||||
scrollEvent: () => {
|
||||
checkForAttachmentsState()
|
||||
}
|
||||
}
|
||||
|
||||
})();
|
||||
|
||||
StepInlineAttachments.init();
|
||||
|
|
|
|||
|
|
@ -288,14 +288,14 @@
|
|||
}
|
||||
|
||||
function uploadedAssetPreview(asset, i) {
|
||||
var html = `<div class="attachment-placeholder pull-left new">
|
||||
<div class="attachment-thumbnail no-shadow new %>">
|
||||
var html = `<div class="attachment-container pull-left new">
|
||||
<div class="attachment-preview no-shadow new %>">
|
||||
<i class="fas fa-image"></i>
|
||||
</div>
|
||||
<div class="attachment-label">
|
||||
${truncateLongString(asset.name, GLOBAL_CONSTANTS.FILENAME_TRUNCATION_LENGTH)}
|
||||
</div>
|
||||
<div class="spencer-bonnet-modif"></div>
|
||||
<div class="attachment-metadata"></div>
|
||||
<div class="remove-icon pull-right">
|
||||
<a data-item-id="${i}" href="#">
|
||||
<span class="fas fa-trash"></span>
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ var FilePreviewModal = (function() {
|
|||
|
||||
$('.file-preview-link').off('click');
|
||||
$('.file-preview-link').click(function(e) {
|
||||
if ($(e.target.offsetParent).hasClass('change-preview-type')) return;
|
||||
e.preventDefault();
|
||||
name = $(this).find('.attachment-label').text();
|
||||
url = $(this).data('preview-url');
|
||||
|
|
|
|||
|
|
@ -5,10 +5,17 @@
|
|||
// scss-lint:disable NestingDepth
|
||||
// scss-lint:disable SelectorFormat
|
||||
// scss-lint:disable ImportantRule
|
||||
// scss-lint:disable Unknown
|
||||
|
||||
@import "constants";
|
||||
@import "mixins";
|
||||
|
||||
:root {
|
||||
--attachment-card-thumbnail-height: 280px;
|
||||
--attachment-card-thumbnail-width: 220px;
|
||||
}
|
||||
|
||||
|
||||
#new_step,
|
||||
.panel-step-attachment {
|
||||
ul {
|
||||
|
|
@ -128,71 +135,51 @@
|
|||
|
||||
.attachments {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
|
||||
|
||||
.pseudo-attachment-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
|
||||
.file-preview-link {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
&.new {
|
||||
order: 0 !important;
|
||||
|
||||
.file-preview-link {
|
||||
transition: .5s;
|
||||
}
|
||||
|
||||
.attachment-placeholder {
|
||||
border: 1px solid $brand-primary;
|
||||
|
||||
&::before {
|
||||
background: $brand-primary;
|
||||
border-radius: 0 5px;
|
||||
bottom: 16px;
|
||||
color: $color-white;
|
||||
content: "NEW";
|
||||
left: 8px;
|
||||
line-height: 20px;
|
||||
position: absolute;
|
||||
width: 50px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
grid-auto-rows: var(--attachment-card-thumbnail-height);
|
||||
grid-column-gap: 1em;
|
||||
grid-row-gap: 1em;
|
||||
grid-template-columns: repeat(auto-fill, minmax(var(--attachment-card-thumbnail-width), 1fr));
|
||||
justify-content: center;
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
.attachment-placeholder {
|
||||
.attachment-container {
|
||||
@include md-card-style;
|
||||
color: $color-silver-chalice;
|
||||
height: 280px;
|
||||
margin: 4px 8px 16px;
|
||||
text-align: center;
|
||||
width: 220px;
|
||||
height: var(--attachment-card-thumbnail-height);
|
||||
justify-self: center;
|
||||
overflow: hidden;
|
||||
padding: 1em;
|
||||
position: relative;
|
||||
width: var(--attachment-card-thumbnail-width);
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
|
||||
.file-preview-link {
|
||||
display: block;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&.new {
|
||||
background-color: rgba(95, 95, 95, .1);
|
||||
.show-inline {
|
||||
background: $color-white;
|
||||
border-radius: $border-radius-default;
|
||||
line-height: 1em;
|
||||
padding: .5em;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
text-align: center;
|
||||
top: 0;
|
||||
width: 2em;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.attachment-thumbnail {
|
||||
display: inline-block;
|
||||
height: 160px;
|
||||
margin: 16px 0 5px;
|
||||
overflow: hidden;
|
||||
padding: 0 10px;
|
||||
.attachment-preview {
|
||||
border-radius: $border-radius-default;
|
||||
height: 170px;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
|
||||
img {
|
||||
border-radius: 5px;
|
||||
max-height: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
|
@ -201,15 +188,15 @@
|
|||
|
||||
&::before,
|
||||
&::after {
|
||||
border-radius: 1em 0 0 1em;
|
||||
bottom: 1em;
|
||||
content: "";
|
||||
border-radius: 16px 0 0 16px;
|
||||
display: block;
|
||||
height: 32px;
|
||||
right: 0;
|
||||
line-height: 32px;
|
||||
height: 2em;
|
||||
line-height: 2em;
|
||||
position: absolute;
|
||||
bottom: 10px;
|
||||
width: 36px;
|
||||
right: -1em;
|
||||
width: 2.25em;
|
||||
}
|
||||
|
||||
&::before {
|
||||
|
|
@ -218,8 +205,9 @@
|
|||
|
||||
&::after {
|
||||
background-image: url("/images/icon_small/marvinjs-white.svg");
|
||||
right: 4px;
|
||||
width: 32px;
|
||||
height: 2.25em;
|
||||
right: -.75em;
|
||||
width: 2em;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -233,70 +221,59 @@
|
|||
box-shadow: none;
|
||||
}
|
||||
|
||||
.attachment-label {
|
||||
.attachment-label,
|
||||
.attachment-metadata {
|
||||
background: $color-white;
|
||||
color: $brand-primary;
|
||||
font-family: Lato;
|
||||
font-size: 16px;
|
||||
height: 40px;
|
||||
line-height: 18px;
|
||||
margin: 0 auto;
|
||||
overflow: hidden;
|
||||
padding-top: 1em;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
top: 20px;
|
||||
text-overflow: ellipsis;
|
||||
transition: $md-transaction;
|
||||
width: 190px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.attachment-label {
|
||||
@include font-main;
|
||||
margin-top: 1.5em;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.spencer-bonnet-modif {
|
||||
align-items: center;
|
||||
.attachment-metadata {
|
||||
@include font-small;
|
||||
color: $color-silver-chalice;
|
||||
display: flex;
|
||||
font-family: Lato;
|
||||
font-size: 12px;
|
||||
height: 40px;
|
||||
justify-content: center;
|
||||
line-height: 15px;
|
||||
margin: 0 auto 5px;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
top: -20px;
|
||||
transition: $md-transaction;
|
||||
width: 149px;
|
||||
margin-top: -4em;
|
||||
}
|
||||
|
||||
.remove-icon {
|
||||
bottom: 15px;
|
||||
bottom: .5em;
|
||||
cursor: pointer;
|
||||
display: none;
|
||||
position: relative;
|
||||
right: 10px;
|
||||
position: absolute;
|
||||
right: .5em;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
box-shadow: $md-shadow;
|
||||
|
||||
.file-preview-link {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
|
||||
.remove-icon {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.attachment-label {
|
||||
top: 0;
|
||||
}
|
||||
.attachment-label,
|
||||
.attachment-metadata {
|
||||
margin-top: 0;
|
||||
|
||||
.spencer-bonnet-modif {
|
||||
top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.new {
|
||||
background-color: rgba(95, 95, 95, .1);
|
||||
|
||||
.attachment-label {
|
||||
background-color: transparent;
|
||||
}
|
||||
border: 1px solid $brand-primary;
|
||||
|
||||
.dnd-error {
|
||||
bottom: 15px;
|
||||
|
|
@ -305,10 +282,18 @@
|
|||
position: relative;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.attachment-label {
|
||||
top: 20px;
|
||||
}
|
||||
&::before {
|
||||
background: $brand-primary;
|
||||
border-radius: 0 $border-radius-default;
|
||||
bottom: 0;
|
||||
color: $color-white;
|
||||
content: "NEW";
|
||||
left: 0;
|
||||
line-height: 20px;
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
width: 50px;
|
||||
z-index: 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -354,3 +339,40 @@
|
|||
.expand-all-steps {
|
||||
margin: 0 0 15px 15px;
|
||||
}
|
||||
|
||||
.inline-attachment-container {
|
||||
@include md-card-style;
|
||||
grid-column: 1/-1;
|
||||
grid-row: span 2;
|
||||
|
||||
.active-iframe-preview {
|
||||
border: 0;
|
||||
height: calc(100% - 4em);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.header {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
height: 4em;
|
||||
padding: 0 1em;
|
||||
|
||||
.show-as-thumbnail {
|
||||
cursor: pointer;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.file-name {
|
||||
@include font-main;
|
||||
color: $brand-primary;
|
||||
}
|
||||
|
||||
.file-metadata {
|
||||
@include font-small;
|
||||
color: $color-silver-chalice;
|
||||
display: grid;
|
||||
grid-column-gap: 1em;
|
||||
grid-template-columns: max-content max-content;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1458,31 +1458,6 @@ ul.content-activities {
|
|||
}
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
&.processing {
|
||||
background-image: url("/images/medium/processing.gif");
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
}
|
||||
|
||||
// Image preview modal
|
||||
.modal-file-preview {
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ class AssetsController < ApplicationController
|
|||
|
||||
before_action :load_vars, except: :create_wopi_file
|
||||
before_action :check_read_permission, except: :edit
|
||||
before_action :check_edit_permission, only: :edit
|
||||
before_action :check_edit_permission, only: %i(edit toggle_view_mode)
|
||||
|
||||
def file_preview
|
||||
file_type = @asset.file.metadata[:asset_type] || (@asset.previewable? ? 'previewable' : false)
|
||||
|
|
@ -98,19 +98,13 @@ class AssetsController < ApplicationController
|
|||
end
|
||||
|
||||
def toggle_view_mode
|
||||
# view_mode: card / inline
|
||||
# @asset.update!(view_mode: toggle_view_mode_params[:view_mode])
|
||||
|
||||
html = if @asset.inline_card && wopi_enabled? && wopi_file?(@asset)
|
||||
url = @asset.get_action_url(current_user, 'embedview')
|
||||
"<iframe src=\"#{url}\" title=\"DocumentPreview\"></iframe>"
|
||||
else
|
||||
render_to_string(partial: 'shared/asset_placeholder.html.erb', locals: { asset: @asset, edit_page: false })
|
||||
end
|
||||
|
||||
respond_to do |format|
|
||||
format.json do
|
||||
render json: { html: html }, status: :ok
|
||||
@asset.view_mode = toggle_view_mode_params[:view_mode]
|
||||
if @asset.save(touch: false)
|
||||
html = render_to_string(partial: 'assets/asset.html.erb', locals: { asset: @asset })
|
||||
respond_to do |format|
|
||||
format.json do
|
||||
render json: { html: html }, status: :ok
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -168,34 +162,10 @@ class AssetsController < ApplicationController
|
|||
@asset.post_process_file(@asset.team)
|
||||
@asset.step&.protocol&.update(updated_at: Time.now)
|
||||
|
||||
render_html = if @asset.step
|
||||
assets = @asset.step.assets
|
||||
order_atoz = az_ordered_assets_index(@asset.step, @asset.id)
|
||||
order_ztoa = assets.length - az_ordered_assets_index(@asset.step, @asset.id)
|
||||
asset_position = @asset.step.asset_position(@asset)
|
||||
render_html = if @asset.step || @asset.result
|
||||
render_to_string(
|
||||
partial: 'steps/attachments/item.html.erb',
|
||||
locals: {
|
||||
asset: @asset,
|
||||
i: asset_position[:pos],
|
||||
assets_count: asset_position[:count],
|
||||
step: @asset.step,
|
||||
order_atoz: order_atoz,
|
||||
order_ztoa: order_ztoa
|
||||
},
|
||||
formats: :html
|
||||
)
|
||||
elsif @asset.result
|
||||
render_to_string(
|
||||
partial: 'steps/attachments/item.html.erb',
|
||||
locals: {
|
||||
asset: @asset,
|
||||
i: 0,
|
||||
assets_count: 0,
|
||||
step: nil,
|
||||
order_atoz: 0,
|
||||
order_ztoa: 0
|
||||
},
|
||||
partial: 'assets/asset.html.erb',
|
||||
locals: { asset: @asset },
|
||||
formats: :html
|
||||
)
|
||||
else
|
||||
|
|
@ -230,7 +200,7 @@ class AssetsController < ApplicationController
|
|||
unless asset.valid?(:wopi_file_creation)
|
||||
render json: {
|
||||
message: asset.errors
|
||||
}, status: 400 and return
|
||||
}, status: :bad_request and return
|
||||
end
|
||||
|
||||
# Create file depending on the type
|
||||
|
|
@ -270,7 +240,7 @@ class AssetsController < ApplicationController
|
|||
private
|
||||
|
||||
def load_vars
|
||||
@asset = Asset.find_by_id(params[:id])
|
||||
@asset = Asset.find_by(id: params[:id])
|
||||
return render_404 unless @asset
|
||||
|
||||
@assoc ||= @asset.step
|
||||
|
|
|
|||
|
|
@ -17,15 +17,7 @@ class MarvinJsAssetsController < ApplicationController
|
|||
|
||||
if result[:asset] && marvin_params[:object_type] == 'Step'
|
||||
render json: {
|
||||
html: render_to_string(
|
||||
partial: 'steps/attachments/item.html.erb',
|
||||
locals: { asset: result[:asset],
|
||||
i: 0,
|
||||
assets_count: 0,
|
||||
step: result[:object],
|
||||
order_atoz: 0,
|
||||
order_ztoa: 0 }
|
||||
)
|
||||
html: render_to_string(partial: 'assets/asset.html.erb', locals: { asset: result[:asset] })
|
||||
}
|
||||
elsif result[:asset] && marvin_params[:object_type] == 'Result'
|
||||
@my_module = result[:object].my_module
|
||||
|
|
@ -63,7 +55,7 @@ class MarvinJsAssetsController < ApplicationController
|
|||
private
|
||||
|
||||
def load_vars
|
||||
@asset = current_team.assets.find_by_id(params[:id])
|
||||
@asset = current_team.assets.find_by(id: params[:id])
|
||||
return render_404 unless @asset
|
||||
|
||||
@assoc ||= @asset.step
|
||||
|
|
@ -77,8 +69,8 @@ class MarvinJsAssetsController < ApplicationController
|
|||
end
|
||||
|
||||
def load_create_vars
|
||||
@assoc = Step.find_by_id(marvin_params[:object_id]) if marvin_params[:object_type] == 'Step'
|
||||
@assoc = MyModule.find_by_id(params[:object_id]) if marvin_params[:object_type] == 'Result'
|
||||
@assoc = Step.find_by(id: marvin_params[:object_id]) if marvin_params[:object_type] == 'Step'
|
||||
@assoc = MyModule.find_by(id: params[:object_id]) if marvin_params[:object_type] == 'Result'
|
||||
|
||||
if @assoc.class == Step
|
||||
@protocol = @assoc.protocol
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
class WopiController < ActionController::Base
|
||||
# frozen_string_literal: true
|
||||
|
||||
class WopiController < ApplicationController
|
||||
include WopiUtil
|
||||
|
||||
skip_before_action :verify_authenticity_token
|
||||
|
|
@ -334,7 +336,7 @@ class WopiController < ActionController::Base
|
|||
url = request.original_url.upcase.encode('utf-8')
|
||||
|
||||
if convert_to_unix_timestamp(timestamp) + 20.minutes >= Time.now
|
||||
if current_wopi_discovery.verify_proof(token, timestamp, signed_proof, signed_proof_old, url)
|
||||
if wopi_verify_proof(token, timestamp, signed_proof, signed_proof_old, url)
|
||||
logger.warn 'WOPI: proof verification: successful'
|
||||
else
|
||||
logger.warn 'WOPI: proof verification: not verified'
|
||||
|
|
|
|||
|
|
@ -317,7 +317,7 @@ class Asset < ApplicationRecord
|
|||
file_ext = file_name.split('.').last
|
||||
action = get_action(file_ext, action)
|
||||
if !action.nil?
|
||||
action_url = action.urlsrc
|
||||
action_url = action[:urlsrc]
|
||||
if ENV['WOPI_BUSINESS_USERS'] && ENV['WOPI_BUSINESS_USERS'] == 'true'
|
||||
action_url = action_url.gsub(/<IsLicensedUser=BUSINESS_USER&>/,
|
||||
'IsLicensedUser=1&')
|
||||
|
|
@ -351,7 +351,7 @@ class Asset < ApplicationRecord
|
|||
def favicon_url(action)
|
||||
file_ext = file_name.split('.').last
|
||||
action = get_action(file_ext, action)
|
||||
action.wopi_app.icon if action.try(:wopi_app)
|
||||
action[:icon] if action[:icon]
|
||||
end
|
||||
|
||||
# locked?, lock_asset and refresh_lock rely on the asset
|
||||
|
|
@ -402,14 +402,6 @@ class Asset < ApplicationRecord
|
|||
return convert_variant_to_base64(medium_preview) if style == :medium
|
||||
end
|
||||
|
||||
def small_card
|
||||
false
|
||||
end
|
||||
|
||||
def inline_card
|
||||
!small_card
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def tempdir
|
||||
|
|
|
|||
|
|
@ -4,9 +4,4 @@ class WopiAction < ApplicationRecord
|
|||
belongs_to :wopi_app, foreign_key: 'wopi_app_id', class_name: 'WopiApp'
|
||||
|
||||
validates :action, :extension, :urlsrc, :wopi_app, presence: true
|
||||
|
||||
def self.find_action(extension, activity)
|
||||
WopiAction.distinct
|
||||
.where('extension = ? and action = ?', extension, activity).first
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -13,43 +13,4 @@ class WopiDiscovery < ApplicationRecord
|
|||
:proof_key_old_mod,
|
||||
:proof_key_old_exp,
|
||||
presence: true
|
||||
|
||||
# Verifies if proof from headers, X-WOPI-Proof/X-WOPI-OldProof was encrypted
|
||||
# with this discovery public key (two key possible old/new)
|
||||
def verify_proof(token, timestamp, signed_proof, signed_proof_old, url)
|
||||
token_length = [token.length].pack('>N').bytes
|
||||
timestamp_bytes = [timestamp.to_i].pack('>Q').bytes.reverse
|
||||
timestamp_length = [timestamp_bytes.length].pack('>N').bytes
|
||||
url_length = [url.length].pack('>N').bytes
|
||||
|
||||
expected_proof = token_length + token.bytes +
|
||||
url_length + url.upcase.bytes +
|
||||
timestamp_length + timestamp_bytes
|
||||
|
||||
key = generate_key(proof_key_mod, proof_key_exp)
|
||||
old_key = generate_key(proof_key_old_mod, proof_key_old_exp)
|
||||
|
||||
# Try all possible combiniations
|
||||
try_verification(expected_proof, signed_proof, key) ||
|
||||
try_verification(expected_proof, signed_proof_old, key) ||
|
||||
try_verification(expected_proof, signed_proof, old_key)
|
||||
end
|
||||
|
||||
# Generates a public key from given modulus and exponent
|
||||
def generate_key(modulus, exponent)
|
||||
mod = Base64.decode64(modulus).unpack('H*').first.to_i(16)
|
||||
exp = Base64.decode64(exponent).unpack('H*').first.to_i(16)
|
||||
|
||||
seq = OpenSSL::ASN1::Sequence.new([OpenSSL::ASN1::Integer.new(mod),
|
||||
OpenSSL::ASN1::Integer.new(exp)])
|
||||
OpenSSL::PKey::RSA.new(seq.to_der)
|
||||
end
|
||||
|
||||
# Verify if decrypting signed_proof with public_key equals to expected_proof
|
||||
def try_verification(expected_proof, signed_proof_b64, public_key)
|
||||
signed_proof = Base64.decode64(signed_proof_b64)
|
||||
public_key.verify(OpenSSL::Digest::SHA256.new, signed_proof,
|
||||
expected_proof.pack('c*'))
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module WopiUtil
|
||||
require 'open-uri'
|
||||
|
||||
|
|
@ -5,72 +7,101 @@ module WopiUtil
|
|||
UNIX_EPOCH_IN_CLR_TICKS = 621355968000000000
|
||||
CLR_TICKS_PER_SECOND = 10000000
|
||||
|
||||
DISCOVERY_TTL = 1.days
|
||||
DISCOVERY_TTL = 1.day
|
||||
DISCOVERY_TTL.freeze
|
||||
|
||||
# For more explanation see this:
|
||||
# http://stackoverflow.com/questions/11888053/
|
||||
# convert-net-datetime-ticks-property-to-date-in-objective-c
|
||||
def convert_to_unix_timestamp(timestamp)
|
||||
Time.at((timestamp - UNIX_EPOCH_IN_CLR_TICKS) / CLR_TICKS_PER_SECOND)
|
||||
Time.zone.at((timestamp - UNIX_EPOCH_IN_CLR_TICKS) / CLR_TICKS_PER_SECOND)
|
||||
end
|
||||
|
||||
def get_action(extension, activity)
|
||||
current_wopi_discovery
|
||||
WopiAction.find_action(extension, activity)
|
||||
def get_action(extension, action)
|
||||
discovery = current_wopi_discovery
|
||||
discovery[:actions].find { |i| i[:extension] == extension && i[:action] == action }
|
||||
end
|
||||
|
||||
def current_wopi_discovery
|
||||
discovery = WopiDiscovery.first
|
||||
return discovery if discovery && discovery.expires >= Time.now.to_i
|
||||
initialize_discovery(discovery)
|
||||
initialize_discovery
|
||||
end
|
||||
|
||||
# Verifies if proof from headers, X-WOPI-Proof/X-WOPI-OldProof was encrypted
|
||||
# with this discovery public key (two key possible old/new)
|
||||
def wopi_verify_proof(token, timestamp, signed_proof, signed_proof_old, url)
|
||||
discovery = current_wopi_discovery
|
||||
token_length = [token.length].pack('>N').bytes
|
||||
timestamp_bytes = [timestamp.to_i].pack('>Q').bytes.reverse
|
||||
timestamp_length = [timestamp_bytes.length].pack('>N').bytes
|
||||
url_length = [url.length].pack('>N').bytes
|
||||
|
||||
expected_proof = token_length + token.bytes +
|
||||
url_length + url.upcase.bytes +
|
||||
timestamp_length + timestamp_bytes
|
||||
|
||||
key = generate_key(discovery[:proof_key_mod], discovery[:proof_key_exp])
|
||||
old_key = generate_key(discovery[:proof_key_old_mod], discovery[:proof_key_old_exp])
|
||||
|
||||
# Try all possible combiniations
|
||||
try_verification(expected_proof, signed_proof, key) ||
|
||||
try_verification(expected_proof, signed_proof_old, key) ||
|
||||
try_verification(expected_proof, signed_proof, old_key)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Currently only saves Excel, Word and PowerPoint view and edit actions
|
||||
def initialize_discovery(discovery)
|
||||
Rails.logger.warn 'Initializing discovery'
|
||||
discovery.destroy if discovery
|
||||
def initialize_discovery
|
||||
Rails.cache.fetch(:wopi_discovery, expires_in: DISCOVERY_TTL) do
|
||||
@doc = Nokogiri::XML(Kernel.open(ENV['WOPI_DISCOVERY_URL']))
|
||||
discovery_json = {}
|
||||
key = @doc.xpath('//proof-key')
|
||||
discovery_json[:proof_key_mod] = key.xpath('@modulus').first.value
|
||||
discovery_json[:proof_key_exp] = key.xpath('@exponent').first.value
|
||||
discovery_json[:proof_key_old_mod] = key.xpath('@oldmodulus').first.value
|
||||
discovery_json[:proof_key_old_exp] = key.xpath('@oldexponent').first.value
|
||||
discovery_json[:actions] = []
|
||||
|
||||
@doc = Nokogiri::XML(Kernel.open(ENV['WOPI_DISCOVERY_URL']))
|
||||
@doc.xpath('//app').each do |app|
|
||||
app_name = app.xpath('@name').first.value
|
||||
next unless %w(Excel Word PowerPoint WopiTest).include?(app_name)
|
||||
|
||||
discovery = WopiDiscovery.new
|
||||
discovery.expires = Time.now.to_i + DISCOVERY_TTL
|
||||
key = @doc.xpath('//proof-key')
|
||||
discovery.proof_key_mod = key.xpath('@modulus').first.value
|
||||
discovery.proof_key_exp = key.xpath('@exponent').first.value
|
||||
discovery.proof_key_old_mod = key.xpath('@oldmodulus').first.value
|
||||
discovery.proof_key_old_exp = key.xpath('@oldexponent').first.value
|
||||
discovery.save!
|
||||
icon = app.xpath('@favIconUrl').first.value
|
||||
|
||||
@doc.xpath('//app').each do |app|
|
||||
app_name = app.xpath('@name').first.value
|
||||
next unless %w(Excel Word PowerPoint WopiTest).include?(app_name)
|
||||
app.xpath('action').each do |action|
|
||||
action_name = action.xpath('@name').first.value
|
||||
next unless %w(view edit editnew embedview wopitest).include?(action_name)
|
||||
|
||||
wopi_app = WopiApp.new
|
||||
wopi_app.name = app.xpath('@name').first.value
|
||||
wopi_app.icon = app.xpath('@favIconUrl').first.value
|
||||
wopi_app.wopi_discovery_id = discovery.id
|
||||
wopi_app.save!
|
||||
app.xpath('action').each do |action|
|
||||
name = action.xpath('@name').first.value
|
||||
next unless %w(view edit editnew embedview wopitest).include?(name)
|
||||
|
||||
wopi_action = WopiAction.new
|
||||
wopi_action.action = name
|
||||
wopi_action.extension = action.xpath('@ext').first.value
|
||||
wopi_action.urlsrc = action.xpath('@urlsrc').first.value
|
||||
wopi_action.wopi_app_id = wopi_app.id
|
||||
wopi_action.save!
|
||||
action_json = {}
|
||||
action_json[:icon] = icon
|
||||
action_json[:action] = action_name
|
||||
action_json[:extension] = action.xpath('@ext').first.value
|
||||
action_json[:urlsrc] = action.xpath('@urlsrc').first.value
|
||||
discovery_json[:actions].push(action_json)
|
||||
end
|
||||
end
|
||||
discovery_json
|
||||
end
|
||||
discovery
|
||||
rescue => e
|
||||
rescue StandardError => e
|
||||
Rails.logger.warn 'WOPI: initialization failed: ' + e.message
|
||||
e.backtrace.each { |line| Rails.logger.error line }
|
||||
discovery = WopiDiscovery.first
|
||||
discovery.destroy if discovery
|
||||
end
|
||||
|
||||
# Generates a public key from given modulus and exponent
|
||||
def generate_key(modulus, exponent)
|
||||
mod = Base64.decode64(modulus).unpack1('H*').to_i(16)
|
||||
exp = Base64.decode64(exponent).unpack1('H*').to_i(16)
|
||||
|
||||
seq = OpenSSL::ASN1::Sequence.new([OpenSSL::ASN1::Integer.new(mod),
|
||||
OpenSSL::ASN1::Integer.new(exp)])
|
||||
OpenSSL::PKey::RSA.new(seq.to_der)
|
||||
end
|
||||
|
||||
# Verify if decrypting signed_proof with public_key equals to expected_proof
|
||||
def try_verification(expected_proof, signed_proof_b64, public_key)
|
||||
signed_proof = Base64.decode64(signed_proof_b64)
|
||||
public_key.verify(OpenSSL::Digest::SHA256.new, signed_proof,
|
||||
expected_proof.pack('c*'))
|
||||
end
|
||||
|
||||
def create_wopi_file_activity(current_user, started_editing)
|
||||
|
|
|
|||
5
app/views/assets/_asset.html.erb
Normal file
5
app/views/assets/_asset.html.erb
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<% if wopi_enabled? && wopi_file?(asset) && asset.show_inline? %>
|
||||
<%= render partial: 'assets/asset_inline.html.erb', locals: { asset: asset } %>
|
||||
<% else %>
|
||||
<%= render partial: 'assets/asset_thumbnail.html.erb', locals: { edit_page: false, asset: asset } %>
|
||||
<% end %>
|
||||
25
app/views/assets/_asset_inline.html.erb
Normal file
25
app/views/assets/_asset_inline.html.erb
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
<div class="inline-attachment-container step-asset"
|
||||
data-asset-id="<%= asset.id %>"
|
||||
data-asset-file-name="<%= asset.file_name %>"
|
||||
data-asset-updated-at="<%= asset.updated_at.to_i %>"
|
||||
data-asset-order="0"
|
||||
>
|
||||
<div class="header">
|
||||
<div class="file-info">
|
||||
<div class="file-name">
|
||||
<%= asset.file_name %>
|
||||
</div>
|
||||
<div class="file-metadata">
|
||||
<span><%= t('assets.placeholder.modified_label') %> <%= l(asset.updated_at, format: :full_date) if asset.updated_at %></span>
|
||||
<span><%= number_to_human_size(asset.file_size) %></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="show-as-thumbnail change-preview-type"
|
||||
data-preview-type="show_as_thumbnail"
|
||||
data-asset-id="<%= asset.id %>" >
|
||||
<i class="fas fa-compress"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="iframe-placeholder" data-iframe-url="<%= asset.get_action_url(current_user, 'embedview') %>"></div>
|
||||
|
||||
</div>
|
||||
40
app/views/assets/_asset_thumbnail.html.erb
Normal file
40
app/views/assets/_asset_thumbnail.html.erb
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
<div class="attachment-container step-asset"
|
||||
data-asset-id="<%= asset.id %>"
|
||||
data-asset-file-name="<%= asset.file_name %>"
|
||||
data-asset-updated-at="<%= asset.updated_at.to_i %>"
|
||||
data-asset-order="1"
|
||||
>
|
||||
<%= link_to rails_blob_path(asset.file, disposition: 'attachment'),
|
||||
class: "file-preview-link",
|
||||
id: "modal_link#{asset.id}",
|
||||
data: { no_turbolink: true, id: true, 'preview-url': asset_file_preview_path(asset)} do %>
|
||||
<% if wopi_enabled? && wopi_file?(asset) %>
|
||||
<div class="show-inline change-preview-type"
|
||||
data-preview-type="show_inline"
|
||||
data-asset-id="<%= asset.id %>">
|
||||
<i class="fas fa-expand"></i>
|
||||
</div>
|
||||
<% end %>
|
||||
<div class="attachment-preview <%= asset.file.attached? ? asset.file.metadata['asset_type'] : '' %>">
|
||||
<% if asset.previewable? %>
|
||||
<%= image_tag asset.medium_preview,
|
||||
onerror: 'ActiveStoragePreviews.reCheckPreview(event)',
|
||||
onload: 'ActiveStoragePreviews.showPreview(event)',
|
||||
style: 'opacity: 0' %>
|
||||
<% else %>
|
||||
<i class="fas <%= file_fa_icon_class(asset) if asset.file_name %>"></i>
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="attachment-label">
|
||||
<% if asset.file.attached? && asset&.file&.metadata['asset_type'] %>
|
||||
<%= asset.file.metadata['name'] %>
|
||||
<% else %>
|
||||
<%= asset.file_name %>
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="attachment-metadata">
|
||||
<%= 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>
|
||||
<% end %>
|
||||
</div>
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
<div class="attachment-placeholder pull-left new">
|
||||
<div class="attachment-thumbnail no-shadow">
|
||||
<div class="attachment-container pull-left new">
|
||||
<div class="attachment-preview no-shadow">
|
||||
<i class="fas fa-image"></i>
|
||||
</div>
|
||||
<div class="attachment-label"><%= truncate(file_name || file_url, length: Constants::FILENAME_TRUNCATION_LENGTH) %>
|
||||
</div>
|
||||
<div class="spencer-bonnet-modif">
|
||||
<div class="attachment-metadata">
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -3,6 +3,5 @@
|
|||
<% elsif result.is_table %>
|
||||
<%= render partial: "results/result_table.html.erb", locals: {result: result} %>
|
||||
<% elsif result.is_asset %>
|
||||
<%= render partial: 'steps/attachments/item.html.erb',
|
||||
locals: { asset: result.asset, i: 0, assets_count: 0, step: nil, order_atoz: 0, order_ztoa: 0 } %>
|
||||
<%= render partial: 'assets/asset.html.erb', locals: { asset: result.asset } %>
|
||||
<% end %>
|
||||
|
|
|
|||
|
|
@ -1,34 +0,0 @@
|
|||
<div class="attachment-placeholder pull-left">
|
||||
<a href="#" class="dataPreviewAction" data-asset-id="<%= asset.id %>" data-preview-type="inline">InLine</a>
|
||||
<div class="attachment-thumbnail <%= asset.previewable? ? '' : 'no-shadow' %> <%= asset.file.attached? ? asset.file.metadata['asset_type'] : '' %>">
|
||||
<% if asset.previewable? %>
|
||||
<%= image_tag asset.medium_preview,
|
||||
onerror: 'ActiveStoragePreviews.reCheckPreview(event)',
|
||||
onload: 'ActiveStoragePreviews.showPreview(event)',
|
||||
style: 'opacity: 0' %>
|
||||
<% else %>
|
||||
<i class="fas <%= file_fa_icon_class(asset) if asset.file_name %>"></i>
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="attachment-label">
|
||||
<% if asset.file.attached? && asset&.file&.metadata['asset_type'] %>
|
||||
<%= truncate(asset.file.metadata['name'], length: Constants::FILENAME_TRUNCATION_LENGTH) %>
|
||||
<% else %>
|
||||
<%= truncate(asset.file_name, length: Constants::FILENAME_TRUNCATION_LENGTH) %>
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="spencer-bonnet-modif">
|
||||
<%= 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>
|
||||
|
||||
<% if edit_page %>
|
||||
<div class="remove-icon pull-right">
|
||||
<% unless ff.object.file.attached? && ff.object.locked? %>
|
||||
<%= ff.remove_nested_fields_link do %>
|
||||
<span class="fas fa-trash"></span>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
|
@ -72,8 +72,8 @@
|
|||
<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: 'shared/asset_placeholder.html.erb',
|
||||
locals: { edit_page: true, asset: ff.object, ff: ff } %>
|
||||
<%= render partial: 'steps/attachments/edit_item.html.erb',
|
||||
locals: { asset: ff.object, ff: ff } %>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
30
app/views/steps/attachments/_edit_item.html.erb
Normal file
30
app/views/steps/attachments/_edit_item.html.erb
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
<div class="attachment-container step-asset" data-asset-id="<%= asset.id %>">
|
||||
<div class="attachment-preview <%= asset.file.attached? ? asset.file.metadata['asset_type'] : '' %>">
|
||||
<% if asset.previewable? %>
|
||||
<%= image_tag asset.medium_preview,
|
||||
onerror: 'ActiveStoragePreviews.reCheckPreview(event)',
|
||||
onload: 'ActiveStoragePreviews.showPreview(event)',
|
||||
style: 'opacity: 0' %>
|
||||
<% else %>
|
||||
<i class="fas <%= file_fa_icon_class(asset) if asset.file_name %>"></i>
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="attachment-label">
|
||||
<% if asset.file.attached? && asset&.file&.metadata['asset_type'] %>
|
||||
<%= truncate(asset.file.metadata['name'], length: Constants::FILENAME_TRUNCATION_LENGTH) %>
|
||||
<% else %>
|
||||
<%= truncate(asset.file_name, length: Constants::FILENAME_TRUNCATION_LENGTH) %>
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="attachment-metadata">
|
||||
<%= 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>
|
||||
<% unless asset.locked? %>
|
||||
<div class="remove-icon pull-right">
|
||||
<%= ff.remove_nested_fields_link do %>
|
||||
<span class="fas fa-trash"></span>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
|
||||
<div class="pseudo-attachment-container" style="order: <%= i %>">
|
||||
<%= link_to rails_blob_path(asset.file, disposition: 'attachment'),
|
||||
class: 'file-preview-link',
|
||||
id: "modal_link#{asset.id}",
|
||||
data: { no_turbolink: true,
|
||||
id: true,
|
||||
'preview-url': asset_file_preview_path(asset),
|
||||
'order-atoz': order_atoz,
|
||||
'order-ztoa': order_ztoa,
|
||||
'order-new': i,
|
||||
'order-old': assets_count - i,
|
||||
} do %>
|
||||
|
||||
<%= render partial: 'shared/asset_placeholder.html.erb', locals: { edit_page: false, asset: asset } %>
|
||||
<% end %>
|
||||
</div>
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
<% assets = ordered_assets(step) %>
|
||||
<% assets = step.assets %>
|
||||
<% current_order = step.current_view_state(current_user).state.dig('assets', 'sort') %>
|
||||
<div class="col-xs-12">
|
||||
<hr>
|
||||
</div>
|
||||
|
|
@ -15,23 +16,21 @@
|
|||
<% if !(preview) && (can_manage_protocol_in_module?(@protocol) ||
|
||||
can_manage_protocol_in_repository?(@protocol)) %>
|
||||
<%= render partial: '/assets/marvinjs/create_marvin_sketch_button.html.erb',
|
||||
locals: { element_id: step.id, element_type: 'Step', sketch_container: ".attachments#att-#{step.id}" } %>
|
||||
locals: { element_id: step.id, element_type: 'Step', sketch_container: ".attachments[data-step-id=#{step.id}]" } %>
|
||||
<%= render partial: '/assets/wopi/create_wopi_file_button.html.erb',
|
||||
locals: { element_id: step.id, element_type: 'Step' } %>
|
||||
<% end %>
|
||||
<div class="dropdown attachments-order" id="dd-att-step-<%= step.id %>">
|
||||
<button class="btn btn-default dropdown-toggle" type="button" id="sortMenu" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
|
||||
<span id="dd-att-step-<%= step.id %>-label">
|
||||
<%= t("general.sort.#{step.current_view_state(current_user).state.dig('assets', 'sort')}_html") %>
|
||||
<div class="dropdown attachments-order" data-step-id="<%= step.id %>" data-state-save-path="<%= update_view_state_step_path(step.id) %>">
|
||||
<button class="btn btn-light dropdown-toggle" type="button" id="sortMenu" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
|
||||
<span class="selected-order">
|
||||
<%= t("general.sort.#{current_order}_html") %>
|
||||
</span>
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu" aria-labelledby="sortMenu" data-state-save-path="<%= update_view_state_step_path(step.id) %>">
|
||||
<ul class="dropdown-menu" aria-labelledby="sortMenu" >
|
||||
<% ['new', 'old', 'atoz', 'ztoa'].each do |sort| %>
|
||||
<li>
|
||||
<a data-order="<%= sort %>" onClick="reorderAttachments(this, '<%= step.id %>', '<%= sort %>')">
|
||||
<%= t("general.sort.#{sort}_html") %>
|
||||
</a>
|
||||
<a data-order="<%= sort %>" class="change-order"><%= t("general.sort.#{sort}_html") %></a>
|
||||
</li>
|
||||
<% end %>
|
||||
</ul>
|
||||
|
|
@ -40,12 +39,9 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-xs-12 attachments" id="att-<%= step.id %>">
|
||||
<div class="col-xs-12 attachments" data-step-id="<%= step.id %>" data-order="<%= current_order %>">
|
||||
<% assets.each_with_index do |asset, i| %>
|
||||
<% order_atoz = az_ordered_assets_index(step, asset.id) %>
|
||||
<% order_ztoa = assets.length - az_ordered_assets_index(step, asset.id) %>
|
||||
<%= render partial: 'steps/attachments/item.html.erb',
|
||||
locals: { asset: asset, i: i, assets_count: assets.length, step: step, order_atoz: order_atoz, order_ztoa: order_ztoa } %>
|
||||
<%= render partial: 'assets/asset.html.erb', locals: { asset: asset } %>
|
||||
<% end %>
|
||||
</div>
|
||||
<hr>
|
||||
|
|
|
|||
|
|
@ -67,44 +67,4 @@ describe AssetsController, type: :controller do
|
|||
.to(change { Activity.count })
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET asset_card' do
|
||||
let(:action) { get :toggle_view_mode, params: params, format: :json }
|
||||
let!(:params) do
|
||||
{ id: asset.id }
|
||||
end
|
||||
|
||||
it do
|
||||
action
|
||||
|
||||
expect(response).to have_http_status 200
|
||||
end
|
||||
|
||||
context 'when small card' do
|
||||
it do
|
||||
action
|
||||
|
||||
expect(response).to have_http_status 200
|
||||
# wtf, not working here?, render_to_string is returning nil
|
||||
# expect(JSON.parse(response.body)['html']).to be_eql 'hellow world'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when iframe' do
|
||||
before do
|
||||
allow_any_instance_of(Asset).to receive(:small_card).and_return(false)
|
||||
allow_any_instance_of(Asset).to receive(:get_action_url).and_return('fakeurl.com')
|
||||
allow(controller).to receive(:wopi_enabled?).and_return(true)
|
||||
allow(controller).to receive(:wopi_file?).and_return(true)
|
||||
end
|
||||
|
||||
it do
|
||||
action
|
||||
|
||||
expect(response).to have_http_status 200
|
||||
expect(JSON.parse(response.body)['html'])
|
||||
.to be_eql '<iframe src="fakeurl.com" title="DocumentPreview"></iframe>'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue