From 21e4dcd96a24474895fd131483332262f8099fbf Mon Sep 17 00:00:00 2001 From: aignatov-bio Date: Tue, 25 Aug 2020 13:22:36 +0200 Subject: [PATCH 01/35] Add wopi preview for office files --- .../javascripts/sitewide/file_preview.js | 27 +++++++++++-------- app/assets/stylesheets/themes/scinote.scss | 12 ++++++++- app/controllers/assets_controller.rb | 1 + app/utilities/wopi_util.rb | 2 +- 4 files changed, 29 insertions(+), 13 deletions(-) diff --git a/app/assets/javascripts/sitewide/file_preview.js b/app/assets/javascripts/sitewide/file_preview.js index 5edc691b4..4f3cd6d32 100644 --- a/app/assets/javascripts/sitewide/file_preview.js +++ b/app/assets/javascripts/sitewide/file_preview.js @@ -429,7 +429,7 @@ var FilePreviewModal = (function() { } }).done(function() { closeEditor(); - + }); if (typeof refreshProtocolStatusBar === 'function') refreshProtocolStatusBar(); }); @@ -457,16 +457,21 @@ var FilePreviewModal = (function() { link.attr('data-status', 'asset-present'); if (data.type === 'previewable') { animateSpinner('.file-preview-container', false); - modal.find('.file-preview-container') - .append($('') - .css('opacity', 0) - .attr('src', data['large-preview-url']) - .attr('alt', name) - .on('error', ActiveStoragePreviews.reCheckPreview) - .on('load', ActiveStoragePreviews.showPreview) - .click(function(ev) { - ev.stopPropagation(); - })); + if (data['wopi-preview-url']) { + modal.find('.file-preview-container') + .html(``); + } else { + modal.find('.file-preview-container') + .append($('') + .css('opacity', 0) + .attr('src', data['large-preview-url']) + .attr('alt', name) + .on('error', ActiveStoragePreviews.reCheckPreview) + .on('load', ActiveStoragePreviews.showPreview) + .click(function(ev) { + ev.stopPropagation(); + })); + } if (!readOnly && data.editable) { modal.find('.file-edit-link').css('display', ''); modal.find('.file-edit-link').off().click(function(ev) { diff --git a/app/assets/stylesheets/themes/scinote.scss b/app/assets/stylesheets/themes/scinote.scss index 42f54a018..3543f3d9b 100644 --- a/app/assets/stylesheets/themes/scinote.scss +++ b/app/assets/stylesheets/themes/scinote.scss @@ -1464,6 +1464,10 @@ ul.content-activities { color: $color-white; height: 100%; width: auto; + + .modal-body { + width: 100%; + } } .file-preview-container { @@ -1475,7 +1479,7 @@ ul.content-activities { height: 100%; justify-content: center; text-align: center; - width: 60%; + width: 100%; &.processing { background-image: url("/images/medium/processing.gif"); @@ -1487,6 +1491,12 @@ ul.content-activities { color: $color-black; margin: 30px 0; } + + .wopi-file-preview { + border: 0; + height: 100%; + width: 100%; + } } img { diff --git a/app/controllers/assets_controller.rb b/app/controllers/assets_controller.rb index c1a31a88e..ba7a45ffe 100644 --- a/app/controllers/assets_controller.rb +++ b/app/controllers/assets_controller.rb @@ -60,6 +60,7 @@ class AssetsController < ApplicationController if wopi_enabled? && wopi_file?(@asset) edit_supported, title = wopi_file_edit_button_status + response_json['wopi-preview-url'] = @asset.get_action_url(current_user, 'embedview') response_json['wopi-controls'] = render_to_string( partial: 'assets/wopi/file_wopi_controls.html.erb', locals: { diff --git a/app/utilities/wopi_util.rb b/app/utilities/wopi_util.rb index 2ed5ebb9e..645e9b4c0 100644 --- a/app/utilities/wopi_util.rb +++ b/app/utilities/wopi_util.rb @@ -55,7 +55,7 @@ module WopiUtil wopi_app.save! app.xpath('action').each do |action| name = action.xpath('@name').first.value - next unless %w(view edit editnew wopitest).include?(name) + next unless %w(view edit editnew embedview wopitest).include?(name) wopi_action = WopiAction.new wopi_action.action = name From 4eebfa54613ac71af2afc900d8f33ffd3799e873 Mon Sep 17 00:00:00 2001 From: Urban Rotnik Date: Fri, 28 Aug 2020 11:05:52 +0200 Subject: [PATCH 02/35] Controller action for card inline preview --- app/assets/javascripts/protocols/steps.js.erb | 22 ++++++++++ app/controllers/assets_controller.rb | 22 ++++++++++ app/models/asset.rb | 8 ++++ app/views/shared/_asset_placeholder.html.erb | 2 +- config/routes.rb | 1 + spec/controllers/assets_controller_spec.rb | 40 +++++++++++++++++++ 6 files changed, 94 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/protocols/steps.js.erb b/app/assets/javascripts/protocols/steps.js.erb index 8f96b177b..c9dbcd9e2 100644 --- a/app/assets/javascripts/protocols/steps.js.erb +++ b/app/assets/javascripts/protocols/steps.js.erb @@ -380,6 +380,27 @@ }); } + function initPreviewToggle(){ + $('.attachment-placeholder').on('click', '.dataPreviewAction', function (e) { + var view_mode = $(this).data('preview-type'); + var asset_id = $(this).data('asset-id'); + e.preventDefault(); + + $.ajax({ + url: `/files/${asset_id}/toggle_view_mode`, + type: "PATCH", + dataType: "json", + data: { asset: { view_mode: view_mode }}, + success: function (data) { + console.log(data); + }, + error: function (data) { + console.log(data); + } + }); + }); + } + function initCallBacks() { if (typeof(applyCreateWopiFileCallback) === 'function') applyCreateWopiFileCallback(); applyCheckboxCallBack(); @@ -389,6 +410,7 @@ applyCollapseLinkCallBack(); initDeleteStep(); TinyMCE.highlight(); + initPreviewToggle(); } /* diff --git a/app/controllers/assets_controller.rb b/app/controllers/assets_controller.rb index ba7a45ffe..e8dab1b70 100644 --- a/app/controllers/assets_controller.rb +++ b/app/controllers/assets_controller.rb @@ -97,6 +97,24 @@ class AssetsController < ApplicationController return edit_supported, title 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') + "" + 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 + end + end + end + def file_url return render_404 unless @asset.file.attached? @@ -300,6 +318,10 @@ class AssetsController < ApplicationController params.permit(:file) end + def toggle_view_mode_params + params.require(:asset).permit(:view_mode) + end + def asset_data_type(asset) return 'wopi' if wopi_file?(asset) return 'image' if asset.image? diff --git a/app/models/asset.rb b/app/models/asset.rb index f6ec993ec..887268fa7 100644 --- a/app/models/asset.rb +++ b/app/models/asset.rb @@ -403,6 +403,14 @@ 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 diff --git a/app/views/shared/_asset_placeholder.html.erb b/app/views/shared/_asset_placeholder.html.erb index a9b2ace1d..34bf8544a 100644 --- a/app/views/shared/_asset_placeholder.html.erb +++ b/app/views/shared/_asset_placeholder.html.erb @@ -1,5 +1,5 @@
- + InLine
<% if asset.previewable? %> <%= image_tag asset.medium_preview, diff --git a/config/routes.rb b/config/routes.rb index 298b2ead2..8801b7687 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -640,6 +640,7 @@ Rails.application.routes.draw do get 'files/:id/file_url', to: 'assets#file_url', as: 'asset_file_url' get 'files/:id/download', to: 'assets#download', as: 'asset_download' get 'files/:id/edit', to: 'assets#edit', as: 'edit_asset' + patch 'files/:id/toggle_view_mode', to: 'assets#toggle_view_mode', as: 'toggle_view_mode' post 'files/:id/update_image', to: 'assets#update_image', as: 'update_asset_image' post 'files/create_wopi_file', diff --git a/spec/controllers/assets_controller_spec.rb b/spec/controllers/assets_controller_spec.rb index a4a853bb8..5a08b756c 100644 --- a/spec/controllers/assets_controller_spec.rb +++ b/spec/controllers/assets_controller_spec.rb @@ -67,4 +67,44 @@ 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 '' + end + end + end end From a508b072754d9829eb81be94deabf0d0f18e7c1a Mon Sep 17 00:00:00 2001 From: Oleksii Kriuchykhin Date: Wed, 2 Sep 2020 15:02:56 +0200 Subject: [PATCH 03/35] Add view mode field to the Asset model [SCI-4958] --- app/models/asset.rb | 2 ++ db/migrate/20200902093234_add_view_mode_to_asset.rb | 5 +++++ db/structure.sql | 6 ++++-- 3 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 db/migrate/20200902093234_add_view_mode_to_asset.rb diff --git a/app/models/asset.rb b/app/models/asset.rb index 887268fa7..6f953e554 100644 --- a/app/models/asset.rb +++ b/app/models/asset.rb @@ -12,6 +12,8 @@ class Asset < ApplicationRecord # Lock duration set to 30 minutes LOCK_DURATION = 60 * 30 + enum view_mode: { show_as_thumbnail: 0, show_inline: 1 } + # ActiveStorage configuration has_one_attached :file diff --git a/db/migrate/20200902093234_add_view_mode_to_asset.rb b/db/migrate/20200902093234_add_view_mode_to_asset.rb new file mode 100644 index 000000000..4cd417dc6 --- /dev/null +++ b/db/migrate/20200902093234_add_view_mode_to_asset.rb @@ -0,0 +1,5 @@ +class AddViewModeToAsset < ActiveRecord::Migration[6.0] + def change + add_column :assets, :view_mode, :integer, default: 0, null: false + end +end diff --git a/db/structure.sql b/db/structure.sql index 392cd7df3..49757baaf 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -224,7 +224,8 @@ CREATE TABLE public.assets ( version integer DEFAULT 1, file_processing boolean, team_id integer, - file_image_quality integer + file_image_quality integer, + view_mode integer DEFAULT 0 NOT NULL ); @@ -7283,6 +7284,7 @@ INSERT INTO "schema_migrations" (version) VALUES ('20200622140843'), ('20200622155632'), ('20200709142830'), -('20200714082503'); +('20200714082503'), +('20200902093234'); From e4b94b582a74c1dd6ea4094b3691c9899a22f222 Mon Sep 17 00:00:00 2001 From: aignatov-bio Date: Mon, 31 Aug 2020 09:23:56 +0200 Subject: [PATCH 04/35] Add dynamic render of wopi files in viewport --- app/assets/javascripts/application.js.erb | 7 + app/assets/javascripts/protocols/steps.js.erb | 62 ++++++ .../javascripts/sitewide/drag_n_drop.js | 6 +- app/assets/stylesheets/steps.scss | 186 +++++++++--------- app/assets/stylesheets/themes/scinote.scss | 25 --- app/views/shared/_asset_placeholder.html.erb | 60 +++--- app/views/steps/_empty_step.html.erb | 6 +- .../attachments/_inline_attachment.html.erb | 4 + app/views/steps/attachments/_item.html.erb | 36 ++-- .../attachments/_new_attachment.html.erb | 6 +- 10 files changed, 220 insertions(+), 178 deletions(-) create mode 100644 app/views/steps/attachments/_inline_attachment.html.erb diff --git a/app/assets/javascripts/application.js.erb b/app/assets/javascripts/application.js.erb index 27da2b230..0b99864f0 100644 --- a/app/assets/javascripts/application.js.erb +++ b/app/assets/javascripts/application.js.erb @@ -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(); + }) +}) diff --git a/app/assets/javascripts/protocols/steps.js.erb b/app/assets/javascripts/protocols/steps.js.erb index c9dbcd9e2..cc1f244dd 100644 --- a/app/assets/javascripts/protocols/steps.js.erb +++ b/app/assets/javascripts/protocols/steps.js.erb @@ -715,6 +715,12 @@ ); } + function initInlineAttachment() { + $(document).on('scroll', 'body', function(){ + console.log(1) + }) + } + // On init initCallBacks(); initHandsOnTable($(document)); @@ -737,3 +743,59 @@ 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).html(``); + $(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).html(`
`); + $(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(); diff --git a/app/assets/javascripts/sitewide/drag_n_drop.js b/app/assets/javascripts/sitewide/drag_n_drop.js index 09fb99043..4d878d816 100644 --- a/app/assets/javascripts/sitewide/drag_n_drop.js +++ b/app/assets/javascripts/sitewide/drag_n_drop.js @@ -288,14 +288,14 @@ } function uploadedAssetPreview(asset, i) { - var html = `
-
+ var html = `
+
${truncateLongString(asset.name, GLOBAL_CONSTANTS.FILENAME_TRUNCATION_LENGTH)}
-
+
diff --git a/app/assets/stylesheets/steps.scss b/app/assets/stylesheets/steps.scss index df9d1872f..7773b0efb 100644 --- a/app/assets/stylesheets/steps.scss +++ b/app/assets/stylesheets/steps.scss @@ -9,6 +9,12 @@ @import "constants"; @import "mixins"; +:root { + --attachment-card-thumbnail-width: 220px; + --attachment-card-thumbnail-height: 280px; +} + + #new_step, .panel-step-attachment { ul { @@ -114,71 +120,36 @@ .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-column-gap: 1em; + grid-row-gap: 1em; + grid-template-columns: repeat(auto-fill, minmax(var(--attachment-card-thumbnail-width), 1fr)); + grid-auto-rows: var(--attachment-card-thumbnail-height); + 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); + 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); - } - - .attachment-thumbnail { - display: inline-block; - height: 160px; - margin: 16px 0 5px; - overflow: hidden; - padding: 0 10px; - position: relative; + .attachment-preview { + border-radius: $border-radius-default; + height: 170px; text-align: center; + position: relative; width: 100%; img { - border-radius: 5px; max-height: 100%; max-width: 100%; } @@ -188,14 +159,13 @@ &::before, &::after { content: ""; - border-radius: 16px 0 0 16px; + border-radius: 1em 0 0 1em; display: block; - height: 32px; - right: 0; - line-height: 32px; + right: -1em; + line-height: 2em; position: absolute; - bottom: 10px; - width: 36px; + bottom: 1em; + width: 2.25em; } &::before { @@ -204,8 +174,9 @@ &::after { background-image: url("/images/icon_small/marvinjs-white.svg"); - right: 4px; - width: 32px; + height: 2.25em; + right: -.75em; + width: 2em; } } @@ -219,68 +190,63 @@ 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-overflow: ellipsis; text-align: center; - top: 20px; 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; + margin-top: -4em; 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; } .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); + order: 0 !important; - .attachment-label { + .attachment-label, + .attachment-metadata { background-color: transparent; } @@ -291,9 +257,25 @@ position: relative; } + .attachment-preview { + border: 1px solid $brand-primary; + + &::before { + background: $brand-primary; + border-radius: 0 $border-radius-default; + bottom: 0; + color: $color-white; + content: "NEW"; + left: 0; + line-height: 20px; + position: absolute; + width: 50px; + } + } + &:hover { .attachment-label { - top: 20px; + margin-top: 1.5em; } } } @@ -340,3 +322,15 @@ .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: 100%; + width: 100%; + } +} diff --git a/app/assets/stylesheets/themes/scinote.scss b/app/assets/stylesheets/themes/scinote.scss index 3543f3d9b..5d075aa2c 100644 --- a/app/assets/stylesheets/themes/scinote.scss +++ b/app/assets/stylesheets/themes/scinote.scss @@ -1400,31 +1400,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 { diff --git a/app/views/shared/_asset_placeholder.html.erb b/app/views/shared/_asset_placeholder.html.erb index 34bf8544a..0bb82a865 100644 --- a/app/views/shared/_asset_placeholder.html.erb +++ b/app/views/shared/_asset_placeholder.html.erb @@ -1,34 +1,30 @@ -
- InLine -
- <% if asset.previewable? %> - <%= image_tag asset.medium_preview, - onerror: 'ActiveStoragePreviews.reCheckPreview(event)', - onload: 'ActiveStoragePreviews.showPreview(event)', - style: 'opacity: 0' %> - <% else %> - - <% end %> -
-
- <% 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 %> -
-
- <%= t('assets.placeholder.modified_label') %> <%= l(asset.updated_at, format: :full_date) if asset.updated_at %>
- <%= number_to_human_size(asset.file_size) %> -
- - <% if edit_page %> -
- <% unless ff.object.file.attached? && ff.object.locked? %> - <%= ff.remove_nested_fields_link do %> - - <% end %> - <% end %> -
+
+ <% if asset.previewable? %> + <%= image_tag asset.medium_preview, + onerror: 'ActiveStoragePreviews.reCheckPreview(event)', + onload: 'ActiveStoragePreviews.showPreview(event)', + style: 'opacity: 0' %> + <% else %> + <% end %>
+
+ <% 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 %> +
+ +<% if edit_page %> +
+ <% unless ff.object.file.attached? && ff.object.locked? %> + <%= ff.remove_nested_fields_link do %> + + <% end %> + <% end %> +
+<% end %> diff --git a/app/views/steps/_empty_step.html.erb b/app/views/steps/_empty_step.html.erb index 3dae462c0..2fc958008 100644 --- a/app/views/steps/_empty_step.html.erb +++ b/app/views/steps/_empty_step.html.erb @@ -72,8 +72,10 @@
<%= 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: 'shared/asset_placeholder.html.erb', + locals: { edit_page: true, asset: ff.object, ff: ff } %> +
<% end %>
diff --git a/app/views/steps/attachments/_inline_attachment.html.erb b/app/views/steps/attachments/_inline_attachment.html.erb new file mode 100644 index 000000000..8569bb0fc --- /dev/null +++ b/app/views/steps/attachments/_inline_attachment.html.erb @@ -0,0 +1,4 @@ +
+
+ +
diff --git a/app/views/steps/attachments/_item.html.erb b/app/views/steps/attachments/_item.html.erb index 2fcbc4785..bc14cd7b4 100644 --- a/app/views/steps/attachments/_item.html.erb +++ b/app/views/steps/attachments/_item.html.erb @@ -1,17 +1,19 @@ - -
- <%= 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 %> -
+<% if wopi_enabled? && wopi_file?(asset) %> + <%= render partial: 'steps/attachments/inline_attachment.html.erb', locals: { asset: asset } %> +<% else %> +
+ <%= 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 %> +
+<% end %> diff --git a/app/views/steps/attachments/_new_attachment.html.erb b/app/views/steps/attachments/_new_attachment.html.erb index a7bef3902..9425e6928 100644 --- a/app/views/steps/attachments/_new_attachment.html.erb +++ b/app/views/steps/attachments/_new_attachment.html.erb @@ -1,9 +1,9 @@ -
-
+
+
<%= truncate(file_name || file_url, length: Constants::FILENAME_TRUNCATION_LENGTH) %>
-
+
From 780db9d134028c76e50ab4dbf897cf7a99fb8373 Mon Sep 17 00:00:00 2001 From: aignatov-bio Date: Thu, 24 Sep 2020 17:10:36 +0200 Subject: [PATCH 05/35] Add assets view switcher --- app/assets/javascripts/protocols/steps.js.erb | 21 +++++----- .../javascripts/sitewide/file_preview.js | 1 + app/assets/stylesheets/steps.scss | 40 ++++++++++++++++++- app/controllers/assets_controller.rb | 16 +++----- app/views/shared/_asset_placeholder.html.erb | 7 ++++ .../attachments/_inline_attachment.html.erb | 20 +++++++++- app/views/steps/attachments/_item.html.erb | 4 +- 7 files changed, 84 insertions(+), 25 deletions(-) diff --git a/app/assets/javascripts/protocols/steps.js.erb b/app/assets/javascripts/protocols/steps.js.erb index cc1f244dd..b46c5762d 100644 --- a/app/assets/javascripts/protocols/steps.js.erb +++ b/app/assets/javascripts/protocols/steps.js.erb @@ -381,18 +381,19 @@ } 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(); }, error: function (data) { console.log(data); @@ -757,7 +758,8 @@ var StepInlineAttachments = (function() { setTimeout(() => { var iframeUrl = $(element).find('.iframe-placeholder').data('iframe-url'); if (elementVisible(element) && iframeUrl) { - $(element).html(``); + $(element).find('.iframe-placeholder') + .replaceWith(``); $(element).addClass('active').attr('data-created-at', new Date().getTime()); } },500) @@ -766,7 +768,8 @@ var StepInlineAttachments = (function() { function hideElement(element) { var iframeUrl = $(element).find('.active-iframe-preview').attr('src'); if (!elementVisible(element) && iframeUrl) { - $(element).html(`
`); + $(element).find('iframe') + .replaceWith(`
`); $(element).removeClass('active').attr('data-created-at', null) return true } diff --git a/app/assets/javascripts/sitewide/file_preview.js b/app/assets/javascripts/sitewide/file_preview.js index 4f3cd6d32..d31b942d8 100644 --- a/app/assets/javascripts/sitewide/file_preview.js +++ b/app/assets/javascripts/sitewide/file_preview.js @@ -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'); diff --git a/app/assets/stylesheets/steps.scss b/app/assets/stylesheets/steps.scss index 7773b0efb..5efef8a6a 100644 --- a/app/assets/stylesheets/steps.scss +++ b/app/assets/stylesheets/steps.scss @@ -142,6 +142,19 @@ width: 100%; } + .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-preview { border-radius: $border-radius-default; height: 170px; @@ -330,7 +343,32 @@ .active-iframe-preview { border: 0; - height: 100%; + 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; + } + } } diff --git a/app/controllers/assets_controller.rb b/app/controllers/assets_controller.rb index e8dab1b70..57a4dc252 100644 --- a/app/controllers/assets_controller.rb +++ b/app/controllers/assets_controller.rb @@ -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: [:edit, :toggle_view_mode] def file_preview file_type = @asset.file.metadata[:asset_type] || (@asset.previewable? ? 'previewable' : false) @@ -98,16 +98,10 @@ 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') - "" - else - render_to_string(partial: 'shared/asset_placeholder.html.erb', locals: { asset: @asset, edit_page: false }) - end - + @asset.update!(view_mode: toggle_view_mode_params[:view_mode]) + html = render_to_string(partial: 'steps/attachments/item.html.erb', + locals: { asset: @asset, i: 999, assets_count: 999, order_atoz: 999, order_ztoa: 999}) + # Sorting will be refactored later respond_to do |format| format.json do render json: { html: html }, status: :ok diff --git a/app/views/shared/_asset_placeholder.html.erb b/app/views/shared/_asset_placeholder.html.erb index 0bb82a865..b27056929 100644 --- a/app/views/shared/_asset_placeholder.html.erb +++ b/app/views/shared/_asset_placeholder.html.erb @@ -1,3 +1,10 @@ +<% if wopi_enabled? && wopi_file?(asset) %> +
+ +
+<% end %>
<% if asset.previewable? %> <%= image_tag asset.medium_preview, diff --git a/app/views/steps/attachments/_inline_attachment.html.erb b/app/views/steps/attachments/_inline_attachment.html.erb index 8569bb0fc..a535e71ae 100644 --- a/app/views/steps/attachments/_inline_attachment.html.erb +++ b/app/views/steps/attachments/_inline_attachment.html.erb @@ -1,4 +1,20 @@ -
-
+
+
+
+
+ <%= asset.file_name %> +
+ +
+
+ +
+
+
diff --git a/app/views/steps/attachments/_item.html.erb b/app/views/steps/attachments/_item.html.erb index bc14cd7b4..aad0f2005 100644 --- a/app/views/steps/attachments/_item.html.erb +++ b/app/views/steps/attachments/_item.html.erb @@ -1,7 +1,7 @@ -<% if wopi_enabled? && wopi_file?(asset) %> +<% if wopi_enabled? && wopi_file?(asset) && asset.show_inline? %> <%= render partial: 'steps/attachments/inline_attachment.html.erb', locals: { asset: asset } %> <% else %> -
+
<%= link_to rails_blob_path(asset.file, disposition: 'attachment'), class: 'file-preview-link', id: "modal_link#{asset.id}", From 70a0da17b208b7a83d758a031a1a91a6fddf68bd Mon Sep 17 00:00:00 2001 From: aignatov-bio Date: Thu, 24 Sep 2020 17:12:53 +0200 Subject: [PATCH 06/35] Fix markup --- app/views/steps/attachments/_inline_attachment.html.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/steps/attachments/_inline_attachment.html.erb b/app/views/steps/attachments/_inline_attachment.html.erb index a535e71ae..f2d344e70 100644 --- a/app/views/steps/attachments/_inline_attachment.html.erb +++ b/app/views/steps/attachments/_inline_attachment.html.erb @@ -15,6 +15,6 @@
-
+
From 1ed65eddfa0c1d798140671718477e034f5bef5a Mon Sep 17 00:00:00 2001 From: aignatov-bio Date: Fri, 25 Sep 2020 15:06:09 +0200 Subject: [PATCH 07/35] Move ordering to front-end --- app/assets/javascripts/protocols/steps.js.erb | 52 ++++++++++++---- app/assets/stylesheets/steps.scss | 62 ++++++++----------- app/controllers/assets_controller.rb | 50 ++++----------- .../marvin_js_assets_controller.rb | 16 ++--- app/helpers/application_helper.rb | 2 +- app/models/asset.rb | 8 --- app/views/assets/_asset.html.erb | 5 ++ .../_asset_inline.html.erb} | 7 ++- .../{shared => assets}/_asset_link.html.erb | 0 app/views/assets/_asset_thumbnail.html.erb | 40 ++++++++++++ .../_new_asset.html.erb} | 0 .../_result_user_generated.html.erb | 3 +- app/views/shared/_asset_placeholder.html.erb | 37 ----------- app/views/steps/_empty_step.html.erb | 6 +- .../steps/attachments/_edit_item.html.erb | 30 +++++++++ app/views/steps/attachments/_item.html.erb | 19 ------ app/views/steps/attachments/_list.html.erb | 26 ++++---- 17 files changed, 178 insertions(+), 185 deletions(-) create mode 100644 app/views/assets/_asset.html.erb rename app/views/{steps/attachments/_inline_attachment.html.erb => assets/_asset_inline.html.erb} (76%) rename app/views/{shared => assets}/_asset_link.html.erb (100%) create mode 100644 app/views/assets/_asset_thumbnail.html.erb rename app/views/{steps/attachments/_new_attachment.html.erb => assets/_new_asset.html.erb} (100%) delete mode 100644 app/views/shared/_asset_placeholder.html.erb create mode 100644 app/views/steps/attachments/_edit_item.html.erb delete mode 100644 app/views/steps/attachments/_item.html.erb diff --git a/app/assets/javascripts/protocols/steps.js.erb b/app/assets/javascripts/protocols/steps.js.erb index b46c5762d..429cd376c 100644 --- a/app/assets/javascripts/protocols/steps.js.erb +++ b/app/assets/javascripts/protocols/steps.js.erb @@ -394,6 +394,7 @@ success: function (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); @@ -412,6 +413,7 @@ initDeleteStep(); TinyMCE.highlight(); initPreviewToggle(); + reorderAttachmentsInit(); } /* @@ -700,20 +702,44 @@ } // 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() { diff --git a/app/assets/stylesheets/steps.scss b/app/assets/stylesheets/steps.scss index 5efef8a6a..fcebdc3df 100644 --- a/app/assets/stylesheets/steps.scss +++ b/app/assets/stylesheets/steps.scss @@ -5,13 +5,14 @@ // scss-lint:disable NestingDepth // scss-lint:disable SelectorFormat // scss-lint:disable ImportantRule +// scss-lint:disable Unknown @import "constants"; @import "mixins"; :root { - --attachment-card-thumbnail-width: 220px; --attachment-card-thumbnail-height: 280px; + --attachment-card-thumbnail-width: 220px; } @@ -120,16 +121,18 @@ .attachments { display: grid; + 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)); - grid-auto-rows: var(--attachment-card-thumbnail-height); + justify-content: center; margin: 1em 0; } .attachment-container { @include md-card-style; height: var(--attachment-card-thumbnail-height); + justify-self: center; overflow: hidden; padding: 1em; position: relative; @@ -158,8 +161,8 @@ .attachment-preview { border-radius: $border-radius-default; height: 170px; - text-align: center; position: relative; + text-align: center; width: 100%; img { @@ -171,13 +174,14 @@ &::before, &::after { - content: ""; border-radius: 1em 0 0 1em; + bottom: 1em; + content: ""; display: block; - right: -1em; + height: 2em; line-height: 2em; position: absolute; - bottom: 1em; + right: -1em; width: 2.25em; } @@ -209,13 +213,13 @@ overflow: hidden; padding-top: 1em; position: relative; - text-overflow: ellipsis; text-align: center; + text-overflow: ellipsis; transition: $md-transaction; white-space: nowrap; } - .attachment-label { + .attachment-label { @include font-main; margin-top: 1.5em; z-index: 2; @@ -223,8 +227,8 @@ .attachment-metadata { @include font-small; - margin-top: -4em; color: $color-silver-chalice; + margin-top: -4em; } .remove-icon { @@ -255,13 +259,7 @@ } &.new { - background-color: rgba(95, 95, 95, .1); - order: 0 !important; - - .attachment-label, - .attachment-metadata { - background-color: transparent; - } + border: 1px solid $brand-primary; .dnd-error { bottom: 15px; @@ -270,26 +268,18 @@ position: relative; } - .attachment-preview { - border: 1px solid $brand-primary; - - &::before { - background: $brand-primary; - border-radius: 0 $border-radius-default; - bottom: 0; - color: $color-white; - content: "NEW"; - left: 0; - line-height: 20px; - position: absolute; - width: 50px; - } - } - - &:hover { - .attachment-label { - margin-top: 1.5em; - } + &::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; } } } diff --git a/app/controllers/assets_controller.rb b/app/controllers/assets_controller.rb index 57a4dc252..b562f976e 100644 --- a/app/controllers/assets_controller.rb +++ b/app/controllers/assets_controller.rb @@ -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, :toggle_view_mode] + 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,13 +98,13 @@ class AssetsController < ApplicationController end def toggle_view_mode - @asset.update!(view_mode: toggle_view_mode_params[:view_mode]) - html = render_to_string(partial: 'steps/attachments/item.html.erb', - locals: { asset: @asset, i: 999, assets_count: 999, order_atoz: 999, order_ztoa: 999}) - # Sorting will be refactored later - 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 @@ -162,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 @@ -224,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 @@ -264,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 diff --git a/app/controllers/marvin_js_assets_controller.rb b/app/controllers/marvin_js_assets_controller.rb index aa4c1e8a9..eca041604 100644 --- a/app/controllers/marvin_js_assets_controller.rb +++ b/app/controllers/marvin_js_assets_controller.rb @@ -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 diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 0a7886316..4aeba141e 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -216,6 +216,6 @@ module ApplicationHelper end def wopi_enabled? - ENV['WOPI_ENABLED'] == 'true' + ENV['WOPI_ENABLED'] == 'true' || true end end diff --git a/app/models/asset.rb b/app/models/asset.rb index 6f953e554..b11d9a2f0 100644 --- a/app/models/asset.rb +++ b/app/models/asset.rb @@ -405,14 +405,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 diff --git a/app/views/assets/_asset.html.erb b/app/views/assets/_asset.html.erb new file mode 100644 index 000000000..57a278f08 --- /dev/null +++ b/app/views/assets/_asset.html.erb @@ -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 %> diff --git a/app/views/steps/attachments/_inline_attachment.html.erb b/app/views/assets/_asset_inline.html.erb similarity index 76% rename from app/views/steps/attachments/_inline_attachment.html.erb rename to app/views/assets/_asset_inline.html.erb index f2d344e70..a49d83704 100644 --- a/app/views/steps/attachments/_inline_attachment.html.erb +++ b/app/views/assets/_asset_inline.html.erb @@ -1,4 +1,9 @@ -
+
diff --git a/app/views/shared/_asset_link.html.erb b/app/views/assets/_asset_link.html.erb similarity index 100% rename from app/views/shared/_asset_link.html.erb rename to app/views/assets/_asset_link.html.erb diff --git a/app/views/assets/_asset_thumbnail.html.erb b/app/views/assets/_asset_thumbnail.html.erb new file mode 100644 index 000000000..1e6f8b7f7 --- /dev/null +++ b/app/views/assets/_asset_thumbnail.html.erb @@ -0,0 +1,40 @@ +
+ <%= 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) %> +
+ +
+ <% end %> +
+ <% if asset.previewable? %> + <%= image_tag asset.medium_preview, + onerror: 'ActiveStoragePreviews.reCheckPreview(event)', + onload: 'ActiveStoragePreviews.showPreview(event)', + style: 'opacity: 0' %> + <% else %> + + <% end %> +
+
+ <% if asset.file.attached? && asset&.file&.metadata['asset_type'] %> + <%= asset.file.metadata['name'] %> + <% else %> + <%= asset.file_name %> + <% end %> +
+ + <% end %> +
diff --git a/app/views/steps/attachments/_new_attachment.html.erb b/app/views/assets/_new_asset.html.erb similarity index 100% rename from app/views/steps/attachments/_new_attachment.html.erb rename to app/views/assets/_new_asset.html.erb diff --git a/app/views/my_modules/_result_user_generated.html.erb b/app/views/my_modules/_result_user_generated.html.erb index b00aa5eaf..29ac2e3a7 100644 --- a/app/views/my_modules/_result_user_generated.html.erb +++ b/app/views/my_modules/_result_user_generated.html.erb @@ -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 %> diff --git a/app/views/shared/_asset_placeholder.html.erb b/app/views/shared/_asset_placeholder.html.erb deleted file mode 100644 index b27056929..000000000 --- a/app/views/shared/_asset_placeholder.html.erb +++ /dev/null @@ -1,37 +0,0 @@ -<% if wopi_enabled? && wopi_file?(asset) %> -
- -
-<% end %> -
- <% if asset.previewable? %> - <%= image_tag asset.medium_preview, - onerror: 'ActiveStoragePreviews.reCheckPreview(event)', - onload: 'ActiveStoragePreviews.showPreview(event)', - style: 'opacity: 0' %> - <% else %> - - <% end %> -
-
- <% 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 %> -
- -<% if edit_page %> -
- <% unless ff.object.file.attached? && ff.object.locked? %> - <%= ff.remove_nested_fields_link do %> - - <% end %> - <% end %> -
-<% end %> diff --git a/app/views/steps/_empty_step.html.erb b/app/views/steps/_empty_step.html.erb index 2fc958008..d7a836c7f 100644 --- a/app/views/steps/_empty_step.html.erb +++ b/app/views/steps/_empty_step.html.erb @@ -72,10 +72,8 @@
<%= 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 %>
diff --git a/app/views/steps/attachments/_edit_item.html.erb b/app/views/steps/attachments/_edit_item.html.erb new file mode 100644 index 000000000..a90586ff1 --- /dev/null +++ b/app/views/steps/attachments/_edit_item.html.erb @@ -0,0 +1,30 @@ +
+
+ <% if asset.previewable? %> + <%= image_tag asset.medium_preview, + onerror: 'ActiveStoragePreviews.reCheckPreview(event)', + onload: 'ActiveStoragePreviews.showPreview(event)', + style: 'opacity: 0' %> + <% else %> + + <% end %> +
+
+ <% 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 %> +
+ + <% unless asset.locked? %> +
+ <%= ff.remove_nested_fields_link do %> + + <% end %> +
+ <% end %> +
diff --git a/app/views/steps/attachments/_item.html.erb b/app/views/steps/attachments/_item.html.erb deleted file mode 100644 index aad0f2005..000000000 --- a/app/views/steps/attachments/_item.html.erb +++ /dev/null @@ -1,19 +0,0 @@ -<% if wopi_enabled? && wopi_file?(asset) && asset.show_inline? %> - <%= render partial: 'steps/attachments/inline_attachment.html.erb', locals: { asset: asset } %> -<% else %> -
- <%= 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 %> -
-<% end %> diff --git a/app/views/steps/attachments/_list.html.erb b/app/views/steps/attachments/_list.html.erb index 54b2446f9..bf6197811 100644 --- a/app/views/steps/attachments/_list.html.erb +++ b/app/views/steps/attachments/_list.html.erb @@ -1,4 +1,5 @@ -<% assets = ordered_assets(step) %> +<% assets = step.assets %> +<% current_order = step.current_view_state(current_user).state.dig('assets', 'sort') %>

@@ -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 %> -
-
+
<% 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 %>

From cd9b143bc513df9a90eb7f0fc5e069a9708e8892 Mon Sep 17 00:00:00 2001 From: aignatov-bio Date: Fri, 25 Sep 2020 15:07:00 +0200 Subject: [PATCH 08/35] Fix markup --- app/helpers/application_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 4aeba141e..0a7886316 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -216,6 +216,6 @@ module ApplicationHelper end def wopi_enabled? - ENV['WOPI_ENABLED'] == 'true' || true + ENV['WOPI_ENABLED'] == 'true' end end From 9d64121577ac6a72ef5c7120d9565713a95dc5a4 Mon Sep 17 00:00:00 2001 From: aignatov-bio Date: Tue, 29 Sep 2020 12:58:05 +0200 Subject: [PATCH 09/35] Refactor WOPI discovery --- app/controllers/wopi_controller.rb | 6 +- app/models/asset.rb | 4 +- app/models/wopi_action.rb | 5 -- app/models/wopi_discovery.rb | 39 ---------- app/utilities/wopi_util.rb | 115 ++++++++++++++++++----------- 5 files changed, 79 insertions(+), 90 deletions(-) diff --git a/app/controllers/wopi_controller.rb b/app/controllers/wopi_controller.rb index de1f9a26b..bc04e7411 100644 --- a/app/controllers/wopi_controller.rb +++ b/app/controllers/wopi_controller.rb @@ -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' diff --git a/app/models/asset.rb b/app/models/asset.rb index 6f953e554..2aa164fa8 100644 --- a/app/models/asset.rb +++ b/app/models/asset.rb @@ -320,7 +320,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=1&') @@ -354,7 +354,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 diff --git a/app/models/wopi_action.rb b/app/models/wopi_action.rb index 9a51b2b2e..2eff2690f 100644 --- a/app/models/wopi_action.rb +++ b/app/models/wopi_action.rb @@ -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 diff --git a/app/models/wopi_discovery.rb b/app/models/wopi_discovery.rb index 45bffd41a..6ce192d15 100644 --- a/app/models/wopi_discovery.rb +++ b/app/models/wopi_discovery.rb @@ -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 diff --git a/app/utilities/wopi_util.rb b/app/utilities/wopi_util.rb index 645e9b4c0..520cee3ee 100644 --- a/app/utilities/wopi_util.rb +++ b/app/utilities/wopi_util.rb @@ -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) From 143b4077db0874c9eeb82d2e83415917d12b30db Mon Sep 17 00:00:00 2001 From: aignatov-bio Date: Wed, 14 Oct 2020 14:25:50 +0200 Subject: [PATCH 10/35] Fix tests --- spec/controllers/assets_controller_spec.rb | 40 ---------------------- 1 file changed, 40 deletions(-) diff --git a/spec/controllers/assets_controller_spec.rb b/spec/controllers/assets_controller_spec.rb index 5a08b756c..a4a853bb8 100644 --- a/spec/controllers/assets_controller_spec.rb +++ b/spec/controllers/assets_controller_spec.rb @@ -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 '' - end - end - end end From 566ef7aef7a9fd60f7651de5e20e1a7368363cf4 Mon Sep 17 00:00:00 2001 From: aignatov-bio Date: Fri, 16 Oct 2020 11:00:57 +0200 Subject: [PATCH 11/35] Add list view to atachments --- app/assets/javascripts/protocols/steps.js.erb | 11 +-- app/assets/stylesheets/steps.scss | 69 ++++++++++++++++--- app/models/asset.rb | 2 +- app/views/assets/_asset.html.erb | 4 +- app/views/assets/_asset_inline.html.erb | 43 +++++++++--- app/views/assets/_asset_list.html.erb | 35 ++++++++++ app/views/assets/_asset_thumbnail.html.erb | 24 ++++--- 7 files changed, 156 insertions(+), 32 deletions(-) create mode 100644 app/views/assets/_asset_list.html.erb diff --git a/app/assets/javascripts/protocols/steps.js.erb b/app/assets/javascripts/protocols/steps.js.erb index 959df845f..0d191e8c8 100644 --- a/app/assets/javascripts/protocols/steps.js.erb +++ b/app/assets/javascripts/protocols/steps.js.erb @@ -332,18 +332,19 @@ function initPreviewToggle(){ $('.attachments').on('click', '.change-preview-type', function (e) { var viewMode = $(this).data('preview-type'); - var assetId = $(this).data('asset-id'); + var toggleUrl = $(this).data('toggle-view-url'); + var assetId = $(this).closest('.asset').data('asset-id'); e.preventDefault(); e.stopPropagation(); $.ajax({ - url: `/files/${assetId}/toggle_view_mode`, + url: toggleUrl, type: "PATCH", dataType: "json", data: { asset: { view_mode: viewMode }}, success: function (data) { - $(`.step-asset[data-asset-id=${assetId}]`).replaceWith(data.html); + $(`.asset[data-asset-id=${assetId}]`).replaceWith(data.html); FilePreviewModal.init(); - $(`.step-asset[data-asset-id=${assetId}]`).closest('.attachments').trigger('reorder'); + $(`.asset[data-asset-id=${assetId}]`).closest('.attachments').trigger('reorder'); }, error: function (data) { console.log(data); @@ -661,7 +662,7 @@ }) $('.attachments').on('reorder', function() { - var assets = $(`.attachments[data-step-id=${$(this).data('step-id')}] .step-asset`); + var assets = $(`.attachments[data-step-id=${$(this).data('step-id')}] .asset`); var order = $(this).data('order'); var sortedAssets = assets.sort(function(a, b) { if (a.dataset.assetOrder == b.dataset.assetOrder) { diff --git a/app/assets/stylesheets/steps.scss b/app/assets/stylesheets/steps.scss index d7e69669d..7741c0d79 100644 --- a/app/assets/stylesheets/steps.scss +++ b/app/assets/stylesheets/steps.scss @@ -11,8 +11,8 @@ @import "mixins"; :root { - --attachment-card-thumbnail-height: 280px; - --attachment-card-thumbnail-width: 220px; + --attachment-row-height: 3em; + --attachment-column-width: 16em; } @@ -135,22 +135,26 @@ .attachments { display: grid; - grid-auto-rows: var(--attachment-card-thumbnail-height); + grid-auto-rows: var(--attachment-row-height); grid-column-gap: 1em; grid-row-gap: 1em; - grid-template-columns: repeat(auto-fill, minmax(var(--attachment-card-thumbnail-width), 1fr)); + grid-template-columns: repeat(auto-fill, minmax(var(--attachment-column-width), 1fr)); justify-content: center; margin: 1em 0; + + .nested_fields { + display: contents; + } } .attachment-container { @include md-card-style; - height: var(--attachment-card-thumbnail-height); + grid-row: span 6; justify-self: center; overflow: hidden; padding: 1em; position: relative; - width: var(--attachment-card-thumbnail-width); + width: var(--attachment-column-width); .file-preview-link { @@ -174,7 +178,7 @@ .attachment-preview { border-radius: $border-radius-default; - height: 170px; + height: 14em; position: relative; text-align: center; width: 100%; @@ -343,7 +347,7 @@ .inline-attachment-container { @include md-card-style; grid-column: 1/-1; - grid-row: span 2; + grid-row: span 12; .active-iframe-preview { border: 0; @@ -351,6 +355,26 @@ width: 100%; } + .image-container, + .general-file-container { + background: $color-concrete; + align-items: center; + display: flex; + height: calc(100% - 4em); + justify-content: center; + padding: .5em; + width: 100%; + + img { + max-width: 100%; + max-height: 100%; + } + + .fas { + font-size: 10em; + } + } + .header { align-items: center; display: flex; @@ -376,3 +400,32 @@ } } } + +.list-attachment-container { + @include md-card-style; + align-items: center; + display: flex; + grid-column: 1/-1; + padding: .5em; + + .file-name { + @include font-main; + } + + .file-name { + @include font-main; + color: $brand-primary; + margin: 0 .5em; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + .file-metadata { + @include font-small; + color: $color-silver-chalice; + display: grid; + grid-column-gap: 1em; + grid-template-columns: max-content max-content; + } +} diff --git a/app/models/asset.rb b/app/models/asset.rb index 4632048c9..47d2ae451 100644 --- a/app/models/asset.rb +++ b/app/models/asset.rb @@ -12,7 +12,7 @@ class Asset < ApplicationRecord # Lock duration set to 30 minutes LOCK_DURATION = 60 * 30 - enum view_mode: { show_as_thumbnail: 0, show_inline: 1 } + enum view_mode: { thumbnail: 0, inline: 1, list: 2} # ActiveStorage configuration has_one_attached :file diff --git a/app/views/assets/_asset.html.erb b/app/views/assets/_asset.html.erb index 57a278f08..8feef8c14 100644 --- a/app/views/assets/_asset.html.erb +++ b/app/views/assets/_asset.html.erb @@ -1,5 +1,7 @@ -<% if wopi_enabled? && wopi_file?(asset) && asset.show_inline? %> +<% if asset.inline? %> <%= render partial: 'assets/asset_inline.html.erb', locals: { asset: asset } %> +<% elsif asset.list? %> + <%= render partial: 'assets/asset_list.html.erb', locals: { asset: asset } %> <% else %> <%= render partial: 'assets/asset_thumbnail.html.erb', locals: { edit_page: false, asset: asset } %> <% end %> diff --git a/app/views/assets/_asset_inline.html.erb b/app/views/assets/_asset_inline.html.erb index a49d83704..122c106a9 100644 --- a/app/views/assets/_asset_inline.html.erb +++ b/app/views/assets/_asset_inline.html.erb @@ -1,4 +1,4 @@ -
-
+ <%= link_to rails_blob_path(asset.file, disposition: 'attachment'), + class: "file-preview-link file-name", + id: "modal_link#{asset.id}", + data: { no_turbolink: true, id: true, 'preview-url': asset_file_preview_path(asset)} do %> <%= asset.file_name %> -
+ <% end %>
-
- +
+ +
+
+ +
+
+
-
- + <% if wopi_enabled? && wopi_file?(asset) %> +
+ <% elsif asset.previewable? %> +
+ <%= image_tag asset.large_preview, + onerror: 'ActiveStoragePreviews.reCheckPreview(event)', + onload: 'ActiveStoragePreviews.showPreview(event)', + style: 'opacity: 0' %> +
+ <% else %> +
+ +
+ <% end %>
diff --git a/app/views/assets/_asset_list.html.erb b/app/views/assets/_asset_list.html.erb new file mode 100644 index 000000000..38f187435 --- /dev/null +++ b/app/views/assets/_asset_list.html.erb @@ -0,0 +1,35 @@ +
+
+ +
+ <%= link_to rails_blob_path(asset.file, disposition: 'attachment'), + class: "file-preview-link file-name", + id: "modal_link#{asset.id}", + data: { no_turbolink: true, id: true, 'preview-url': asset_file_preview_path(asset)} do %> + <%= asset.file_name %> + <% end %> + +
+ +
+
+ +
+
+ +
+
diff --git a/app/views/assets/_asset_thumbnail.html.erb b/app/views/assets/_asset_thumbnail.html.erb index 1e6f8b7f7..b41997fa4 100644 --- a/app/views/assets/_asset_thumbnail.html.erb +++ b/app/views/assets/_asset_thumbnail.html.erb @@ -1,20 +1,28 @@ -
<%= 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) %> -
- +
+ +
+
+ +
+
+
- <% end %>
<% if asset.previewable? %> <%= image_tag asset.medium_preview, From 3fe3c2087f8503f7cc36e51b90928636ee199719 Mon Sep 17 00:00:00 2001 From: aignatov-bio Date: Fri, 16 Oct 2020 11:27:23 +0200 Subject: [PATCH 12/35] Fix hound issues --- app/assets/stylesheets/steps.scss | 8 ++++---- app/models/asset.rb | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/assets/stylesheets/steps.scss b/app/assets/stylesheets/steps.scss index 7741c0d79..f93f2a66b 100644 --- a/app/assets/stylesheets/steps.scss +++ b/app/assets/stylesheets/steps.scss @@ -11,8 +11,8 @@ @import "mixins"; :root { - --attachment-row-height: 3em; --attachment-column-width: 16em; + --attachment-row-height: 3em; } @@ -357,8 +357,8 @@ .image-container, .general-file-container { - background: $color-concrete; align-items: center; + background: $color-concrete; display: flex; height: calc(100% - 4em); justify-content: center; @@ -366,8 +366,8 @@ width: 100%; img { - max-width: 100%; max-height: 100%; + max-width: 100%; } .fas { @@ -408,7 +408,7 @@ grid-column: 1/-1; padding: .5em; - .file-name { + .file-icon { @include font-main; } diff --git a/app/models/asset.rb b/app/models/asset.rb index 47d2ae451..909b9b7c6 100644 --- a/app/models/asset.rb +++ b/app/models/asset.rb @@ -12,7 +12,7 @@ class Asset < ApplicationRecord # Lock duration set to 30 minutes LOCK_DURATION = 60 * 30 - enum view_mode: { thumbnail: 0, inline: 1, list: 2} + enum view_mode: { thumbnail: 0, inline: 1, list: 2 } # ActiveStorage configuration has_one_attached :file From aefaf4d0c11913df3137f37a9ee0bd86c30f4240 Mon Sep 17 00:00:00 2001 From: aignatov-bio Date: Sun, 25 Oct 2020 16:57:50 +0100 Subject: [PATCH 13/35] Update image edit modal --- .../stylesheets/shared/image_edit_modal.scss | 37 +++++++++++++ app/assets/stylesheets/themes/scinote.scss | 55 ------------------- app/views/shared/_file_edit_modal.html.erb | 12 ++-- 3 files changed, 45 insertions(+), 59 deletions(-) create mode 100644 app/assets/stylesheets/shared/image_edit_modal.scss diff --git a/app/assets/stylesheets/shared/image_edit_modal.scss b/app/assets/stylesheets/shared/image_edit_modal.scss new file mode 100644 index 000000000..b5f592e90 --- /dev/null +++ b/app/assets/stylesheets/shared/image_edit_modal.scss @@ -0,0 +1,37 @@ +// Image edit modal +.modal-file-edit { + background: transparent; + font-size: $font-size-base; + padding: 0 !important; + + .modal-dialog { + height: 100%; + margin: 0; + width: auto; + } + + .modal-content { + border: 0; + height: 100%; + } + + .modal-header { + align-items: center; + background: $color-white; + display: flex; + height: 4em; + padding: 0 1em; + + .file-name { + font-weight: bold; + margin-right: auto; + } + } + + .modal-body { + height: calc(100% - 4em); + padding: 0; + } +} + + diff --git a/app/assets/stylesheets/themes/scinote.scss b/app/assets/stylesheets/themes/scinote.scss index 8cfde42c4..1bc515b94 100644 --- a/app/assets/stylesheets/themes/scinote.scss +++ b/app/assets/stylesheets/themes/scinote.scss @@ -1591,61 +1591,6 @@ ul.content-activities { } } -// Image edit modal -.modal-file-edit { - background: transparent; - font-size: $font-size-large; - padding: 0 !important; - - .preview-close { - background: transparent; - border: 0; - color: $color-white; - display: inline-block; - float: right; - } - - .modal-dialog { - height: 100%; - margin: 0; - padding: 0; - width: auto; - } - - .modal-content { - background: transparent; - border: 0; - -webkit-box-shadow: none; - box-shadow: none; - color: $color-white; - height: 100%; - width: auto; - } - - .modal-header { - background: $color-black; - border: 0; - height: 60px; - text-align: center; - - .file-name { - float: left; - } - } - - .modal-body { - height: calc(100% - 60px); - padding: 0; - } - - .file-save-link { - color: $color-white; - display: inline-block; - float: right; - margin-right: 20px; - } -} - // Disable textarea resizing throughout application // (will be done via autosize JS plugin) textarea { diff --git a/app/views/shared/_file_edit_modal.html.erb b/app/views/shared/_file_edit_modal.html.erb index 74eced9b4..a57db6bfd 100644 --- a/app/views/shared/_file_edit_modal.html.erb +++ b/app/views/shared/_file_edit_modal.html.erb @@ -8,11 +8,15 @@