mirror of
https://github.com/scinote-eln/scinote-web.git
synced 2025-11-08 07:21:03 +08:00
Merge pull request #1561 from aignatov-bio/ai-sci-3131-improve-image-file-upload
Improve the image file upload into RTE input fields [SCI-3131]
This commit is contained in:
commit
f47d0e2932
39 changed files with 585 additions and 424 deletions
|
|
@ -146,7 +146,7 @@
|
|||
var nameValid = textValidator(ev, $nameInput, 0,
|
||||
<%= Constants::NAME_MAX_LENGTH %>);
|
||||
var $descrTextarea = $form.find("#result_result_text_attributes_text");
|
||||
var $tinyMCEInput = TinyMCE.getContent();
|
||||
var $tinyMCEInput = TinyMCE.init('#result_result_text_attributes_text');;
|
||||
textValidator(ev, $descrTextarea, 1, <%= Constants::RICH_TEXT_MAX_LENGTH %>, false, $tinyMCEInput);
|
||||
break;
|
||||
case ResultTypeEnum.COMMENT:
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@
|
|||
animateSpinner(null, false);
|
||||
initNewReslutText();
|
||||
}
|
||||
})
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -59,6 +59,7 @@
|
|||
});
|
||||
Results.toggleResultEditButtons(false);
|
||||
TinyMCE.refresh();
|
||||
TinyMCE.init('#result_result_text_attributes_text');
|
||||
$('#result_name').focus();
|
||||
});
|
||||
}
|
||||
|
|
|
|||
2
app/assets/javascripts/sitewide/tiny_mce.js
vendored
2
app/assets/javascripts/sitewide/tiny_mce.js
vendored
|
|
@ -49,7 +49,7 @@ var TinyMCE = (function() {
|
|||
autosave_interval: '15s',
|
||||
autosave_retention: '1440m',
|
||||
removed_menuitems: 'newdocument',
|
||||
object_resizing: false,
|
||||
object_resizing: true,
|
||||
elementpath: false,
|
||||
forced_root_block: false,
|
||||
default_link_target: '_blank',
|
||||
|
|
|
|||
337
app/assets/javascripts/sitewide/tiny_mce_file_upload_plugin.js
vendored
Normal file
337
app/assets/javascripts/sitewide/tiny_mce_file_upload_plugin.js
vendored
Normal file
|
|
@ -0,0 +1,337 @@
|
|||
/* eslint no-underscore-dangle: "off" */
|
||||
/* eslint no-use-before-define: "off" */
|
||||
/* eslint no-restricted-syntax: ["off", "BinaryExpression[operator='in']"] */
|
||||
/* global tinymce I18n */
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
tinymce.PluginManager.requireLangPack('customimageuploader');
|
||||
|
||||
tinymce.create('tinymce.plugins.CustomImageUploader', {
|
||||
CustomImageUploader: function(ed, url) {
|
||||
var form;
|
||||
var iframe;
|
||||
var win;
|
||||
var throbber;
|
||||
var editor = ed;
|
||||
var textAreaElement = $('#' + ed.id);
|
||||
function showDialog() {
|
||||
var ctrl;
|
||||
var cycle;
|
||||
var el;
|
||||
var body;
|
||||
var containers;
|
||||
var inputs;
|
||||
win = editor.windowManager.open({
|
||||
title: I18n.t('tiny_mce.upload_window_title'),
|
||||
width: 520 + parseInt(editor.getLang(
|
||||
'customimageuploader.delta_width', 0
|
||||
), 10),
|
||||
height: 180 + parseInt(editor.getLang(
|
||||
'customimageuploader.delta_height', 0
|
||||
), 10),
|
||||
body: [
|
||||
{ type: 'iframe', url: 'javascript:void(0)' },
|
||||
{
|
||||
type: 'textbox',
|
||||
name: 'file',
|
||||
classes: 'image-loader',
|
||||
label: I18n.t('tiny_mce.upload_window_label'),
|
||||
subtype: 'file'
|
||||
},
|
||||
{
|
||||
type: 'container',
|
||||
classes: 'error',
|
||||
html: "<p style='color: #b94a48;'> </p>"
|
||||
},
|
||||
{ type: 'container', classes: 'throbber' }
|
||||
],
|
||||
buttons: [
|
||||
{
|
||||
text: I18n.t('tiny_mce.insert_btn'),
|
||||
onclick: insertImage,
|
||||
subtype: 'primary'
|
||||
},
|
||||
{
|
||||
text: I18n.t('general.cancel'),
|
||||
onclick: editor.windowManager.close
|
||||
}
|
||||
]
|
||||
}, {
|
||||
plugin_url: url
|
||||
});
|
||||
// Let's make image selection looks fancy
|
||||
$('<div class="image-selection-container">'
|
||||
+ '<div class="select_button btn btn-primary">' + I18n.t('tiny_mce.choose_file') + '</div>'
|
||||
+ '<input type="text" placeholder="' + I18n.t('tiny_mce.no_image_chosen') + '" disabled></input>'
|
||||
+ '</div>').insertAfter('.mce-image-loader')
|
||||
.click(() => { $('.mce-image-loader').click(); })
|
||||
.parent()
|
||||
.css('height', '32px');
|
||||
|
||||
$('.mce-image-loader')
|
||||
.change(e => {
|
||||
$(e.target).next().find('input[type=text]')[0].value = e.target.value.split(/(\\|\/)/g).pop();
|
||||
})
|
||||
.parent().find('label')
|
||||
.css('line-height', '32px')
|
||||
.css('height', '32px');
|
||||
|
||||
el = win.getEl();
|
||||
body = document.getElementById(el.id + '-body');
|
||||
containers = body.getElementsByClassName('mce-container');
|
||||
|
||||
win.off('submit');
|
||||
win.on('submit', insertImage);
|
||||
|
||||
iframe = win.find('iframe')[0];
|
||||
form = createElement('form', {
|
||||
action: editor.getParam(
|
||||
'customimageuploader_form_url',
|
||||
'/tinymce_assets'
|
||||
),
|
||||
target: iframe._id,
|
||||
method: 'POST',
|
||||
enctype: 'multipart/form-data',
|
||||
accept_charset: 'UTF-8'
|
||||
});
|
||||
inputs = form.getElementsByTagName('input');
|
||||
iframe.getEl().name = iframe._id;
|
||||
|
||||
// Create some needed hidden inputs
|
||||
form.appendChild(
|
||||
createElement(
|
||||
'input',
|
||||
{
|
||||
type: 'hidden',
|
||||
name: 'utf8',
|
||||
value: '✓'
|
||||
}
|
||||
)
|
||||
);
|
||||
form.appendChild(
|
||||
createElement(
|
||||
'input',
|
||||
{
|
||||
type: 'hidden',
|
||||
name: 'authenticity_token',
|
||||
value: getMetaContents('csrf-token')
|
||||
}
|
||||
)
|
||||
);
|
||||
form.appendChild(
|
||||
createElement(
|
||||
'input',
|
||||
{
|
||||
type: 'hidden',
|
||||
name: 'object_type',
|
||||
value: $(editor.getElement()).data('object-type')
|
||||
}
|
||||
)
|
||||
);
|
||||
form.appendChild(
|
||||
createElement(
|
||||
'input',
|
||||
{
|
||||
type: 'hidden',
|
||||
name: 'object_id',
|
||||
value: $(editor.getElement()).data('object-id')
|
||||
}
|
||||
)
|
||||
);
|
||||
form.appendChild(
|
||||
createElement(
|
||||
'input',
|
||||
{
|
||||
type: 'hidden',
|
||||
name: 'hint',
|
||||
value: editor.getParam('uploadimage_hint', '')
|
||||
}
|
||||
)
|
||||
);
|
||||
for (cycle = 0; cycle < containers.length; cycle += 1) {
|
||||
form.appendChild(containers[cycle]);
|
||||
}
|
||||
for (cycle = 0; cycle < inputs.length; cycle += 1) {
|
||||
ctrl = inputs[cycle];
|
||||
|
||||
if (ctrl.tagName.toLowerCase() === 'input' && ctrl.type !== 'hidden') {
|
||||
if (ctrl.type === 'file') {
|
||||
ctrl.name = 'file';
|
||||
|
||||
tinymce.DOM.setStyles(ctrl, {
|
||||
border: 0,
|
||||
boxShadow: 'none',
|
||||
webkitBoxShadow: 'none'
|
||||
});
|
||||
} else {
|
||||
ctrl.name = 'alt';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
body.appendChild(form);
|
||||
}
|
||||
|
||||
function insertImage() {
|
||||
var target = iframe.getEl();
|
||||
if (getInputValue('file') === '') {
|
||||
return handleError(I18n.t('tiny_mce.error_message'));
|
||||
}
|
||||
|
||||
throbber = new top.tinymce.ui.Throbber(win.getEl());
|
||||
throbber.show(throbber);
|
||||
clearErrors();
|
||||
|
||||
/* Add event listeners.
|
||||
* We remove the existing to avoid them being called twice in case
|
||||
* of errors and re-submitting afterwards.
|
||||
*/
|
||||
if (target.attachEvent) {
|
||||
target.detachEvent('onload', uploadDone);
|
||||
target.attachEvent('onload', uploadDone);
|
||||
} else {
|
||||
target.removeEventListener('load', uploadDone);
|
||||
target.addEventListener('load', uploadDone, false);
|
||||
}
|
||||
|
||||
form.submit();
|
||||
return true;
|
||||
}
|
||||
|
||||
function uploadDone() {
|
||||
var target = iframe.getEl();
|
||||
var doc;
|
||||
if (throbber) {
|
||||
throbber.hide();
|
||||
}
|
||||
if (target.document || target.contentDocument) {
|
||||
doc = target.contentDocument || target.contentWindow.document;
|
||||
handleResponse(doc.getElementsByTagName('body')[0].innerHTML);
|
||||
} else {
|
||||
handleError(I18n.t('tiny_mce.server_not_respond'));
|
||||
}
|
||||
}
|
||||
|
||||
function handleResponse(ret) {
|
||||
var json;
|
||||
var errorJson;
|
||||
try {
|
||||
json = tinymce.util.JSON.parse(ret);
|
||||
|
||||
if (json.error) {
|
||||
handleError(json.error.message);
|
||||
} else {
|
||||
editor.execCommand('mceInsertContent', false, buildHTML(json));
|
||||
editor.windowManager.close();
|
||||
updateActiveImages(ed);
|
||||
}
|
||||
} catch (e) {
|
||||
// hack that gets the server error message
|
||||
errorJson = JSON.parse($(ret).text());
|
||||
handleError(errorJson.error[0]);
|
||||
}
|
||||
}
|
||||
|
||||
function clearErrors() {
|
||||
var message = win.find('.error')[0].getEl();
|
||||
|
||||
if (message) {
|
||||
message.getElementsByTagName('p')[0].innerHTML = ' ';
|
||||
}
|
||||
}
|
||||
|
||||
function handleError(error) {
|
||||
var message = win.find('.error')[0].getEl();
|
||||
|
||||
if (message) {
|
||||
message
|
||||
.getElementsByTagName('p')[0]
|
||||
.innerHTML = editor.translate(error);
|
||||
}
|
||||
}
|
||||
function createElement(element, attributes) {
|
||||
var elResult = document.createElement(element);
|
||||
var property;
|
||||
for (property in attributes) {
|
||||
if (!(attributes[property] instanceof Function)) {
|
||||
elResult[property] = attributes[property];
|
||||
}
|
||||
}
|
||||
return elResult;
|
||||
}
|
||||
|
||||
function buildHTML(json) {
|
||||
var imgstr = "<img src='" + json.image.url + "'";
|
||||
imgstr += " data-mce-token='" + json.image.token + "'";
|
||||
imgstr += " alt='description-" + json.image.token + "' />";
|
||||
return imgstr;
|
||||
}
|
||||
|
||||
function getInputValue(name) {
|
||||
var inputValues = form.getElementsByTagName('input');
|
||||
var cycle;
|
||||
|
||||
for (cycle in inputValues) {
|
||||
if (inputValues[cycle].name === name) {
|
||||
return inputValues[cycle].value;
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
function getMetaContents(mn) {
|
||||
var m = document.getElementsByTagName('meta');
|
||||
var cycle;
|
||||
|
||||
for (cycle in m) {
|
||||
if (m[cycle].name === mn) {
|
||||
return m[cycle].content;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// Finding images in text
|
||||
function updateActiveImages() {
|
||||
var images;
|
||||
iframe = $('#' + editor.id).prev().find('.mce-edit-area iframe').contents();
|
||||
images = $.map($('img', iframe), e => {
|
||||
return e.dataset.mceToken;
|
||||
});
|
||||
$('#' + editor.id).next()[0].value = JSON.stringify(images);
|
||||
return JSON.stringify(images);
|
||||
}
|
||||
|
||||
// Add a button that opens a window
|
||||
editor.addButton('customimageuploader', {
|
||||
tooltip: I18n.t('tiny_mce.upload_window_label'),
|
||||
icon: 'image',
|
||||
onclick: showDialog
|
||||
});
|
||||
|
||||
// Adds a menu item to the tools menu
|
||||
editor.addMenuItem('customimageuploader', {
|
||||
text: I18n.t('tiny_mce.upload_window_label'),
|
||||
icon: 'image',
|
||||
context: 'insert',
|
||||
onclick: showDialog
|
||||
});
|
||||
|
||||
ed.on('NodeChange', () => {
|
||||
updateActiveImages(ed);
|
||||
});
|
||||
|
||||
textAreaElement.parent().find('input#tiny-mce-images').remove();
|
||||
$('<input type="hidden" id="tiny-mce-images" name="tiny_mce_images" value="[]">').insertAfter(textAreaElement);
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
|
||||
tinymce.PluginManager.add(
|
||||
'customimageuploader',
|
||||
tinymce.plugins.CustomImageUploader
|
||||
);
|
||||
})();
|
||||
|
|
@ -1,245 +0,0 @@
|
|||
(function() {
|
||||
'use strict';
|
||||
|
||||
tinymce.PluginManager.requireLangPack('customimageuploader');
|
||||
|
||||
tinymce.create('tinymce.plugins.CustomImageUploader', {
|
||||
CustomImageUploader: function(ed, url) {
|
||||
var form, iframe, win, throbber, editor = ed;
|
||||
function showDialog() {
|
||||
win = editor.windowManager.open({
|
||||
title: "<%= I18n.t 'tiny_mce.upload_window_title' %>",
|
||||
width: 520 + parseInt(editor.getLang(
|
||||
'customimageuploader.delta_width', 0
|
||||
), 10),
|
||||
height: 180 + parseInt(editor.getLang(
|
||||
'customimageuploader.delta_height', 0
|
||||
), 10),
|
||||
body: [
|
||||
{type: 'iframe', url: 'javascript:void(0)'},
|
||||
{type: 'textbox',
|
||||
name: 'file',
|
||||
label: "<%= I18n.t 'tiny_mce.upload_window_label' %>",
|
||||
subtype: 'file'},
|
||||
{type: 'container',
|
||||
classes: 'error',
|
||||
html: "<p style='color: #b94a48;'> </p>"},
|
||||
{type: 'container', classes: 'throbber'},
|
||||
],
|
||||
buttons: [
|
||||
{text: "<%= I18n.t 'tiny_mce.insert_btn' %>",
|
||||
onclick: insertImage,
|
||||
subtype: 'primary'},
|
||||
{text: "<%= I18n.t 'general.cancel' %>",
|
||||
onclick: editor.windowManager.close}
|
||||
],
|
||||
}, {
|
||||
plugin_url: url
|
||||
});
|
||||
|
||||
win.off('submit');
|
||||
win.on('submit', insertImage);
|
||||
|
||||
iframe = win.find('iframe')[0];
|
||||
form = createElement('form', {
|
||||
action: editor.getParam('customimageuploader_form_url',
|
||||
'<%= Rails.application
|
||||
.routes
|
||||
.url_helpers
|
||||
.tiny_mce_assets_path %>'),
|
||||
target: iframe._id,
|
||||
method: 'POST',
|
||||
enctype: 'multipart/form-data',
|
||||
accept_charset: 'UTF-8',
|
||||
});
|
||||
|
||||
iframe.getEl().name = iframe._id;
|
||||
|
||||
// Create some needed hidden inputs
|
||||
form.appendChild(createElement('input',
|
||||
{type: 'hidden',
|
||||
name: 'utf8',
|
||||
value: '✓'}));
|
||||
form.appendChild(createElement('input',
|
||||
{type: 'hidden',
|
||||
name: 'authenticity_token',
|
||||
value: getMetaContents('csrf-token')}));
|
||||
form.appendChild(createElement('input',
|
||||
{type: 'hidden',
|
||||
name: 'object_type',
|
||||
value: $(editor.getElement())
|
||||
.data('object-type')}));
|
||||
form.appendChild(createElement('input',
|
||||
{type: 'hidden',
|
||||
name: 'object_id',
|
||||
value: $(editor.getElement())
|
||||
.data('object-id')}));
|
||||
form.appendChild(createElement('input',
|
||||
{type: 'hidden',
|
||||
name: 'hint',
|
||||
value: editor.getParam('uploadimage_hint', '')}));
|
||||
|
||||
var el = win.getEl();
|
||||
var body = document.getElementById(el.id + '-body');
|
||||
|
||||
// Copy everything TinyMCE made into our form
|
||||
var containers = body.getElementsByClassName('mce-container');
|
||||
for(var i = 0; i < containers.length; i++) {
|
||||
form.appendChild(containers[i]);
|
||||
}
|
||||
|
||||
var inputs = form.getElementsByTagName('input');
|
||||
for(var i = 0; i < inputs.length; i++) {
|
||||
var ctrl = inputs[i];
|
||||
|
||||
if(ctrl.tagName.toLowerCase() == 'input' && ctrl.type != 'hidden') {
|
||||
if(ctrl.type == 'file') {
|
||||
ctrl.name = 'file';
|
||||
|
||||
tinymce.DOM.setStyles(ctrl, {
|
||||
'border': 0,
|
||||
'boxShadow': 'none',
|
||||
'webkitBoxShadow': 'none',
|
||||
});
|
||||
} else {
|
||||
ctrl.name = 'alt';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
body.appendChild(form);
|
||||
}
|
||||
|
||||
function insertImage() {
|
||||
if(getInputValue('file') == '') {
|
||||
return handleError("<%= I18n.t 'tiny_mce.error_message' %>");
|
||||
}
|
||||
|
||||
throbber = new top.tinymce.ui.Throbber(win.getEl());
|
||||
throbber.show();
|
||||
|
||||
clearErrors();
|
||||
|
||||
/* Add event listeners.
|
||||
* We remove the existing to avoid them being called twice in case
|
||||
* of errors and re-submitting afterwards.
|
||||
*/
|
||||
var target = iframe.getEl();
|
||||
if(target.attachEvent) {
|
||||
target.detachEvent('onload', uploadDone);
|
||||
target.attachEvent('onload', uploadDone);
|
||||
} else {
|
||||
target.removeEventListener('load', uploadDone);
|
||||
target.addEventListener('load', uploadDone, false);
|
||||
}
|
||||
|
||||
form.submit();
|
||||
}
|
||||
|
||||
function uploadDone() {
|
||||
if(throbber) {
|
||||
throbber.hide();
|
||||
}
|
||||
|
||||
var target = iframe.getEl();
|
||||
if(target.document || target.contentDocument) {
|
||||
var doc = target.contentDocument || target.contentWindow.document;
|
||||
handleResponse(doc.getElementsByTagName("body")[0].innerHTML);
|
||||
} else {
|
||||
handleError("<%= I18n.t 'tiny_mce.server_not_respond' %>");
|
||||
}
|
||||
}
|
||||
|
||||
function handleResponse(ret) {
|
||||
try {
|
||||
var json = tinymce.util.JSON.parse(ret);
|
||||
|
||||
if(json['error']) {
|
||||
handleError(json['error']['message']);
|
||||
} else {
|
||||
editor.execCommand('mceInsertContent', false, buildHTML(json));
|
||||
editor.windowManager.close();
|
||||
}
|
||||
} catch(e) {
|
||||
// hack that gets the server error message
|
||||
var error_json = JSON.parse($(ret).text());
|
||||
handleError(error_json['error'][0]);
|
||||
}
|
||||
}
|
||||
|
||||
function clearErrors() {
|
||||
var message = win.find('.error')[0].getEl();
|
||||
|
||||
if(message)
|
||||
message.getElementsByTagName('p')[0].innerHTML = ' ';
|
||||
}
|
||||
|
||||
function handleError(error) {
|
||||
var message = win.find('.error')[0].getEl();
|
||||
|
||||
if(message) {
|
||||
message
|
||||
.getElementsByTagName('p')[0]
|
||||
.innerHTML = editor.translate(error);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function createElement(element, attributes) {
|
||||
var el = document.createElement(element);
|
||||
for(var property in attributes) {
|
||||
if (!(attributes[property] instanceof Function)) {
|
||||
el[property] = attributes[property];
|
||||
}
|
||||
}
|
||||
|
||||
return el;
|
||||
}
|
||||
|
||||
function buildHTML(json) {
|
||||
var imgstr = "<img src='" + json['image']['url'] + "'";
|
||||
imgstr += " data-token='" + json['image']['token'] + "'"
|
||||
imgstr += " alt='description-" + json['image']['token'] + "'/>";
|
||||
return imgstr;
|
||||
}
|
||||
|
||||
function getInputValue(name) {
|
||||
var inputs = form.getElementsByTagName('input');
|
||||
|
||||
for(var i in inputs)
|
||||
if(inputs[i].name == name)
|
||||
return inputs[i].value;
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
function getMetaContents(mn) {
|
||||
var m = document.getElementsByTagName('meta');
|
||||
|
||||
for(var i in m)
|
||||
if(m[i].name == mn)
|
||||
return m[i].content;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// Add a button that opens a window
|
||||
editor.addButton('customimageuploader', {
|
||||
tooltip: "<%= I18n.t 'tiny_mce.upload_window_label' %>",
|
||||
icon: 'image',
|
||||
onclick: showDialog
|
||||
});
|
||||
|
||||
// Adds a menu item to the tools menu
|
||||
editor.addMenuItem('customimageuploader', {
|
||||
text: "<%= I18n.t 'tiny_mce.upload_window_label' %>",
|
||||
icon: 'image',
|
||||
context: 'insert',
|
||||
onclick: showDialog
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
tinymce.PluginManager.add('customimageuploader',
|
||||
tinymce.plugins.CustomImageUploader);
|
||||
})();
|
||||
|
|
@ -21,4 +21,5 @@
|
|||
@import "extend/bootstrap";
|
||||
@import "themes/scinote";
|
||||
@import "select2.min";
|
||||
@import "my_modules/protocols/*"
|
||||
@import "my_modules/protocols/*";
|
||||
@import "hooks/*";
|
||||
|
|
|
|||
37
app/assets/stylesheets/hooks/tinymce.scss
Normal file
37
app/assets/stylesheets/hooks/tinymce.scss
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
@import "constants";
|
||||
@import "mixins";
|
||||
|
||||
.mce-image-loader {
|
||||
display: none !important;
|
||||
|
||||
+ .image-selection-container {
|
||||
height: 28px;
|
||||
left: 140px;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 338px;
|
||||
|
||||
.select_button {
|
||||
background-color: $brand-success;
|
||||
border-color: $brand-success;
|
||||
color: $color-white;
|
||||
cursor: pointer;
|
||||
line-height: 20px;
|
||||
padding: 6px 12px;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
width: 76px;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
input[type=text] {
|
||||
border: 1px solid $color-gainsboro;
|
||||
border-radius: 4px;
|
||||
height: 30px;
|
||||
left: -5px;
|
||||
padding-left: 10px;
|
||||
position: relative;
|
||||
width: calc(100% - 100px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,8 +3,6 @@
|
|||
module Api
|
||||
module V1
|
||||
class ResultsController < BaseController
|
||||
include TinyMceHelper
|
||||
|
||||
before_action :load_team
|
||||
before_action :load_project
|
||||
before_action :load_experiment
|
||||
|
|
@ -52,15 +50,18 @@ module Api
|
|||
tiny_mce_asset_params.each do |t|
|
||||
image_params = t[:attributes]
|
||||
token = image_params[:file_token]
|
||||
unless result_text.text["[~tiny_mce_id:#{token}]"]
|
||||
unless result_text.text["data-mce-token=\"#{token}\""]
|
||||
raise ActiveRecord::RecordInvalid,
|
||||
I18n.t('api.core.errors.result_wrong_tinymce.detail')
|
||||
end
|
||||
image = Paperclip.io_adapters.for(image_params[:file_data])
|
||||
image.original_filename = image_params[:file_name]
|
||||
tiny_img = TinyMceAsset.create!(image: image, team: @team)
|
||||
result_text.text.sub!("[~tiny_mce_id:#{token}]",
|
||||
"[~tiny_mce_id:#{tiny_img.id}]")
|
||||
TinyMceAsset.create!(
|
||||
image: image,
|
||||
team: @team,
|
||||
object: result_text,
|
||||
saved: true
|
||||
)
|
||||
end
|
||||
end
|
||||
@result = Result.new(user: current_user,
|
||||
|
|
@ -69,12 +70,12 @@ module Api
|
|||
result_text: result_text,
|
||||
last_modified_by: current_user)
|
||||
@result.save! && result_text.save!
|
||||
link_tiny_mce_assets(result_text.text, result_text)
|
||||
end
|
||||
end
|
||||
|
||||
def result_params
|
||||
raise TypeError unless params.require(:data).require(:type) == 'results'
|
||||
|
||||
params.require(:data).require(:attributes).require(:name)
|
||||
params.permit(data: { attributes: :name })[:data][:attributes]
|
||||
end
|
||||
|
|
@ -95,7 +96,7 @@ module Api
|
|||
end
|
||||
file_tokens = prms.map { |p| p[:attributes][:file_token] }
|
||||
result_text_params[:text].scan(
|
||||
/\[~tiny_mce_id:(\w+)\]/
|
||||
/data-mce-token="(\w+)"/
|
||||
).flatten.each do |token|
|
||||
unless file_tokens.include?(token)
|
||||
raise ActiveRecord::RecordInvalid,
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ class MyModulesController < ApplicationController
|
|||
include Rails.application.routes.url_helpers
|
||||
include ActionView::Helpers::UrlHelper
|
||||
include ApplicationHelper
|
||||
include TinyMceHelper
|
||||
|
||||
before_action :load_vars,
|
||||
only: %i(show update destroy description due_date protocols
|
||||
|
|
@ -158,7 +157,6 @@ class MyModulesController < ApplicationController
|
|||
@my_module.assign_attributes(update_params)
|
||||
@my_module.last_modified_by = current_user
|
||||
description_changed = @my_module.description_changed?
|
||||
|
||||
if @my_module.archived_changed?(from: false, to: true)
|
||||
|
||||
saved = @my_module.archive(current_user)
|
||||
|
|
@ -196,9 +194,9 @@ class MyModulesController < ApplicationController
|
|||
)
|
||||
end
|
||||
else
|
||||
|
||||
saved = @my_module.save
|
||||
if saved and description_changed then
|
||||
TinyMceAsset.update_images(@my_module, params[:tiny_mce_images])
|
||||
Activity.create(
|
||||
type_of: :change_module_description,
|
||||
project: @my_module.experiment.project,
|
||||
|
|
@ -213,7 +211,6 @@ class MyModulesController < ApplicationController
|
|||
)
|
||||
end
|
||||
end
|
||||
|
||||
respond_to do |format|
|
||||
if restored
|
||||
format.html do
|
||||
|
|
@ -261,9 +258,10 @@ class MyModulesController < ApplicationController
|
|||
respond_to do |format|
|
||||
format.json do
|
||||
if @my_module.update(description: params.require(:my_module)[:description])
|
||||
TinyMceAsset.update_images(@my_module, params[:tiny_mce_images])
|
||||
render json: {
|
||||
html: custom_auto_link(
|
||||
generate_image_tag_from_token(@my_module.description, @my_module),
|
||||
@my_module.tinymce_render(:description),
|
||||
simple_format: false,
|
||||
tags: %w(img),
|
||||
team: current_team
|
||||
|
|
@ -282,9 +280,10 @@ class MyModulesController < ApplicationController
|
|||
respond_to do |format|
|
||||
format.json do
|
||||
if protocol.update(description: params.require(:protocol)[:description])
|
||||
TinyMceAsset.update_images(protocol, params[:tiny_mce_images])
|
||||
render json: {
|
||||
html: custom_auto_link(
|
||||
generate_image_tag_from_token(protocol.description, protocol),
|
||||
protocol.tinymce_render(:description),
|
||||
simple_format: false,
|
||||
tags: %w(img),
|
||||
team: current_team
|
||||
|
|
|
|||
|
|
@ -220,6 +220,7 @@ class ProtocolsController < ApplicationController
|
|||
|
||||
respond_to do |format|
|
||||
if @protocol.save
|
||||
TinyMceAsset.update_images(@protocol, params[:tiny_mce_images])
|
||||
format.json do
|
||||
render json: {
|
||||
url: edit_protocol_path(
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ class ResultTextsController < ApplicationController
|
|||
include ResultsHelper
|
||||
include ActionView::Helpers::UrlHelper
|
||||
include ApplicationHelper
|
||||
include TinyMceHelper
|
||||
include InputSanitizeHelper
|
||||
include Rails.application.routes.url_helpers
|
||||
|
||||
|
|
@ -32,9 +31,6 @@ class ResultTextsController < ApplicationController
|
|||
|
||||
def create
|
||||
@result_text = ResultText.new(result_params[:result_text_attributes])
|
||||
# gerate a tag that replaces img tag in database
|
||||
@result_text.text = parse_tiny_mce_asset_to_token(@result_text.text,
|
||||
@result_text)
|
||||
@result = Result.new(
|
||||
user: current_user,
|
||||
my_module: @my_module,
|
||||
|
|
@ -46,7 +42,7 @@ class ResultTextsController < ApplicationController
|
|||
respond_to do |format|
|
||||
if @result.save && @result_text.save
|
||||
# link tiny_mce_assets to the text result
|
||||
link_tiny_mce_assets(@result_text.text, @result_text)
|
||||
TinyMceAsset.update_images(@result_text, params[:tiny_mce_images])
|
||||
|
||||
result_annotation_notification
|
||||
# Generate activity
|
||||
|
|
@ -88,8 +84,6 @@ class ResultTextsController < ApplicationController
|
|||
end
|
||||
|
||||
def edit
|
||||
@result_text.text = generate_image_tag_from_token(@result_text.text,
|
||||
@result_text)
|
||||
respond_to do |format|
|
||||
format.json {
|
||||
render json: {
|
||||
|
|
@ -106,8 +100,7 @@ class ResultTextsController < ApplicationController
|
|||
update_params = result_params
|
||||
@result.last_modified_by = current_user
|
||||
@result.assign_attributes(update_params)
|
||||
@result_text.text = parse_tiny_mce_asset_to_token(@result_text.text,
|
||||
@result_text)
|
||||
|
||||
success_flash = t("result_texts.update.success_flash",
|
||||
module: @my_module.name)
|
||||
if @result.archived_changed?(from: false, to: true)
|
||||
|
|
@ -115,6 +108,7 @@ class ResultTextsController < ApplicationController
|
|||
success_flash = t("result_texts.archive.success_flash",
|
||||
module: @my_module.name)
|
||||
if saved
|
||||
TinyMceAsset.update_images(@result_text, params[:tiny_mce_images])
|
||||
Activity.create(
|
||||
type_of: :archive_result,
|
||||
project: @my_module.experiment.project,
|
||||
|
|
@ -134,6 +128,7 @@ class ResultTextsController < ApplicationController
|
|||
saved = @result.save
|
||||
|
||||
if saved then
|
||||
TinyMceAsset.update_images(@result_text, params[:tiny_mce_images])
|
||||
Activity.create(
|
||||
type_of: :edit_result,
|
||||
user: current_user,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
class StepsController < ApplicationController
|
||||
include ActionView::Helpers::TextHelper
|
||||
include ApplicationHelper
|
||||
include TinyMceHelper
|
||||
include StepsActions
|
||||
|
||||
before_action :load_vars, only: %i(edit update destroy show toggle_step_state
|
||||
|
|
@ -32,7 +31,6 @@ class StepsController < ApplicationController
|
|||
def create
|
||||
@step = Step.new(step_params)
|
||||
# gerate a tag that replaces img tag in database
|
||||
@step.description = parse_tiny_mce_asset_to_token(@step.description, @step)
|
||||
@step.completed = false
|
||||
@step.position = @protocol.number_of_steps
|
||||
@step.protocol = @protocol
|
||||
|
|
@ -61,7 +59,7 @@ class StepsController < ApplicationController
|
|||
end
|
||||
|
||||
# link tiny_mce_assets to the step
|
||||
link_tiny_mce_assets(@step.description, @step)
|
||||
TinyMceAsset.update_images(@step, params[:tiny_mce_images])
|
||||
|
||||
create_annotation_notifications(@step)
|
||||
|
||||
|
|
@ -120,7 +118,6 @@ class StepsController < ApplicationController
|
|||
end
|
||||
|
||||
def edit
|
||||
@step.description = generate_image_tag_from_token(@step.description, @step)
|
||||
respond_to do |format|
|
||||
format.json do
|
||||
render json: {
|
||||
|
|
@ -159,14 +156,9 @@ class StepsController < ApplicationController
|
|||
table.last_modified_by = current_user unless table.new_record?
|
||||
table.team = current_team
|
||||
end
|
||||
|
||||
# gerate a tag that replaces img tag in databases
|
||||
@step.description = parse_tiny_mce_asset_to_token(
|
||||
params[:step][:description],
|
||||
@step
|
||||
)
|
||||
|
||||
if @step.save
|
||||
|
||||
TinyMceAsset.update_images(@step, params[:tiny_mce_images])
|
||||
@step.reload
|
||||
|
||||
# generates notification on step upadate
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class TinyMceAssetsController < ApplicationController
|
||||
before_action :find_object
|
||||
|
||||
def create
|
||||
image = params.fetch(:file) { render_404 }
|
||||
tiny_img = TinyMceAsset.new(image: image,
|
||||
reference: @obj,
|
||||
team_id: current_team.id)
|
||||
team_id: current_team.id,
|
||||
saved: false)
|
||||
if tiny_img.save
|
||||
render json: {
|
||||
image: {
|
||||
|
|
@ -20,12 +21,4 @@ class TinyMceAssetsController < ApplicationController
|
|||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def find_object
|
||||
obj_type = params.fetch(:object_type) { render_404 }
|
||||
obj_id = params.fetch(:object_id) { render_404 }
|
||||
render_404 unless %w(step result_text).include? obj_type
|
||||
@obj = obj_type.classify.constantize.find_by_id(obj_id)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,91 +0,0 @@
|
|||
module TinyMceHelper
|
||||
def parse_tiny_mce_asset_to_token(text, obj)
|
||||
ids = []
|
||||
html = Nokogiri::HTML(remove_pasted_tokens(text))
|
||||
html.search('img').each do |img|
|
||||
next unless img['data-token']
|
||||
img_id = Base62.decode(img['data-token'])
|
||||
ids << img_id
|
||||
token = "[~tiny_mce_id:#{img_id}]"
|
||||
img.replace(token)
|
||||
next unless obj
|
||||
tiny_img = TinyMceAsset.find_by_id(img_id)
|
||||
tiny_img.reference = obj unless tiny_img.step || tiny_img.result_text
|
||||
tiny_img.save
|
||||
end
|
||||
destroy_removed_tiny_mce_assets(ids, obj) if obj
|
||||
html
|
||||
end
|
||||
|
||||
# @param pdf_export_ready is needed for wicked_pdf in export report action
|
||||
def generate_image_tag_from_token(text, obj, pdf_export_ready = false)
|
||||
return unless text
|
||||
regex = Constants::TINY_MCE_ASSET_REGEX
|
||||
text.gsub(regex) do |el|
|
||||
match = el.match(regex)
|
||||
img = TinyMceAsset.find_by_id(match[1])
|
||||
next unless img && check_image_permissions(obj, img)
|
||||
if pdf_export_ready
|
||||
tmp_f = Tempfile.open(img.image_file_name, Rails.root.join('tmp'))
|
||||
begin
|
||||
img.image.copy_to_local_file(:large, tmp_f.path)
|
||||
encoded_image = Base64.strict_encode64(tmp_f.read)
|
||||
"<img src='data:image/jpg;base64,#{encoded_image}'>"
|
||||
ensure
|
||||
tmp_f.close
|
||||
tmp_f.unlink
|
||||
end
|
||||
else
|
||||
image_tag(img.url,
|
||||
class: 'img-responsive',
|
||||
data: { token: Base62.encode(img.id) })
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def link_tiny_mce_assets(text, ref)
|
||||
ids = []
|
||||
regex = Constants::TINY_MCE_ASSET_REGEX
|
||||
text.gsub(regex) do |img|
|
||||
match = img.match(regex)
|
||||
tiny_img = TinyMceAsset.find_by_id(match[1])
|
||||
next unless tiny_img
|
||||
ids << tiny_img.id
|
||||
tiny_img.public_send("#{ref.class.to_s.underscore}=".to_sym, ref)
|
||||
tiny_img.save!
|
||||
end
|
||||
destroy_removed_tiny_mce_assets(ids, ref)
|
||||
end
|
||||
|
||||
def replace_tiny_mce_assets(text, img_ids)
|
||||
img_ids.each do |src_id, dest_id|
|
||||
regex = /\[~tiny_mce_id:#{src_id}\]/
|
||||
new_token = "[~tiny_mce_id:#{dest_id}]"
|
||||
text.sub!(regex, new_token)
|
||||
end
|
||||
text
|
||||
end
|
||||
|
||||
def destroy_removed_tiny_mce_assets(ids, ref)
|
||||
# need to check if the array is empty because if we pass the empty array
|
||||
# in the SQL query it will not work properly
|
||||
if ids.empty?
|
||||
ref.tiny_mce_assets.destroy_all
|
||||
else
|
||||
ref.tiny_mce_assets.where.not('id IN (?)', ids).destroy_all
|
||||
end
|
||||
end
|
||||
|
||||
def check_image_permissions(obj, img)
|
||||
if obj.class == Step
|
||||
img.step == obj
|
||||
elsif obj.class == ResultText
|
||||
img.result_text == obj
|
||||
end
|
||||
end
|
||||
|
||||
def remove_pasted_tokens(text)
|
||||
regex = Constants::TINY_MCE_ASSET_REGEX
|
||||
text.gsub(regex, ' ')
|
||||
end
|
||||
end
|
||||
39
app/models/concerns/tiny_mce_images.rb
Normal file
39
app/models/concerns/tiny_mce_images.rb
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module TinyMceImages
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
has_many :tiny_mce_assets,
|
||||
as: :object,
|
||||
class_name: :TinyMceAsset,
|
||||
dependent: :destroy
|
||||
|
||||
def prepare_for_report(field)
|
||||
description = self[field]
|
||||
tiny_mce_assets.each do |tm_asset|
|
||||
tmp_f = Tempfile.open(tm_asset.image_file_name, Rails.root.join('tmp'))
|
||||
begin
|
||||
tm_asset.image.copy_to_local_file(:large, tmp_f.path)
|
||||
encoded_tm_asset = Base64.strict_encode64(tmp_f.read)
|
||||
new_tm_asset = "<img class='img-responsive'
|
||||
src='data:image/jpg;base64,#{encoded_tm_asset}' >"
|
||||
html_description = Nokogiri::HTML(description)
|
||||
tm_asset_to_update = html_description.css(
|
||||
"img[data-mce-token=\"#{Base62.encode(tm_asset.id)}\"]"
|
||||
)[0]
|
||||
tm_asset_to_update.replace new_tm_asset
|
||||
description = html_description.css('body').inner_html.to_s
|
||||
ensure
|
||||
tmp_f.close
|
||||
tmp_f.unlink
|
||||
end
|
||||
end
|
||||
description
|
||||
end
|
||||
|
||||
def tinymce_render(field)
|
||||
TinyMceAsset.generate_url(self[field])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
class MyModule < ApplicationRecord
|
||||
include ArchivableModel, SearchableModel
|
||||
include TinyMceImages
|
||||
|
||||
enum state: Extends::TASKS_STATES
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
class Protocol < ApplicationRecord
|
||||
include SearchableModel
|
||||
include RenamingUtil
|
||||
extend TinyMceHelper
|
||||
include TinyMceImages
|
||||
|
||||
after_save :update_linked_children
|
||||
after_destroy :decrement_linked_children
|
||||
|
|
@ -333,13 +333,13 @@ class Protocol < ApplicationRecord
|
|||
step2.tables << table2
|
||||
end
|
||||
|
||||
# Copy tinyMce assets
|
||||
# Copy steps tinyMce assets
|
||||
cloned_img_ids = []
|
||||
step.tiny_mce_assets.each do |tiny_img|
|
||||
tiny_img2 = TinyMceAsset.new(
|
||||
image: tiny_img.image,
|
||||
estimated_size: tiny_img.estimated_size,
|
||||
step: step2,
|
||||
object: step2,
|
||||
team: dest.team
|
||||
)
|
||||
tiny_img2.save
|
||||
|
|
@ -347,9 +347,7 @@ class Protocol < ApplicationRecord
|
|||
step2.tiny_mce_assets << tiny_img2
|
||||
cloned_img_ids << [tiny_img.id, tiny_img2.id]
|
||||
end
|
||||
step2.update(
|
||||
description: replace_tiny_mce_assets(step2.description, cloned_img_ids)
|
||||
)
|
||||
TinyMceAsset.reload_images(cloned_img_ids)
|
||||
end
|
||||
|
||||
# Call clone helper
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
class ResultText < ApplicationRecord
|
||||
include TinyMceImages
|
||||
|
||||
auto_strip_attributes :text, nullify: false
|
||||
validates :text,
|
||||
presence: true,
|
||||
length: { maximum: Constants::RICH_TEXT_MAX_LENGTH }
|
||||
validates :result, presence: true
|
||||
belongs_to :result, inverse_of: :result_text, touch: true, optional: true
|
||||
has_many :tiny_mce_assets, inverse_of: :result_text, dependent: :destroy
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
class Step < ApplicationRecord
|
||||
include SearchableModel
|
||||
include TinyMceImages
|
||||
|
||||
auto_strip_attributes :name, :description, nullify: false
|
||||
validates :name,
|
||||
|
|
@ -30,7 +31,6 @@ class Step < ApplicationRecord
|
|||
has_many :tables, through: :step_tables
|
||||
has_many :report_elements, inverse_of: :step,
|
||||
dependent: :destroy
|
||||
has_many :tiny_mce_assets, inverse_of: :step, dependent: :destroy
|
||||
|
||||
accepts_nested_attributes_for :checklists,
|
||||
reject_if: :all_blank,
|
||||
|
|
|
|||
|
|
@ -1,8 +1,11 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class TinyMceAsset < ApplicationRecord
|
||||
attr_accessor :reference
|
||||
before_create :set_reference, optional: true
|
||||
after_create :update_estimated_size
|
||||
after_create :update_estimated_size, :self_destruct
|
||||
after_destroy :release_team_space
|
||||
after_save :update_description
|
||||
|
||||
belongs_to :team, inverse_of: :tiny_mce_assets, optional: true
|
||||
belongs_to :step, inverse_of: :tiny_mce_assets, touch: true, optional: true
|
||||
|
|
@ -10,6 +13,10 @@ class TinyMceAsset < ApplicationRecord
|
|||
inverse_of: :tiny_mce_assets,
|
||||
touch: true,
|
||||
optional: true
|
||||
|
||||
belongs_to :object, polymorphic: true,
|
||||
optional: true,
|
||||
inverse_of: :tiny_mce_assets
|
||||
has_attached_file :image,
|
||||
styles: { large: [Constants::LARGE_PIC_FORMAT, :jpg] },
|
||||
convert_options: { large: '-quality 100 -strip' }
|
||||
|
|
@ -21,20 +28,69 @@ class TinyMceAsset < ApplicationRecord
|
|||
validates_attachment :image,
|
||||
presence: true,
|
||||
size: {
|
||||
less_than: Rails.configuration.x.file_max_size_mb.megabytes
|
||||
less_than: Rails.configuration.x\
|
||||
.file_max_size_mb.megabytes
|
||||
}
|
||||
validates :estimated_size, presence: true
|
||||
|
||||
# When using S3 file upload, we can limit file accessibility with url signing
|
||||
def self.update_images(object, images)
|
||||
images = JSON.parse(images)
|
||||
current_images = object.tiny_mce_assets.pluck(:id)
|
||||
images_to_delete = current_images.reject do |x|
|
||||
(images.include? Base62.encode(x))
|
||||
end
|
||||
images.each do |image|
|
||||
image_to_update = find_by_id(Base62.decode(image))
|
||||
image_to_update&.update(object: object, saved: true)
|
||||
end
|
||||
where(id: images_to_delete).destroy_all
|
||||
rescue StandardError => e
|
||||
Rails.logger.error e.message
|
||||
end
|
||||
|
||||
def self.reload_images(images = [])
|
||||
images.each do |image|
|
||||
old_id = image.class == Array ? image[0] : image
|
||||
new_id = image.class == Array ? image[1] : image
|
||||
image_to_update = find_by_id(new_id)
|
||||
next unless image_to_update
|
||||
|
||||
object_field = data_fields[image_to_update.object_type]
|
||||
next unless image_to_update.object
|
||||
|
||||
old_description = Nokogiri::HTML(image_to_update.object[object_field])
|
||||
description_image = old_description.css(
|
||||
"img[data-mce-token=\"#{Base62.encode(old_id)}\"]"
|
||||
)
|
||||
description_image.attr('src').value = ''
|
||||
description_image.attr('data-mce-token').value = Base62.encode(new_id)
|
||||
description_image[0]['class'] = 'img-responsive'
|
||||
new_description = old_description.css('body').inner_html.to_s
|
||||
image_to_update.object.update(object_field => new_description)
|
||||
end
|
||||
end
|
||||
|
||||
def self.generate_url(description)
|
||||
description = Nokogiri::HTML(description)
|
||||
tm_assets = description.css('img')
|
||||
tm_assets.each do |tm_asset|
|
||||
asset_id = tm_asset.attr('data-mce-token')
|
||||
new_asset_url = find_by_id(Base62.decode(asset_id))
|
||||
if new_asset_url
|
||||
tm_asset.attributes['src'].value = new_asset_url.url
|
||||
tm_asset['class'] = 'img-responsive'
|
||||
end
|
||||
end
|
||||
description.css('body').inner_html.to_s
|
||||
end
|
||||
|
||||
def presigned_url(style = :large,
|
||||
download: false,
|
||||
timeout: Constants::URL_LONG_EXPIRE_TIME)
|
||||
if stored_on_s3?
|
||||
if download
|
||||
download_arg = 'attachment; filename=' + URI.escape(image_file_name)
|
||||
else
|
||||
download_arg = nil
|
||||
end
|
||||
download_arg = if download
|
||||
'attachment; filename=' + CGI.escape(image_file_name)
|
||||
end
|
||||
|
||||
signer = Aws::S3::Presigner.new(client: S3_BUCKET.client)
|
||||
signer.presigned_url(:get_object,
|
||||
|
|
@ -65,21 +121,44 @@ class TinyMceAsset < ApplicationRecord
|
|||
end
|
||||
end
|
||||
|
||||
def self.delete_unsaved_image(id)
|
||||
asset = find_by_id(id)
|
||||
asset.destroy if asset && !asset.saved
|
||||
end
|
||||
|
||||
def self.data_fields
|
||||
{
|
||||
'Step' => :description,
|
||||
'ResultText' => :text,
|
||||
'Protocol' => :description,
|
||||
'MyModule' => :description
|
||||
}
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def update_description
|
||||
TinyMceAsset.reload_images([id]) if object
|
||||
end
|
||||
|
||||
def self_destruct
|
||||
TinyMceAsset.delay(queue: :assets, run_at: 1.days.from_now).delete_unsaved_image(id)
|
||||
end
|
||||
|
||||
def update_estimated_size
|
||||
return if image_file_size.blank?
|
||||
|
||||
es = image_file_size * Constants::ASSET_ESTIMATED_SIZE_FACTOR
|
||||
update(estimated_size: es)
|
||||
Rails.logger.info "Asset #{id}: Estimated size successfully calculated"
|
||||
# update team space taken
|
||||
self.team.take_space(es)
|
||||
self.team.save
|
||||
team.take_space(es)
|
||||
team.save
|
||||
end
|
||||
|
||||
def release_team_space
|
||||
self.team.release_space(estimated_size)
|
||||
self.team.save
|
||||
team.release_space(estimated_size)
|
||||
team.save
|
||||
end
|
||||
|
||||
def set_reference
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
<div id="my_module_description_view"
|
||||
class="ql-editor tinymce-view"
|
||||
data-placeholder="<%= t('my_modules.module_header.empty_description_edit_label') %>">
|
||||
<%= custom_auto_link(generate_image_tag_from_token(@my_module.description, @my_module),
|
||||
<%= custom_auto_link(@my_module.tinymce_render(:description),
|
||||
simple_format: false,
|
||||
tags: %w(img),
|
||||
team: current_team) %>
|
||||
|
|
@ -18,7 +18,7 @@
|
|||
id: :my_module_description_textarea,
|
||||
class: 'hidden',
|
||||
hide_label: true,
|
||||
value: sanitize_input(@my_module.description),
|
||||
value: sanitize_input(@my_module.tinymce_render(:description)),
|
||||
data: {
|
||||
object_type: 'my_module',
|
||||
object_id: @my_module.id,
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@
|
|||
<% if can_manage_module?(@my_module) %>
|
||||
<%= render partial: "description_form" %>
|
||||
<% elsif @my_module.description.present? %>
|
||||
<%= custom_auto_link(generate_image_tag_from_token(@my_module.description, @my_module),
|
||||
<%= custom_auto_link(@my_module.description,
|
||||
simple_format: false,
|
||||
tags: %w(img),
|
||||
team: current_team) %>
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@
|
|||
<% if can_manage_module?(@my_module) %>
|
||||
<%= render partial: "my_modules/protocols/protocol_description_form" %>
|
||||
<% elsif @my_module.protocol.description.present? %>
|
||||
<%= custom_auto_link(generate_image_tag_from_token(@my_module.protocol.description, @my_module.protocol),
|
||||
<%= custom_auto_link(@my_module.protocol.description,
|
||||
simple_format: false,
|
||||
tags: %w(img),
|
||||
team: current_team) %>
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
<div id="protocol_description_view"
|
||||
class="ql-editor tinymce-view"
|
||||
data-placeholder="<%= t('my_modules.protocols.protocol_status_bar.empty_description_edit_label') %>">
|
||||
<%= custom_auto_link(generate_image_tag_from_token(@my_module.protocol.description, @my_module.protocol),
|
||||
<%= custom_auto_link(@my_module.protocol.tinymce_render(:description),
|
||||
simple_format: false,
|
||||
tags: %w(img),
|
||||
team: current_team) %>
|
||||
|
|
@ -18,7 +18,7 @@
|
|||
id: :protocol_description_textarea,
|
||||
class: 'hidden',
|
||||
hide_label: true,
|
||||
value: sanitize_input(@my_module.protocol.description),
|
||||
value: sanitize_input(@my_module.protocol.tinymce_render(:description)),
|
||||
data: {
|
||||
object_type: 'protocol',
|
||||
object_id: @my_module.protocol.id,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<% if @protocol.description.present? %>
|
||||
<strong><%= @protocol.description %></strong>
|
||||
<strong><%= @protocol.tinymce_render(:description) %></strong>
|
||||
<% else %>
|
||||
<em><%= t("protocols.header.no_description") %></em>
|
||||
<% end %>
|
||||
|
|
@ -85,7 +85,7 @@
|
|||
<em><%= t("protocols.steps.no_description") %></em>
|
||||
<% else %>
|
||||
<div class="ql-editor">
|
||||
<%= sanitize_input(generate_image_tag_from_token(step.description, step), ['img']) %>
|
||||
<%= sanitize_input(step.tinymce_render(:description), ['img']) %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@
|
|||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<% if my_module.description.present? %>
|
||||
<%= custom_auto_link(my_module.description, team: current_team) %>
|
||||
<%= custom_auto_link(my_module.tinymce_render(:description), team: current_team) %>
|
||||
<% else %>
|
||||
<em><%=t "projects.reports.elements.module.no_description" %></em>
|
||||
<% end %>
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@
|
|||
<div class="report-element-body">
|
||||
<div class="row">
|
||||
<div class="col-xs-12 text-container ql-editor">
|
||||
<%= custom_auto_link(generate_image_tag_from_token(result_text.text, result_text, pdf_export_ready),
|
||||
<%= custom_auto_link(result_text.prepare_for_report(:text),
|
||||
team: current_team,
|
||||
simple_format: false,
|
||||
tags: %w(img)) %>
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@
|
|||
<div class="row">
|
||||
<div class="col-xs-12 ql-editor">
|
||||
<% if strip_tags(step.description).present? %>
|
||||
<%= custom_auto_link(generate_image_tag_from_token(step.description, step, pdf_export_ready),
|
||||
<%= custom_auto_link(step.prepare_for_report(:description),
|
||||
team: current_team,
|
||||
simple_format: false,
|
||||
tags: %w(img)) %>
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
<%= f.fields_for :result_text do |ff| %>
|
||||
<div class="form-group">
|
||||
<%= ff.tiny_mce_editor(:text,
|
||||
value: @result.result_text.text,
|
||||
value: @result.result_text.tinymce_render(:text),
|
||||
data: { object_type: 'result_text',
|
||||
object_id: @result.result_text.id }) %>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<div class="ql-editor">
|
||||
<%= custom_auto_link(generate_image_tag_from_token(result.result_text.text, result.result_text),
|
||||
<%= custom_auto_link(result.result_text.text,
|
||||
simple_format: false,
|
||||
tags: %w(img),
|
||||
team: current_team) %>
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@
|
|||
<%= f.tiny_mce_editor(:description,
|
||||
id: :step_description_textarea,
|
||||
hide_label: false,
|
||||
value: sanitize_input(@step.description),
|
||||
value: sanitize_input(@step.tinymce_render(:description)),
|
||||
data: {
|
||||
object_type: 'step',
|
||||
object_id: @step.id,
|
||||
|
|
|
|||
|
|
@ -49,11 +49,11 @@
|
|||
<div class="panel-body">
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<% if strip_tags(step.description).blank? %>
|
||||
<% if step.description.blank? %>
|
||||
<em><%= t('protocols.steps.no_description') %></em>
|
||||
<% else %>
|
||||
<div class="ql-editor">
|
||||
<%= custom_auto_link(generate_image_tag_from_token(step.description, step),
|
||||
<%= custom_auto_link(step.tinymce_render(:description),
|
||||
simple_format: false,
|
||||
tags: %w(img),
|
||||
team: current_team) %>
|
||||
|
|
|
|||
|
|
@ -221,7 +221,7 @@ class Constants
|
|||
|
||||
WHITELISTED_TAGS = %w(
|
||||
a b strong i em li ul ol h1 del ins h2 h3 h4 h5 h6 br sub sup p code hr div
|
||||
span u s blockquote pre col colgroup table thead tbody th tr td
|
||||
span u s blockquote pre col colgroup table thead tbody th tr td img
|
||||
).freeze
|
||||
|
||||
WHITELISTED_ATTRIBUTES = [
|
||||
|
|
|
|||
|
|
@ -1971,6 +1971,8 @@ en:
|
|||
tiny_mce:
|
||||
upload_window_title: 'Insert an image from your computer'
|
||||
upload_window_label: 'Choose an image'
|
||||
choose_file: 'Choose file'
|
||||
no_image_chosen: 'No image chosen'
|
||||
insert_btn: 'Insert'
|
||||
error_message: 'You must choose a file'
|
||||
server_not_respond: "Didn't get a response from the server"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddColumnSavedToTinyMceAsset < ActiveRecord::Migration[5.1]
|
||||
def change
|
||||
add_column :tiny_mce_assets, :saved, :boolean, default: true
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddPolymorphicToTinyMceAsset < ActiveRecord::Migration[5.1]
|
||||
def change
|
||||
add_reference :tiny_mce_assets, :object, polymorphic: true
|
||||
end
|
||||
end
|
||||
|
|
@ -10,7 +10,7 @@
|
|||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(version: 20190304153544) do
|
||||
ActiveRecord::Schema.define(version: 20190308092130) do
|
||||
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "plpgsql"
|
||||
|
|
@ -744,6 +744,10 @@ ActiveRecord::Schema.define(version: 20190304153544) do
|
|||
t.integer "result_text_id"
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.boolean "saved", default: true
|
||||
t.string "object_type"
|
||||
t.bigint "object_id"
|
||||
t.index ["object_type", "object_id"], name: "index_tiny_mce_assets_on_object_type_and_object_id"
|
||||
t.index ["result_text_id"], name: "index_tiny_mce_assets_on_result_text_id"
|
||||
t.index ["step_id"], name: "index_tiny_mce_assets_on_step_id"
|
||||
t.index ["team_id"], name: "index_tiny_mce_assets_on_team_id"
|
||||
|
|
|
|||
|
|
@ -126,7 +126,9 @@ RSpec.describe "Api::V1::ResultsController", type: :request do
|
|||
included: [
|
||||
{ type: 'result_texts',
|
||||
attributes: {
|
||||
text: 'Result text 1 [~tiny_mce_id:a1]'
|
||||
text: 'Result text 1 <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAA'\
|
||||
'AACCAIAAAD91JpzAAAACXBIWXMAAC4jAAAuIwF4pT92AAAAE0lE'\
|
||||
'QVQIHWP8//8/AwMDExADAQAkBgMBOOSShwAAAABJRU5ErkJggg==" data-mce-token="a1">'
|
||||
} },
|
||||
{ type: 'tiny_mce_assets',
|
||||
attributes: {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue