Merge pull request #1876 from aignatov-bio/ai-sci-3624-refactor-marvin-js-over-active-storage

Refactor MarvinJS core over active storage [SCI-3624]
This commit is contained in:
aignatov-bio 2019-07-22 10:53:37 +02:00 committed by GitHub
commit cd40084c82
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
34 changed files with 439 additions and 520 deletions

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><defs><style>.cls-2{fill:#505050}</style></defs><path fill="none" d="M0 0h24v24H0z" id="Trim_Area" data-name="Trim Area"/><g id="Icons"><path class="cls-2" d="M12 2.56l8 4.62v9.64l-8 4.62-8-4.62V7.18zm0-.95L3 6.8v10.4l9 5.19 9-5.19V6.8l-9-5.19z"/><path class="cls-2" d="M5.28 9.52l8.21-4.74-1-.58-7.21 4.16v1.16zM18.51 16V7.67l-1-.57v9.48l1-.58zm-13.23-.95v1.15l6.44 3.72 1-.58-7.44-4.29z"/></g></svg>

After

Width:  |  Height:  |  Size: 461 B

View file

@ -14,7 +14,7 @@
_expandAllResults();
applyCollapseLinkCallBack();
applyCreateWopiFileCallback();
Assets.setupAssetsLoading();
//Assets.setupAssetsLoading();

View file

@ -122,6 +122,7 @@
SmartAnnotation.preventPropagation('.atwho-user-popover');
TinyMCE.destroyAll();
DragNDropSteps.clearFiles();
MarvinJsEditor.initNewButton('.new-marvinjs-upload-button');
}, 1000);
})
@ -375,7 +376,6 @@
function initCallBacks() {
applyCreateWopiFileCallback()
if (typeof(MarvinJsEditor.initNewButton) == 'function') MarvinJsEditor.initNewButton('.new-marvinjs-upload-button');
applyCheckboxCallBack();
applyStepCompletedCallBack();
applyEditCallBack();
@ -601,16 +601,12 @@
SmartAnnotation.preventPropagation('.atwho-user-popover');
tinyMCE.editors.step_description_textarea.remove();
MarvinJsEditor.initNewButton('.new-marvinjs-upload-button');
//Rerender tables
$new_step.find("div.step-result-hot-table").each(function() {
$(this).handsontable("render");
});
animateSpinner(null, false);
<<<<<<< HEAD
Assets.setupAssetsLoading();
=======
>>>>>>> activestorage_migration
DragNDropSteps.clearFiles();
FilePreviewModal.init();
$.initTooltips();

View file

@ -25,10 +25,6 @@ var FilePreviewModal = (function() {
name = $(this).find('.attachment-label').text();
url = $(this).data('preview-url');
downloadUrl = $(this).attr('href');
if ($(this).data('asset-type') === 'marvin-sketch') {
openMarvinPrevieModal(name, $(this).find('#description'), this);
return true;
}
openPreviewModal(name, url, downloadUrl);
return true;
});
@ -412,7 +408,7 @@ var FilePreviewModal = (function() {
imageEditor = {};
$('#tui-image-editor').html('');
$('#fileEditModal').modal('hide');
}
});
if (data.mode === 'tinymce') {
$.ajax({
@ -487,6 +483,8 @@ var FilePreviewModal = (function() {
modal.find('.file-edit-link').css('display', 'none');
}
}
} else if (data.type === 'marvinjs') {
openMarvinEditModal(data, modal);
} else {
modal.find('.file-edit-link').css('display', 'none');
modal.find('.file-preview-container').html(data['preview-icon']);
@ -570,41 +568,30 @@ var FilePreviewModal = (function() {
modal.find('.file-edit-link').css('display', 'none');
}
function openMarvinPrevieModal(name, src, sketch) {
var modal = $('#filePreviewModal');
var link = modal.find('.file-download-link');
var target;
clearPrevieModal();
modal.modal('show');
function openMarvinEditModal(data, modal) {
modal.find('.file-preview-container')
.append($('<img>').attr('src', '').attr('alt', ''));
target = modal.find('.file-preview-container').find('img');
MarvinJsEditor.create_preview(src, target);
MarvinJsEditor.create_download_link(src, link, name);
modal.find('.file-name').text(name);
$.get(sketch.dataset.updateUrl, function(result) {
if (!readOnly && result.editable) {
modal.find('.file-edit-link').css('display', '');
modal.find('.file-edit-link').off().click(function(ev) {
ev.preventDefault();
.append($('<img>')
.attr('src', data['large-preview-url'])
.attr('alt', data.name)
.click(function(ev) {
ev.stopPropagation();
modal.modal('hide');
MarvinJsEditor.open({
mode: 'edit',
data: src.val(),
name: name,
marvinUrl: sketch.dataset.updateUrl,
reloadImage: {
src: src,
sketch: sketch
}
});
}));
if (!readOnly && data.editable) {
modal.find('.file-edit-link').css('display', '');
modal.find('.file-edit-link').off().click(function(ev) {
ev.preventDefault();
ev.stopPropagation();
modal.modal('hide');
MarvinJsEditor.open({
mode: 'edit',
data: data.description,
name: data.name,
marvinUrl: data['update-url']
});
} else {
modal.find('.file-edit-link').css('display', 'none');
}
});
});
} else {
modal.find('.file-edit-link').css('display', 'none');
}
}
return Object.freeze({

View file

@ -1,12 +1,15 @@
/* global TinyMCE, ChemicalizeMarvinJs, MarvinJSUtil, I18n, FilePreviewModal, tinymce */
/* global Results, Comments */
/* eslint-disable no-param-reassign */
/* eslint-disable wrap-iife */
/* eslint-disable no-use-before-define */
var marvinJsRemoteLastMrv;
var marvinJsRemoteEditor;
var MarvinJsEditor;
MarvinJsEditor = (function() {
var MarvinJsEditorApi = (function() {
var marvinJsModal = $('#MarvinJsModal');
var marvinJsContainer = $('#marvinjs-editor');
var marvinJsObject = $('#marvinjs-sketch');
@ -15,23 +18,6 @@ MarvinJsEditor = (function() {
var marvinJsMode = marvinJsContainer.data('marvinjsMode');
// Facade api actions
var marvinJsExport = (childFunction, options = {}) => {
if (marvinJsMode === 'remote') {
remoteExport(childFunction, options);
} else {
localExport(childFunction, options);
}
};
var marvinJsImage = (childFunction, source, options = {}) => {
if (marvinJsMode === 'remote') {
remoteImage(childFunction, source, options);
} else {
localImage(childFunction, source, options);
}
};
var marvinJsExportImage = (childFuction, options = {}) => {
if (marvinJsMode === 'remote') {
remoteExportImage(childFuction, options);
@ -53,25 +39,6 @@ MarvinJsEditor = (function() {
// Local marvinJS installation
var localExport = (childFuction, options = {}) => {
loadEditor().then(function(sketcherInstance) {
sketcherInstance.exportStructure('mrv').then(function(source) {
childFuction(source, options);
});
});
};
var localImage = (childFuction, source, options = {}) => {
loadPackages().then(function(sketcherInstance) {
sketcherInstance.onReady(function() {
var exporter = createExporter(sketcherInstance, 'image/jpeg');
exporter.render(source).then(function(image) {
childFuction(source, image, options);
});
});
});
};
var localExportImage = (childFuction, options = {}) => {
loadEditor().then(function(sketcherInstance) {
sketcherInstance.exportStructure('mrv').then(function(source) {
@ -89,10 +56,6 @@ MarvinJsEditor = (function() {
// Web services installation
var remoteExport = (childFuction, options = {}) => {
childFuction(marvinJsRemoteLastMrv, options);
};
var remoteImage = (childFuction, source, options = {}) => {
var params = {
carbonLabelVisible: false,
@ -105,7 +68,7 @@ MarvinJsEditor = (function() {
setTimeout(() => { remoteImage(childFuction, source, options); }, 100);
return false;
}
marvinJsRemoteEditor.exportMrvToImageDataUri(source, 'jpeg', params).then(function(image) {
marvinJsRemoteEditor.exportMrvToImageDataUri(source, 'image/jpeg', params).then(function(image) {
childFuction(source, image, options);
});
return true;
@ -120,17 +83,18 @@ MarvinJsEditor = (function() {
if (marvinJsMode === 'remote') {
if (config.mode === 'new' || config.mode === 'new-tinymce') {
marvinJsRemoteEditor.importStructure('mrv', emptySketch);
sketchName.val(I18n.t('marvinjs.new_sketch'));
sketchName.val('');
} else if (config.mode === 'edit') {
marvinJsRemoteLastMrv = config.data;
marvinJsRemoteEditor.importStructure('mrv', config.data);
sketchName.val(config.name);
} else if (config.mode === 'edit-tinymce') {
$.get(config.marvinUrl, function(result) {
marvinJsRemoteLastMrv = config.data;
$.get(config.marvinUrl, { object_type: 'TinyMceAsset' }, function(result) {
marvinJsRemoteEditor.importStructure('mrv', result.description);
sketchName.val(result.name);
});
}
marvinJsRemoteEditor.on('molchange', () => {
marvinJsRemoteEditor.exportStructure('mrv').then(function(source) {
marvinJsRemoteLastMrv = source;
@ -169,42 +133,16 @@ MarvinJsEditor = (function() {
return new marvin.ImageExporter(params);
}
function assignImage(source, data, target) {
target.attr('src', data);
return data;
}
function TinyMceBuildHTML(json) {
var imgstr = "<img src='" + json.image.url + "'";
imgstr += " width='300' height='300'";
imgstr += " data-mce-token='" + json.image.token + "'";
imgstr += " data-source-id='" + json.image.source_id + "'";
imgstr += " data-source-type='" + json.image.source_type + "'";
imgstr += " alt='description-" + json.image.token + "' />";
return imgstr;
}
function saveFunction(source, config) {
$.post(config.marvinUrl, {
description: source,
object_id: config.objectId,
object_type: config.objectType,
name: sketchName.val()
}, function(result) {
var newAsset;
if (config.objectType === 'Step') {
newAsset = $(result.html);
newAsset.find('.file-preview-link').css('top', '-300px');
newAsset.addClass('new').prependTo($(config.container));
setTimeout(function() {
newAsset.find('.file-preview-link').css('top', '0px');
}, 200);
FilePreviewModal.init();
}
$(marvinJsModal).modal('hide');
});
}
function saveTinymceFunction(source, image, config) {
function saveFunction(source, image, config) {
$.post(config.marvinUrl, {
description: source,
object_id: config.objectId,
@ -212,48 +150,49 @@ MarvinJsEditor = (function() {
name: sketchName.val(),
image: image
}, function(result) {
var json = tinymce.util.JSON.parse(result);
config.editor.execCommand('mceInsertContent', false, TinyMceBuildHTML(json));
TinyMCE.updateImages(config.editor);
$(marvinJsModal).modal('hide');
});
}
function updateFunction(source, config) {
$.ajax({
url: config.marvinUrl,
data: {
description: source,
name: sketchName.val()
},
dataType: 'json',
type: 'PUT',
success: function(json) {
$(marvinJsModal).modal('hide');
config.reloadImage.src.val(json.description);
$(config.reloadImage.sketch).find('.attachment-label').text(json.name);
MarvinJsEditor.create_preview(
config.reloadImage.src,
$(config.reloadImage.sketch).find('img')
);
var newAsset = $(result.html);
var json;
if (config.objectType === 'Step') {
newAsset.find('.file-preview-link').css('top', '-300px');
newAsset.addClass('new').prependTo($(config.container));
setTimeout(function() {
newAsset.find('.file-preview-link').css('top', '0px');
}, 200);
} else if (config.objectType === 'Result') {
newAsset.prependTo($(config.container));
Results.expandResult(newAsset);
Comments.init();
} else if (config.objectType === 'TinyMceAsset') {
json = tinymce.util.JSON.parse(result);
config.editor.execCommand('mceInsertContent', false, TinyMceBuildHTML(json));
TinyMCE.updateImages(config.editor);
}
$(marvinJsModal).modal('hide');
FilePreviewModal.init();
});
}
function updateTinymceFunction(source, image, config) {
function updateFunction(source, image, config) {
$.ajax({
url: config.marvinUrl,
data: {
description: source,
name: sketchName.val(),
object_type: 'TinyMceAsset',
object_type: config.objectType,
image: image
},
dataType: 'json',
type: 'PUT',
success: function(json) {
config.image[0].src = json.url;
$(marvinJsModal).modal('hide');
if (config.objectType === 'TinyMceAsset') {
config.image[0].src = json.url;
config.image[0].dataset.mceSrc = json.url;
$(marvinJsModal).modal('hide');
} else {
$(marvinJsModal).modal('hide');
$('#modal_link' + json.id + ' img').attr('src', json.url);
$('#modal_link' + json.id + ' .attachment-label').html(json.file_name);
}
}
});
}
@ -270,7 +209,6 @@ MarvinJsEditor = (function() {
$('#MarvinJsPromoModal').modal('show');
return false;
}
if (marvinJsMode === 'remote' && typeof (marvinJsRemoteEditor) === 'undefined') {
setTimeout(() => { MarvinJsEditor.open(config); }, 100);
return false;
@ -284,12 +222,14 @@ MarvinJsEditor = (function() {
if (config.mode === 'new') {
MarvinJsEditor.save(config);
} else if (config.mode === 'edit') {
config.objectType = 'Asset';
MarvinJsEditor.update(config);
} else if (config.mode === 'new-tinymce') {
config.objectType = 'TinyMceAsset';
MarvinJsEditor.save_with_image(config);
MarvinJsEditor.save(config);
} else if (config.mode === 'edit-tinymce') {
MarvinJsEditor.update_tinymce(config);
config.objectType = 'TinyMceAsset';
MarvinJsEditor.update(config);
}
});
return true;
@ -312,42 +252,11 @@ MarvinJsEditor = (function() {
},
save: function(config) {
marvinJsExport(saveFunction, config);
},
save_with_image: function(config) {
marvinJsExportImage(saveTinymceFunction, config);
marvinJsExportImage(saveFunction, config);
},
update: function(config) {
marvinJsExport(updateFunction, config);
},
update_tinymce: function(config) {
marvinJsExportImage(updateTinymceFunction, config);
},
create_preview: function(source, target) {
marvinJsImage(assignImage, source.val(), target);
},
create_download_link: function(source, link, filename) {
var downloadLink = (mrv, image, option) => {
option.link.attr('href', image);
option.link.attr('download', option.filename);
};
marvinJsImage(downloadLink, source.val(), { link: link, filename: filename });
},
delete_sketch: function(url, object) {
$.ajax({
url: url,
dataType: 'json',
type: 'DELETE',
success: function() {
$(object).remove();
}
});
marvinJsExportImage(updateFunction, config);
}
};
});
@ -366,21 +275,21 @@ MarvinJsEditor = (function() {
function openMarvinJs() {
MarvinJsEditor.open({
mode: 'new-tinymce',
marvinUrl: '/marvin_js_assets',
marvinUrl: '/tiny_mce_assets/marvinjs',
editor: editor
});
}
// Add a button that opens a window
editor.addButton('marvinjsplugin', {
tooltip: I18n.t('marvinjs.new_button'),
icon: 'file-invoice',
icon: 'marvinjs',
onclick: openMarvinJs
});
// Adds a menu item to the tools menu
editor.addMenuItem('marvinjsplugin', {
text: I18n.t('marvinjs.new_button'),
icon: 'file-invoice',
icon: 'marvinjs',
context: 'insert',
onclick: openMarvinJs
});
@ -397,7 +306,7 @@ MarvinJsEditor = (function() {
$(document).on('turbolinks:load', function() {
MarvinJsEditor = MarvinJsEditor();
MarvinJsEditor = MarvinJsEditorApi();
if (MarvinJsEditor.enabled()) {
if ($('#marvinjs-editor')[0].dataset.marvinjsMode === 'remote') {
ChemicalizeMarvinJs.createEditor('#marvinjs-sketch').then(function(marvin) {

View file

@ -1,5 +1,5 @@
/* global _ hljs tinyMCE SmartAnnotation MarvinJsEditor FilePreviewModal globalConstants */
/* global _ hljs tinyMCE SmartAnnotation MarvinJsEditor globalConstants */
/* global _ I18n */
@ -30,7 +30,6 @@ var TinyMCE = (function() {
$('<div class="tinymce-active-object-handler" style="display:none">'
+ '<a class="file-download-link tool-button" href="#" data-turbolinks="false"><i class="mce-ico mce-i-donwload"></i></a>'
+ '<span class="file-edit-link tool-button" href="#" data-turbolinks="false"><i class="mce-ico mce-i-pencil"></i></span>'
+ '<span class="file-image-editor-link tool-button" href="#" data-turbolinks="false"><i class="mce-ico mce-i-image"></i></span>'
+ '</div>').appendTo(editorToolbar.find('.mce-stack-layout'));
editorIframe.contents().click(function() {
var marvinJsEdit;
@ -44,20 +43,19 @@ var TinyMCE = (function() {
});
editorContainer.find('.tinymce-active-object-handler').css('display', 'block');
editorContainer.find('.tinymce-active-object-handler .file-download-link')
.attr('href', image[0].src)
.attr('download', 'tinymce-image');
.attr('href', '/tiny_mce_assets/' + image.data('mceToken') + '/download');
// Edit link
editLink = editorContainer.find('.tinymce-active-object-handler .file-edit-link');
if (image[0].dataset.sourceId) {
if (image[0].dataset.sourceType) {
editLink.css('display', 'inline-block');
marvinJsEdit = (image[0].dataset.sourceType === 'MarvinJsAsset' && typeof (MarvinJsEditor) !== 'undefined');
marvinJsEdit = (image[0].dataset.sourceType === 'marvinjs' && typeof (MarvinJsEditor) !== 'undefined');
if (!marvinJsEdit) editLink.css('display', 'none');
editLink.on('click', function() {
if (marvinJsEdit) {
MarvinJsEditor.open({
mode: 'edit-tinymce',
marvinUrl: '/marvin_js_assets/' + image[0].dataset.sourceId,
marvinUrl: '/tiny_mce_assets/' + image[0].dataset.mceToken + '/marvinjs',
image: image
});
}
@ -66,26 +64,6 @@ var TinyMCE = (function() {
editLink.css('display', 'none');
editLink.off('click');
}
// imaged editor Link
imageEditorLink = editorContainer.find('.tinymce-active-object-handler .file-image-editor-link');
if (image[0].dataset.mceToken && image[0].dataset.sourceId) {
imageEditorLink.css('display', 'inline-block');
imageEditorLink.on('click', function() {
FilePreviewModal.imageEditor({
'download-url': image[0].src,
filename: 'tinymce-image.jpg',
mode: 'tinymce',
url: '/tiny_mce_assets/' + image[0].dataset.mceToken,
quality: 100,
'mime-type': 'image/jpeg',
image: image[0]
});
});
} else {
imageEditorLink.css('display', 'none');
imageEditorLink.off('click');
}
} else {
editorContainer.find('.tinymce-active-object-handler').css('display', 'none');
}

View file

@ -88,7 +88,7 @@
form = createElement('form', {
action: editor.getParam(
'customimageuploader_form_url',
'/tinymce_assets'
'/tiny_mce_assets'
),
target: iframe._id,
method: 'POST',

View file

@ -23,5 +23,6 @@
@import "select2.min";
@import "extend/perfect-scrollbar";
@import "my_modules/protocols/*";
@import "my_modules/results/*";
@import "protocols/*";
@import "hooks/*";

View file

@ -41,9 +41,17 @@
background: $color-black;
border: 0;
height: 60px;
line-height: 40px;
padding: 10px 15px;
text-align: center;
.file-save-link {
margin: 0 20px 0 0;
}
.file-name {
align-items: center;
display: flex;
float: left;
input {
@ -51,10 +59,10 @@
box-shadow: none;
color: $color-black;
height: 40px;
margin-left: 5px;
outline: 0;
padding: 5px 10px;
position: relative;
top: -5px;
width: 350px;
}
}
@ -131,10 +139,28 @@
}
}
.mce-i-file-invoice::before {
content: "\F570";
font-family: "Font Awesome 5 Free";
font-weight: 900;
line-height: 16px;
position: absolute;
.new-marvinjs-upload-button {
.new-marvinjs-upload-icon {
display: inline-block;
height: 22px;
width: 22px;
img {
height: 100%;
width: 100%;
}
}
}
.mce-i-marvinjs::before {
background-image: url("icon_small/marvinjs.svg");
content: "";
display: block;
height: 22px;
left: -3px;
line-height: 16px;
position: relative;
top: -3px;
width: 22px;
}

View file

@ -0,0 +1,17 @@
// scss-lint:disable SelectorDepth
// scss-lint:disable NestingDepth
// scss-lint:disable SelectorFormat
// scss-lint:disable ImportantRule
@import "constants";
@import "mixins";
#results-toolbar {
.help_tooltips {
.btn-default {
border: 0;
color: inherit;
margin-left: 10px;
}
}
}

View file

@ -164,6 +164,10 @@
text-align: center;
width: 220px;
.attachment-thumbnail {
width: calc(100% - 20px);
}
a {
color: inherit;
}

View file

@ -20,8 +20,7 @@ class AssetsController < ApplicationController
def file_preview
response_json = {
'id' => @asset.id,
'type' => (@asset.image? ? 'image' : 'file'),
'type' => @asset.file.metadata[:asset_type] || (@asset.image? ? 'image' : 'file'),
'filename' => truncate(escape_input(@asset.file_name),
length: Constants::FILENAME_TRUNCATION_LENGTH),
'download-url' => download_asset_path(@asset, timestamp: Time.now.to_i)
@ -34,15 +33,22 @@ class AssetsController < ApplicationController
elsif @assoc.class == RepositoryCell
can_manage_repository_rows?(@repository.team)
end
if @asset.image?
if ['image/jpeg', 'image/pjpeg'].include? @asset.content_type
if response_json['type'] == 'image'
if ['image/jpeg', 'image/pjpeg'].include? @asset.file.content_type
response_json['quality'] = @asset.file_image_quality || 90
end
response_json.merge!(
'editable' => @asset.editable_image? && can_edit,
'mime-type' => @asset.file.content_type,
'large-preview-url' => @asset.large_preview
'large-preview-url' => rails_representation_url(@asset.large_preview)
)
elsif response_json['type'] == 'marvinjs'
response_json.merge!(
'editable' => can_edit,
'large-preview-url' => rails_representation_url(@asset.large_preview),
'update-url' => marvin_js_asset_path(@asset.id),
'description' => @asset.file.metadata[:description],
'name' => @asset.file.metadata[:name]
)
else

View file

@ -1,82 +1,98 @@
# frozen_string_literal: true
class MarvinJsAssetsController < ApplicationController
before_action :load_vars, except: :create
before_action :load_create_vars, only: :create
before_action :check_read_permission
before_action :check_edit_permission, only: %i(update create)
def create
new_asset = MarvinJsAsset.add_sketch(marvin_params, current_team)
if new_asset.object_type == 'Step'
result = MarvinJsService.create_sketch(marvin_params, current_user, current_team)
if result[:asset] && marvin_params[:object_type] == 'Step'
render json: {
html: render_to_string(
partial: 'assets/marvinjs/marvin_sketch_card.html.erb',
locals: { sketch: new_asset, i: 0, assets_count: 0, step: new_asset.object }
partial: 'steps/attachments/item.html.erb',
locals: { asset: result[:asset],
i: 0,
assets_count: 0,
step: result[:object],
order_atoz: 0,
order_ztoa: 0 }
)
}
elsif new_asset.object_type == 'TinyMceAsset'
tiny_img = TinyMceAsset.find(new_asset.object_id)
elsif result[:asset] && marvin_params[:object_type] == 'Result'
@my_module = result[:object].my_module
render json: {
image: {
url: view_context.image_url(tiny_img.url(:large)),
token: Base62.encode(tiny_img.id),
source_id: new_asset.id,
source_type: new_asset.class.name
}
}, content_type: 'text/html'
elsif new_asset
render json: new_asset
html: render_to_string(
partial: 'my_modules/result.html.erb',
locals: { result: result[:object] }
)
}, status: :ok
elsif result[:asset]
render json: result[:asset]
else
render json: new_asset.errors, status: :unprocessable_entity
end
end
def show
sketch = current_team.marvin_js_assets.find_by_id(params[:id])
if sketch
if sketch.object_type == 'Step'
editable = can_manage_protocol_in_module?(sketch.object.protocol) ||
can_manage_protocol_in_repository?(sketch.object.protocol)
render json: {
sketch: sketch,
editable: editable
}
else
render json: sketch
end
else
render json: { error: t('marvinjs.no_sketches_found') }, status: :unprocessable_entity
end
end
def destroy
sketch = current_team.marvin_js_assets.find_by_id(params[:id])
if sketch.destroy
render json: sketch
else
render json: { error: t('marvinjs.no_sketches_found') }, status: :unprocessable_entity
render json: result[:asset].errors, status: :unprocessable_entity
end
end
def update
sketch = MarvinJsAsset.update_sketch(marvin_params, current_team)
if sketch
render json: sketch
asset = MarvinJsService.update_sketch(marvin_params, current_user, current_team)
if asset
render json: { url: rails_representation_url(asset.medium_preview), id: asset.id, file_name: asset.file_name }
else
render json: { error: t('marvinjs.no_sketches_found') }, status: :unprocessable_entity
end
end
def team_sketches
result = ''
sketches = current_team.marvin_js_assets.where.not(object_type: 'TinyMceAsset')
sketches.each do |sketch|
result += render_to_string(
partial: 'shared/marvinjs_modal_sketch.html.erb',
locals: { sketch: sketch }
)
end
private
render json: { html: result, sketches: sketches.pluck(:id) }
def load_vars
@asset = current_team.assets.find_by_id(params[:id])
return render_404 unless @asset
@assoc ||= @asset.step
@assoc ||= @asset.result
if @assoc.class == Step
@protocol = @assoc.protocol
elsif @assoc.class == Result
@my_module = @assoc.my_module
end
end
private
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'
if @assoc.class == Step
@protocol = @assoc.protocol
elsif @assoc.class == MyModule
@my_module = @assoc
end
end
def check_read_permission
if @assoc.class == Step
return render_403 unless can_read_protocol_in_module?(@protocol) ||
can_read_protocol_in_repository?(@protocol)
elsif @assoc.class == Result || @assoc.class == MyModule
return render_403 unless can_read_experiment?(@my_module.experiment)
else
render_403
end
end
def check_edit_permission
if @assoc.class == Step
return render_403 unless can_manage_protocol_in_module?(@protocol) ||
can_manage_protocol_in_repository?(@protocol)
elsif @assoc.class == Result || @assoc.class == MyModule
return render_403 unless can_manage_module?(@my_module)
else
render_403
end
end
def marvin_params
params.permit(:id, :description, :object_id, :object_type, :name, :image)

View file

@ -1,6 +1,11 @@
# frozen_string_literal: true
class TinyMceAssetsController < ApplicationController
before_action :load_vars, only: %i(marvinjs_show marvinjs_update download)
before_action :check_read_permission, only: %i(marvinjs_show marvinjs_update download)
before_action :check_edit_permission, only: %i(marvinjs_update)
def create
image = params.fetch(:file) { render_404 }
tiny_img = TinyMceAsset.new(team_id: current_team.id, saved: false)
@ -23,4 +28,91 @@ class TinyMceAssetsController < ApplicationController
}, status: :unprocessable_entity
end
end
def download
if @asset&.image&.attached?
redirect_to rails_blob_path(@asset.image, disposition: 'attachment')
else
render_404
end
end
def marvinjs_show
asset = current_team.tiny_mce_assets.find_by_id(Base62.decode(params[:id]))
return render_404 unless asset
render json: {
name: asset.image.metadata[:name],
description: asset.image.metadata[:description]
}
end
def marvinjs_create
result = MarvinJsService.create_sketch(marvin_params, current_user, current_team)
if result[:asset]
render json: {
image: {
url: rails_representation_url(result[:asset].preview),
token: Base62.encode(result[:asset].id),
source_type: result[:asset].image.metadata[:asset_type]
}
}, content_type: 'text/html'
else
render json: result[:asset].errors, status: :unprocessable_entity
end
end
def marvinjs_update
asset = MarvinJsService.update_sketch(marvin_params, current_user, current_team)
if asset
render json: { url: rails_representation_url(asset.preview), id: asset.id }
else
render json: { error: t('marvinjs.no_sketches_found') }, status: :unprocessable_entity
end
end
private
def load_vars
@asset = current_team.tiny_mce_assets.find_by_id(Base62.decode(params[:id]))
return render_404 unless @asset
@assoc = @asset.object
if @assoc.class == Step
@protocol = @assoc.protocol
elsif @assoc.class == Protocol
@protocol = @assoc
elsif @assoc.class == MyModule
@my_module = @assoc
elsif @assoc.class == ResultText
@my_module = @assoc.result.my_module
end
end
def check_read_permission
if @assoc.class == Step || @assoc.class == Protocol
return render_403 unless can_read_protocol_in_module?(@protocol) ||
can_read_protocol_in_repository?(@protocol)
elsif @assoc.class == ResultText || @assoc.class == MyModule
return render_403 unless can_read_experiment?(@my_module.experiment)
else
render_403
end
end
def check_edit_permission
if @assoc.class == Step || @assoc.class == Protocol
return render_403 unless can_manage_protocol_in_module?(@protocol) ||
can_manage_protocol_in_repository?(@protocol)
elsif @assoc.class == ResultText || @assoc.class == MyModule
return render_403 unless can_manage_module?(@my_module)
else
render_403
end
end
def marvin_params
params.permit(:id, :description, :object_id, :object_type, :name, :image)
end
end

View file

@ -10,23 +10,15 @@ module MyModulesHelper
end
def ordered_assets(step)
assets = []
assets += step.assets
assets += step.marvin_js_assets
view_state = step.current_view_state(current_user)
assets.sort! do |a, b|
case view_state.state.dig('assets', 'sort')
when 'old'
b[asset_date_sort_field(b)] <=> a[asset_date_sort_field(a)]
when 'atoz'
(a[asset_name_sort_field(a)]).downcase <=> (b[asset_name_sort_field(b)]).downcase
when 'ztoa'
(b[asset_name_sort_field(b)]).downcase <=> (a[asset_name_sort_field(a)]).downcase
else
a[asset_date_sort_field(a)] <=> b[asset_date_sort_field(b)]
end
end
sort = case view_state.state.dig('assets', 'sort')
when 'old' then { created_at: :asc }
when 'atoz' then { file_file_name: :asc }
when 'ztoa' then { file_file_name: :desc }
else { created_at: :desc }
end
step.assets.order(sort)
end
def az_ordered_assets_index(step, asset_id)
@ -63,22 +55,4 @@ module MyModulesHelper
def is_results_page?
action_name == 'results'
end
private
def asset_date_sort_field(element)
result = {
'Asset' => :file_updated_at,
'MarvinJsAsset' => :updated_at
}
result[element.class.name]
end
def asset_name_sort_field(element)
result = {
'Asset' => :file_file_name,
'MarvinJsAsset' => :name
}
result[element.class.name] || ''
end
end

View file

@ -257,7 +257,7 @@ class Asset < ApplicationRecord
end
def image?
content_type == %r{^image/#{Regexp.union(Constants::WHITELISTED_IMAGE_TYPES)}}
content_type =~ %r{^image/#{Regexp.union(Constants::WHITELISTED_IMAGE_TYPES)}}
end
def text?
@ -448,21 +448,7 @@ class Asset < ApplicationRecord
!locked? && %r{^image/#{Regexp.union(Constants::WHITELISTED_IMAGE_TYPES_EDITABLE)}} =~ file.content_type
end
<<<<<<< HEAD
def generate_base64(style)
image = if file.options[:storage].to_sym == :s3
URI.parse(url(style)).open.to_a.join
else
File.open(file.path(style)).to_a.join
end
encoded_data = Base64.strict_encode64(image)
"data:#{file_content_type};base64,#{encoded_data}"
end
protected
=======
private
>>>>>>> activestorage_migration
def tempdir
Rails.root.join('tmp')

View file

@ -1,44 +0,0 @@
# frozen_string_literal: true
class MarvinJsAsset < ApplicationRecord
validates :name, presence: true
validates :description, presence: true
validates :object_id, presence: true
validates :object_type, presence: true
belongs_to :object, polymorphic: true,
optional: true,
inverse_of: :marvin_js_assets
belongs_to :team, inverse_of: :marvin_js_assets, optional: true
def self.add_sketch(values, team)
if values[:object_type] == 'TinyMceAsset'
tiny_mce_img = TinyMceAsset.create(
object: nil,
team_id: team.id,
saved: false,
image: values[:image],
image_file_name: "#{name}.jpg"
)
values[:object_id] = tiny_mce_img.id
end
values[:name] = I18n.t('marvinjs.new_sketch') if values[:name].empty?
create(values.merge(team_id: team.id).except(:image))
end
def self.update_sketch(values, team)
sketch = team.marvin_js_assets.find(values[:id])
return false unless sketch
values[:name] = I18n.t('marvinjs.new_sketch') if values[:name].empty?
sketch.update(values.except(:image, :object_type, :id))
if values[:object_type] == 'TinyMceAsset'
image = TinyMceAsset.find(sketch.object_id)
image.update(image: values[:image], image_file_name: "#{name}.jpg")
return { url: image.url(:large), description: sketch.description }
end
sketch
end
end

View file

@ -25,11 +25,6 @@ class Step < ApplicationRecord
has_many :tables, through: :step_tables
has_many :report_elements, inverse_of: :step, dependent: :destroy
has_many :marvin_js_assets,
as: :object,
class_name: :MarvinJsAsset,
dependent: :destroy
accepts_nested_attributes_for :checklists,
reject_if: :all_blank,
allow_destroy: true
@ -41,9 +36,6 @@ class Step < ApplicationRecord
attributes['contents'].blank?
},
allow_destroy: true
accepts_nested_attributes_for :marvin_js_assets,
reject_if: :all_blank,
allow_destroy: true
after_destroy :cascade_after_destroy
before_save :set_last_modified_by

View file

@ -41,7 +41,7 @@ class Team < ApplicationRecord
has_many :repositories, dependent: :destroy
has_many :reports, inverse_of: :team, dependent: :destroy
has_many :activities, inverse_of: :team, dependent: :destroy
has_many :marvin_js_assets, inverse_of: :team, dependent: :destroy
has_many :assets, inverse_of: :team, dependent: :destroy
attr_accessor :without_templates
attr_accessor :without_intro_demo

View file

@ -14,11 +14,6 @@ class TinyMceAsset < ApplicationRecord
touch: true,
optional: true
has_one :marvin_js_asset,
as: :object,
class_name: :MarvinJsAsset,
dependent: :destroy
belongs_to :object, polymorphic: true,
optional: true,
inverse_of: :tiny_mce_assets
@ -41,10 +36,6 @@ class TinyMceAsset < ApplicationRecord
# }
validates :estimated_size, presence: true
def source
return marvin_js_asset if marvin_js_asset
end
def self.update_images(object, images)
images = JSON.parse(images)
current_images = object.tiny_mce_assets.pluck(:id)
@ -75,8 +66,6 @@ class TinyMceAsset < ApplicationRecord
tm_asset.attributes['src'].value = Rails.application.routes.url_helpers.url_for(new_asset.image)
tm_asset['class'] = 'img-responsive'
end
tm_asset.attributes['src'].value = new_asset_url.url
tm_asset['class'] = 'img-responsive'
end
description.css('body').inner_html.to_s
end
@ -107,7 +96,7 @@ class TinyMceAsset < ApplicationRecord
asset = find_by_id(id)
asset.destroy if asset && !asset.saved
end
def self.update_estimated_size(id)
asset = find_by_id(id)
return unless asset&.image&.attached?

View file

@ -1,11 +1,89 @@
# frozen_string_literal: true
class MarvinJsService
def self.url
ENV['MARVINJS_URL']
end
class << self
def url
ENV['MARVINJS_URL']
end
def self.enabled?
!ENV['MARVINJS_URL'].nil? || !ENV['MARVINJS_API_KEY'].nil?
def enabled?
!ENV['MARVINJS_URL'].nil? || !ENV['MARVINJS_API_KEY'].nil?
end
def create_sketch(params, current_user, current_team)
file = generate_image(params)
if params[:object_type] == 'TinyMceAsset'
asset = TinyMceAsset.new(team_id: current_team.id)
attach_file(asset.image, file, params)
asset.save!
return { asset: asset }
end
asset = Asset.new(created_by: current_user,
last_modified_by: current_user,
team_id: current_team.id)
attach_file(asset.file, file, params)
asset.save!
connect_asset(asset, params, current_user)
end
def update_sketch(params, _current_user, current_team)
if params[:object_type] == 'TinyMceAsset'
asset = current_team.tiny_mce_assets.find(Base62.decode(params[:id]))
attachment = asset&.image
else
asset = current_team.assets.find(params[:id])
attachment = asset&.file
end
return unless attachment
file = generate_image(params)
attach_file(attachment, file, params)
asset
end
private
def connect_asset(asset, params, current_user)
if params[:object_type] == 'Step'
object = params[:object_type].constantize.find(params[:object_id])
object.assets << asset
elsif params[:object_type] == 'Result'
my_module = MyModule.find_by_id(params[:object_id])
return unless my_module
object = Result.create(user: current_user,
my_module: my_module,
name: prepare_name(params[:name]),
asset: asset,
last_modified_by: current_user)
end
{ asset: asset, object: object }
end
def generate_image(params)
StringIO.new(Base64.decode64(params[:image].split(',')[1]))
end
def attach_file(asset, file, params)
asset.attach(
io: file,
filename: "#{prepare_name(params[:name])}.jpg",
content_type: 'image/jpeg',
metadata: {
name: prepare_name(params[:name]),
description: params[:description],
asset_type: 'marvinjs'
}
)
end
def prepare_name(sketch_name)
if !sketch_name.empty?
sketch_name
else
I18n.t('marvinjs.new_sketch')
end
end
end
end

View file

@ -5,6 +5,8 @@
data-marvin-url="<%= marvin_js_assets_path %>"
data-sketch-container="<%= sketch_container %>"
>
<span class="fas fa-file-invoice new-marvinjs-upload-icon"></span>
<span class="new-marvinjs-upload-icon">
<%= image_tag 'icon_small/marvinjs.svg' %>
</span>
<%= t('marvinjs.new_button') %>
</span>

View file

@ -1,18 +0,0 @@
<div class="pseudo-attachment-container" style="order: <%= assets_count - i %>">
<%= link_to '',
class: 'file-preview-link',
id: "marvin_js_sketch_#{sketch.id}",
data: { no_turbolink: true, id: true, status: 'asset-present',
'preview-url': '',
'order-atoz': az_ordered_assets_index(step, sketch.id),
'order-ztoa': assets_count - az_ordered_assets_index(step, sketch.id),
'order-old': i,
'order-new': assets_count - i,
'asset-type': 'marvin-sketch',
'asset-id': sketch.id,
'update_url': marvin_js_asset_path(sketch.id)
} do %>
<%= render partial: 'assets/marvinjs/marvin_sketch_card_placeholder.html.erb',
locals: { edit_page: false, sketch: sketch } %>
<% end %>
</div>

View file

@ -1,18 +0,0 @@
<div class="attachment-placeholder pull-left" data-marvinjs-sketch="<%= sketch.id %>">
<div class="attachment-thumbnail">
<img src>
<%= hidden_field_tag :description, sketch.description %>
</div>
<div class="attachment-label"><%= truncate(sketch.name,
length: Constants::FILENAME_TRUNCATION_LENGTH) %></div>
<div class="spencer-bonnet-modif">
<%= t('protocols.steps.attachments.modified_label') %> <%= l(sketch.updated_at, format: :full_date) if sketch.updated_at %>
</div>
<% if edit_page %>
<div class="remove-icon pull-right">
<%= ff.remove_nested_fields_link do %>
<span class="fas fa-trash"></span>
<% end %>
</div>
<% end %>
</div>

View file

@ -41,6 +41,8 @@
<span class="fas fa-paperclip"></span>
<span class="hidden-xs"><%= t("my_modules.results.new_asset_result") %></span>
</a>
<%= render partial: '/assets/marvinjs/create_marvin_sketch_button.html.erb',
locals: { element_id: @my_module.id, element_type: 'Result', sketch_container: "#results[data-module-id=#{@my_module.id}]" } %>
<%= render partial: "assets/wopi/create_wopi_file_button",
locals: { element_id: @my_module.id, element_type: 'Result' } %>
</div>

View file

@ -11,7 +11,8 @@
<div class="modal-header">
<button type="button" class="preview-close" data-dismiss="modal"><span class="fas fa-times"></span></button>
<span class="file-name">
<%= text_field_tag :sketch_name %>
<%= t('marvinjs.modal_name_title') %>
<%= text_field_tag :sketch_name, '', placeholder: t('marvinjs.structure_placeholder') %>
</span>
<p class="file-save-link"><span class="fas fa-save"></span> <%= t('SaveClose')%></p>
</div>

View file

@ -24,12 +24,6 @@
<%= t("protocols.steps.new.tab_tables") %>
</a>
</li>
<li role="presentation" id="new-step-sketch-tab">
<a href="#new-step-sketch" data-toggle="tab" data-no-turbolink="true">
<span class="fas fa-file-invoice"></span>
<%= t('marvinjs.checmical_drawing') %>
</a>
</li>
</ul>
<div class="tab-content">
<div class="tab-pane active" role="tabpanel" id="new-step-main">
@ -90,13 +84,4 @@
<%= t("protocols.steps.new.add_table") %>
<% end %>
</div>
<div class="tab-pane" role="tabpanel" id="new-step-sketch">
<div class="sketch-container">
<%= f.nested_fields_for :marvin_js_assets do |ff| %>
<% next unless ff.object.description %>
<%= render partial: 'assets/marvinjs/marvin_sketch_card_placeholder.html.erb',
locals: { sketch: ff.object, edit_page: true, ff: ff} %>
<% end %>
</div>
</div>
</div>

View file

@ -44,13 +44,8 @@
<% 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) %>
<% if asset.class.name == 'Asset' %>
<%= 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 } %>
<% elsif asset.class.name == 'MarvinJsAsset' %>
<%= render partial: 'assets/marvinjs/marvin_sketch_card.html.erb',
locals: { sketch: asset, i:i, assets_count: assets.count, step: step} %>
<% end %>
<%= 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 } %>
<% end %>
</div>
<hr>

View file

@ -1765,10 +1765,11 @@ en:
complete_title: "Complete Step"
uncomplete_title: "Uncomplete Step"
attachments:
sort_new: "Newest first &#8595;"
sort_old: "Oldest first &#8593;"
sort_atoz: "Name &#8595;"
sort_ztoa: "Name &#8593;"
sort:
new_html: "Newest first &#8595;"
old_html: "Oldest first &#8593;"
atoz_html: "Name &#8595;"
ztoa_html: "Name &#8593;"
new:
add_step_title: "Add new step"
tab_checklists: "Checklists"
@ -2154,9 +2155,8 @@ en:
visibility: "https://support.scinote.net/hc/en-us/articles/360004627472"
manage_columns: "https://support.scinote.net/hc/en-us/articles/360004695831"
marvinjs:
new_sketch: "New sketch"
new_button: "New chemical drawing"
checmical_drawing: "Chemical drawings"
team_drawings: "Team drawings"
task: "Task"
no_sketches_found: "No sketches found"
new_sketch: "New structure"
new_button: "New structure"
structure_placeholder: "Click here to enter structure name"
modal_name_title: "Structure name:"
checmical_drawing: "Chemical drawings"

View file

@ -449,8 +449,16 @@ Rails.application.routes.draw do
end
# tinyMCE image uploader endpoint
resources :tiny_mce_assets, only: [:update]
post '/tinymce_assets', to: 'tiny_mce_assets#create', as: :tiny_mce_assets
resources :tiny_mce_assets, only: [:create] do
member do
get :download
get :marvinjs, to: 'tiny_mce_assets#marvinjs_show'
put :marvinjs, to: 'tiny_mce_assets#marvinjs_update'
end
collection do
post :marvinjs, to: 'tiny_mce_assets#marvinjs_create'
end
end
resources :results, only: [:update, :destroy] do
resources :result_comments,
@ -580,10 +588,6 @@ Rails.application.routes.draw do
# We cannot use 'resources :assets' because assets is a reserved route
# in Rails (assets pipeline) and causes funky behavior
<<<<<<< HEAD
get 'files/:id/present', to: 'assets#file_present', as: 'file_present_asset'
=======
>>>>>>> activestorage_migration
get 'files/:id/preview',
to: 'assets#file_preview',
as: 'asset_file_preview'

View file

@ -1,15 +0,0 @@
# frozen_string_literal: true
class CreateMarvinJsAssets < ActiveRecord::Migration[5.1]
def change
create_table :marvin_js_assets do |t|
t.bigint :team_id
t.string :description
t.references :object, polymorphic: true
t.timestamps
end
change_column :marvin_js_assets, :id, :bigint
end
end

View file

@ -1,7 +0,0 @@
# frozen_string_literal: true
class AddNameToMarvinJsAssets < ActiveRecord::Migration[5.1]
def change
add_column :marvin_js_assets, :name, :string
end
end

View file

@ -201,17 +201,6 @@ ActiveRecord::Schema.define(version: 2019_06_13_134100) do
t.index ["restored_by_id"], name: "index_experiments_on_restored_by_id"
end
create_table "marvin_js_assets", force: :cascade do |t|
t.bigint "team_id"
t.string "description"
t.string "object_type"
t.bigint "object_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "name"
t.index ["object_type", "object_id"], name: "index_marvin_js_assets_on_object_type_and_object_id"
end
create_table "my_module_groups", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false

View file

@ -1,9 +0,0 @@
# frozen_string_literal: true
FactoryBot.define do
factory :marvin_js_asset do
team_id 1
description 'MyString'
object ''
end
end