From fef691d30f7d7ed08aa452443df68d61fc3b478a Mon Sep 17 00:00:00 2001 From: Anton Ignatov Date: Thu, 2 May 2019 16:12:25 +0200 Subject: [PATCH] Add error handling and secutiry checks --- app/assets/javascripts/marvinjs_editor.js | 1 - .../javascripts/sitewide/file_preview.js | 43 +++--- app/assets/javascripts/sitewide/tiny_mce.js | 139 ++++++++++-------- .../marvin_js_assets_controller.rb | 36 ++++- app/models/marvin_js_asset.rb | 19 ++- app/views/steps/attachments/_list.html.erb | 2 +- config/locales/en.yml | 5 +- 7 files changed, 144 insertions(+), 101 deletions(-) diff --git a/app/assets/javascripts/marvinjs_editor.js b/app/assets/javascripts/marvinjs_editor.js index 0b307d568..4e11f4374 100644 --- a/app/assets/javascripts/marvinjs_editor.js +++ b/app/assets/javascripts/marvinjs_editor.js @@ -200,7 +200,6 @@ var MarvinJsEditor = (function() { type: 'PUT', success: function(json) { config.image[0].src = json.url; - config.saveButton.removeClass('hidden'); $(marvinJsModal).modal('hide'); } }); diff --git a/app/assets/javascripts/sitewide/file_preview.js b/app/assets/javascripts/sitewide/file_preview.js index b6830e444..9f12242fe 100644 --- a/app/assets/javascripts/sitewide/file_preview.js +++ b/app/assets/javascripts/sitewide/file_preview.js @@ -561,31 +561,32 @@ var FilePreviewModal = (function() { MarvinJsEditor().create_preview(src, target); MarvinJsEditor().create_download_link(src, link, name); modal.find('.file-name').text(name); - - if (!readOnly) { - 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: src.val(), - name: name, - marvinUrl: sketch.dataset.updateUrl, - reloadImage: { - src: src, - sketch: sketch - } + $.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(); + ev.stopPropagation(); + modal.modal('hide'); + MarvinJsEditor().open({ + mode: 'edit', + data: src.val(), + name: name, + marvinUrl: sketch.dataset.updateUrl, + reloadImage: { + src: src, + sketch: sketch + } + }); }); - }); - } else { - modal.find('.file-edit-link').css('display', 'none'); - } + } else { + modal.find('.file-edit-link').css('display', 'none'); + } + }); } return Object.freeze({ init: initPreviewModal, - imageEditor: initImageEditor, + imageEditor: initImageEditor }); }(window)); diff --git a/app/assets/javascripts/sitewide/tiny_mce.js b/app/assets/javascripts/sitewide/tiny_mce.js index 0007b6f3c..53b0fcc06 100644 --- a/app/assets/javascripts/sitewide/tiny_mce.js +++ b/app/assets/javascripts/sitewide/tiny_mce.js @@ -37,6 +37,78 @@ var TinyMCE = (function() { } } + function initImageToolBar(editor) { + var editorForm = $(editor.getContainer()).closest('form'); + var editorContainer = $(editor.getContainer()); + var menuBar = editorForm.find('.mce-menubar.mce-toolbar.mce-first .mce-flow-layout'); + var editorToolbar = editorForm.find('.mce-top-part'); + var editorIframe = $('#' + editor.id).prev().find('.mce-edit-area iframe'); + $('').appendTo(editorToolbar.find('.mce-stack-layout')); + editorIframe.contents().click(function() { + var marvinJsEdit; + setTimeout(() => { + var image = editorIframe.contents().find('img[data-mce-selected="1"]'); + var editLink; + var imageEditorLink; + if (image.length > 0) { + image.on('load', function() { + editorContainer.find('.tinymce-save-button').removeClass('hidden'); + }); + 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'); + + // Edit link + editLink = editorContainer.find('.tinymce-active-object-handler .file-edit-link'); + if (image[0].dataset.sourceId) { + editLink.css('display', 'inline-block'); + marvinJsEdit = (image[0].dataset.sourceType === 'MarvinJsAsset' && 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, + image: image + }); + } + }); + } else { + 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'); + } + }, 100); + }); + } + // returns a public API for TinyMCE editor return Object.freeze({ init: function(selector, mceConfig = {}) { @@ -153,6 +225,9 @@ var TinyMCE = (function() { moveToolbar(editor, editorToolbar, editorToolbaroffset); }); + // Init image toolbar + initImageToolBar(editor); + // Init Save button editorForm .find('.tinymce-save-button') @@ -166,70 +241,6 @@ var TinyMCE = (function() { editorForm.submit(); }); - // Init image helpers - $('').appendTo(editorToolbar.find('.mce-stack-layout')); - editorIframe.contents().click(function() { - var marvinJsEdit; - setTimeout(() => { - var image = editorIframe.contents().find('img[data-mce-selected="1"]'); - var editLink; - var imageEditorLink; - if (image.length > 0) { - 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'); - - // Edit link - editLink = editorContainer.find('.tinymce-active-object-handler .file-edit-link'); - if (image[0].dataset.sourceId) { - editLink.css('display', 'inline-block'); - marvinJsEdit = (image[0].dataset.sourceType === 'MarvinJsAsset' && 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, - image: image, - saveButton: editorContainer.find('.tinymce-save-button') - }); - } - }); - } else { - 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'); - } - }, 100); - }); - // After save action editorForm .on('ajax:success', function(ev, data) { diff --git a/app/controllers/marvin_js_assets_controller.rb b/app/controllers/marvin_js_assets_controller.rb index 5652be0a7..465210ee6 100644 --- a/app/controllers/marvin_js_assets_controller.rb +++ b/app/controllers/marvin_js_assets_controller.rb @@ -20,23 +20,47 @@ class MarvinJsAssetsController < ApplicationController source_type: new_asset.class.name } }, content_type: 'text/html' - else + elsif new_asset render json: new_asset + else + render json: new_asset.errors, status: :unprocessable_entity end end def show - render json: (MarvinJsAsset.find_by_id(params[:id]) || {}) + 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 = MarvinJsAsset.find(params[:id]) - sketch.destroy - render json: sketch + 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 + end end def update - render json: MarvinJsAsset.update_sketch(marvin_params) + sketch = MarvinJsAsset.update_sketch(marvin_params, current_team) + if sketch + render json: sketch + else + render json: { error: t('marvinjs.no_sketches_found') }, status: :unprocessable_entity + end end def team_sketches diff --git a/app/models/marvin_js_asset.rb b/app/models/marvin_js_asset.rb index 2524bc762..6c8ec222c 100644 --- a/app/models/marvin_js_asset.rb +++ b/app/models/marvin_js_asset.rb @@ -1,6 +1,11 @@ # 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 @@ -17,28 +22,30 @@ class MarvinJsAsset < ApplicationRecord def self.add_sketch(values, team) if values[:object_type] == 'TinyMceAsset' - tiny_mce_img = TinyMceAsset.new( + tiny_mce_img = TinyMceAsset.create( object: nil, team_id: team.id, saved: false, image: values[:image], image_file_name: "#{name}.jpg" ) - tiny_mce_img.save! - 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) - sketch = MarvinJsAsset.find(values[:id]) - sketch.update(values.except(:image, :object_type, :id)) + def self.update_sketch(values, team) + sketch = team.marvin_js_assets.find(values[:id]) + return false unless sketch + if values[:object_type] == 'TinyMceAsset' image = TinyMceAsset.find(sketch.object_id) image.update(image: values[:image]) return { url: image.url(:large) } end + values[:name] = I18n.t('marvinjs.new_sketch') if values[:name].empty? + sketch.update(values.except(:image, :object_type, :id)) sketch end end diff --git a/app/views/steps/attachments/_list.html.erb b/app/views/steps/attachments/_list.html.erb index 703d97de5..1b8212492 100644 --- a/app/views/steps/attachments/_list.html.erb +++ b/app/views/steps/attachments/_list.html.erb @@ -10,7 +10,7 @@
- <% if MarvinJsAsset.enabled? %> + <% if MarvinJsAsset.enabled? && (can_manage_protocol_in_module?(step.protocol) || can_manage_protocol_in_repository?(step.protocol)) %> <%= render partial: '/assets/marvinjs/create_marvin_sketch_button.html.erb', locals: { element_id: step.id, element_type: 'Step', sketch_container: ".attacments#att-#{step.id}" } %> <% end %> diff --git a/config/locales/en.yml b/config/locales/en.yml index 5af4b3bb9..7a1d1fa7b 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -2146,5 +2146,6 @@ en: new_sketch: "New sketch" new_button: "New chemical drawing" checmical_drawing: "Chemical drawings" - team_drawings: Team drawings - task: Task \ No newline at end of file + team_drawings: "Team drawings" + task: "Task" + no_sketches_found: "No sketches found" \ No newline at end of file