mirror of
https://github.com/scinote-eln/scinote-web.git
synced 2024-09-20 14:45:56 +08:00
Basic OVE editor implementation [SCI-8929]
This commit is contained in:
parent
a6c2a3aa59
commit
76a0f433cd
2
app/assets/javascripts/i18n_bundle.js
Normal file
2
app/assets/javascripts/i18n_bundle.js
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
//= require i18n.js
|
||||||
|
//= require i18n/translations
|
21
app/assets/javascripts/sitewide/iframe_modal.js
Normal file
21
app/assets/javascripts/sitewide/iframe_modal.js
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
/* global iFrameModal */
|
||||||
|
// General-purpose iframe modal. For closing the modal, you need will to take care of triggering
|
||||||
|
// the 'hide' event on the modal itself, example from inside the iframe:
|
||||||
|
// parent.document.getElementById('iFrameModal').dispatchEvent(new Event('hide'));
|
||||||
|
|
||||||
|
$(document).on('turbolinks:load', function() {
|
||||||
|
window.iFrameModal = document.getElementById('iFrameModal');
|
||||||
|
let iFrameModalFrame = document.getElementById('iFrameModalFrame');
|
||||||
|
|
||||||
|
window.showIFrameModal = (url) => {
|
||||||
|
iFrameModalFrame.setAttribute('src', url);
|
||||||
|
iFrameModal.classList.remove('hidden');
|
||||||
|
iFrameModal.dispatchEvent(new Event('shown'));
|
||||||
|
};
|
||||||
|
|
||||||
|
iFrameModal.addEventListener('hide', () => {
|
||||||
|
iFrameModal.classList.add('hidden');
|
||||||
|
iFrameModalFrame.removeAttribute('src');
|
||||||
|
iFrameModal.dispatchEvent(new Event('hidden'));
|
||||||
|
});
|
||||||
|
});
|
4
app/assets/stylesheets/sn_icon_font.css
Normal file
4
app/assets/stylesheets/sn_icon_font.css
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
/*
|
||||||
|
*= require sn-icon-font
|
||||||
|
*= require sn-inter-font
|
||||||
|
*/
|
123
app/controllers/gene_sequence_assets_controller.rb
Normal file
123
app/controllers/gene_sequence_assets_controller.rb
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class GeneSequenceAssetsController < ApplicationController
|
||||||
|
include ActiveStorage::SetCurrent
|
||||||
|
|
||||||
|
skip_before_action :verify_authenticity_token
|
||||||
|
|
||||||
|
before_action :load_vars, except: %i(new create)
|
||||||
|
before_action :load_create_vars, only: %i(new create)
|
||||||
|
|
||||||
|
before_action :check_read_permission
|
||||||
|
before_action :check_manage_permission, only: %i(new update create)
|
||||||
|
|
||||||
|
def new
|
||||||
|
render :edit, layout: false
|
||||||
|
end
|
||||||
|
|
||||||
|
def edit
|
||||||
|
@file_url = rails_representation_url(@asset.file)
|
||||||
|
@file_name = @asset.render_file_name
|
||||||
|
render :edit, layout: false
|
||||||
|
end
|
||||||
|
|
||||||
|
def create
|
||||||
|
save_asset!
|
||||||
|
head :ok
|
||||||
|
end
|
||||||
|
|
||||||
|
def update
|
||||||
|
save_asset!
|
||||||
|
head :ok
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def save_asset!
|
||||||
|
ActiveRecord::Base.transaction do
|
||||||
|
ensure_asset!
|
||||||
|
|
||||||
|
@asset.file.purge
|
||||||
|
@asset.preview_image.purge
|
||||||
|
|
||||||
|
@asset.file.attach(
|
||||||
|
io: StringIO.new(params[:sequence_data].to_json),
|
||||||
|
filename: "#{params[:sequence_name]}.json"
|
||||||
|
)
|
||||||
|
|
||||||
|
@asset.preview_image.attach(
|
||||||
|
io: StringIO.new(Base64.decode64(params[:base64_image].split(',').last)),
|
||||||
|
filename: "#{params[:sequence_name]}.png"
|
||||||
|
)
|
||||||
|
|
||||||
|
file = @asset.file
|
||||||
|
|
||||||
|
file.blob.metadata['asset_type'] = 'gene_sequence'
|
||||||
|
file.blob.metadata['name'] = params[:sequence_name]
|
||||||
|
file.save!
|
||||||
|
|
||||||
|
@asset.save!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def ensure_asset!
|
||||||
|
return if @asset
|
||||||
|
return unless @parent
|
||||||
|
|
||||||
|
@asset = @parent.assets.create!(last_modified_by: current_user, team: current_team)
|
||||||
|
end
|
||||||
|
|
||||||
|
def load_vars
|
||||||
|
@asset = current_team.assets.find_by(id: params[:id])
|
||||||
|
return render_404 unless @asset
|
||||||
|
|
||||||
|
@parent ||= @asset.step
|
||||||
|
@parent ||= @asset.result
|
||||||
|
|
||||||
|
case @parent
|
||||||
|
when Step
|
||||||
|
@protocol = @parent.protocol
|
||||||
|
when Result
|
||||||
|
@my_module = @parent.my_module
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def load_create_vars
|
||||||
|
@parent = case params[:parent_type]
|
||||||
|
when 'Step'
|
||||||
|
Step.find_by(id: params[:parent_id])
|
||||||
|
when 'Result'
|
||||||
|
Result.find_by(id: params[:parent_id])
|
||||||
|
end
|
||||||
|
|
||||||
|
case @parent
|
||||||
|
when Step
|
||||||
|
@protocol = @parent.protocol
|
||||||
|
when Result
|
||||||
|
@result = @parent
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def check_read_permission
|
||||||
|
case @parent
|
||||||
|
when Step
|
||||||
|
return render_403 unless can_read_protocol_in_module?(@protocol) ||
|
||||||
|
can_read_protocol_in_repository?(@protocol)
|
||||||
|
when Result
|
||||||
|
return render_403 unless can_read_my_module?(@my_module)
|
||||||
|
else
|
||||||
|
render_403
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def check_manage_permission
|
||||||
|
case @parent
|
||||||
|
when Step
|
||||||
|
return render_403 unless can_manage_step?(@parent)
|
||||||
|
when Result
|
||||||
|
return render_403 unless can_manage_my_module?(@parent)
|
||||||
|
else
|
||||||
|
render_403
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
2
app/javascript/packs/open_vector_editor.js
Normal file
2
app/javascript/packs/open_vector_editor.js
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
import './ove/script.js';
|
||||||
|
import './ove/style.css';
|
222154
app/javascript/packs/ove/script.js
Normal file
222154
app/javascript/packs/ove/script.js
Normal file
File diff suppressed because one or more lines are too long
12005
app/javascript/packs/ove/style.css
Normal file
12005
app/javascript/packs/ove/style.css
Normal file
File diff suppressed because one or more lines are too long
11
app/javascript/packs/vue/open_vector_editor.js
Normal file
11
app/javascript/packs/vue/open_vector_editor.js
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import TurbolinksAdapter from 'vue-turbolinks';
|
||||||
|
import Vue from 'vue/dist/vue.esm';
|
||||||
|
import OpenVectorEditor from '../../vue/ove/OpenVectorEditor.vue';
|
||||||
|
|
||||||
|
Vue.use(TurbolinksAdapter);
|
||||||
|
Vue.prototype.i18n = window.I18n;
|
||||||
|
|
||||||
|
new Vue({
|
||||||
|
el: '#open-vector-editor',
|
||||||
|
components: { OpenVectorEditor }
|
||||||
|
});
|
106
app/javascript/vue/ove/OpenVectorEditor.vue
Normal file
106
app/javascript/vue/ove/OpenVectorEditor.vue
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
<template>
|
||||||
|
<div class="ove-wrapper flex flex-col h-screen">
|
||||||
|
<div class="ove-header flex justify-between">
|
||||||
|
<span class="file-name flex items-center ml-3">
|
||||||
|
<div class="sci-input-container">
|
||||||
|
<input v-model="sequenceName" class="sci-input-field" type="text" />
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
<div class="ove-buttons">
|
||||||
|
<button @click="saveAndClose" class="btn btn-light">
|
||||||
|
<i class="sn-icon sn-icon-save"></i>
|
||||||
|
{{ i18n.t('SaveClose') }}
|
||||||
|
</button>
|
||||||
|
<button @click="closeModal" class="preview-close btn btn-light icon-btn">
|
||||||
|
<i class="sn-icon sn-icon-close"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div ref="container" class="ove-container w-full h-full">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import axios from 'axios';
|
||||||
|
import { blobToBase64 } from '../shared/blobToBase64.js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'OpenVectorEditor',
|
||||||
|
props: {
|
||||||
|
fileUrl: { type: String },
|
||||||
|
fileName: { type: String },
|
||||||
|
updateUrl: { type: String }
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
editor: null,
|
||||||
|
sequenceName: null,
|
||||||
|
closeAfterSave: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
sequenceName() {
|
||||||
|
if (this.editor && this.editor.getState()) {
|
||||||
|
this.editor.updateEditor({
|
||||||
|
sequenceData: { ...this.editor.getState().sequenceData, name: this.sequenceName }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.editor = window.createVectorEditor(this.$refs.container, {
|
||||||
|
onSave: this.saveFile,
|
||||||
|
generatePng: true
|
||||||
|
});
|
||||||
|
this.sequenceName = this.fileName || this.i18n.t('open_vector_editor.default_sequence_name');
|
||||||
|
|
||||||
|
if (this.fileUrl) {
|
||||||
|
this.loadFile();
|
||||||
|
} else {
|
||||||
|
this.editor.updateEditor(
|
||||||
|
{
|
||||||
|
sequenceData: { circular: true, name: this.sequenceName },
|
||||||
|
readOnly: false
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
loadFile() {
|
||||||
|
fetch(this.fileUrl).then((response) => response.json()).then(
|
||||||
|
(json) => this.editor.updateEditor(
|
||||||
|
{
|
||||||
|
sequenceData: json,
|
||||||
|
readOnly: false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
saveAndClose() {
|
||||||
|
this.closeAfterSave = close;
|
||||||
|
document.querySelector('[data-test=saveTool]').click();
|
||||||
|
},
|
||||||
|
saveFile(opts, sequenceDataToSave, editorState, onSuccessCallback) {
|
||||||
|
blobToBase64(opts.pngFile).then((base64image) => {
|
||||||
|
(this.fileUrl ? axios.patch : axios.post)(
|
||||||
|
this.updateUrl,
|
||||||
|
{
|
||||||
|
sequence_data: sequenceDataToSave,
|
||||||
|
sequence_name: this.sequenceName,
|
||||||
|
base64_image: base64image
|
||||||
|
}
|
||||||
|
).then(() => {
|
||||||
|
parent.document.getElementById('iFrameModal').dispatchEvent(new Event('sequenceSaved'));
|
||||||
|
if (this.closeAfterSave) this.closeModal();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
closeModal() {
|
||||||
|
if (parent !== window) {
|
||||||
|
parent.document.getElementById('iFrameModal').dispatchEvent(new Event('hide'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -141,6 +141,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
this.initOVE();
|
||||||
this.initMarvinJS();
|
this.initMarvinJS();
|
||||||
$(this.$refs.actionsDropdownButton).on('shown.bs.dropdown hidden.bs.dropdown', this.handleDropdownPosition);
|
$(this.$refs.actionsDropdownButton).on('shown.bs.dropdown hidden.bs.dropdown', this.handleDropdownPosition);
|
||||||
},
|
},
|
||||||
|
@ -165,6 +166,9 @@
|
||||||
deleteAttachment(id) {
|
deleteAttachment(id) {
|
||||||
this.$emit('attachment:deleted', id)
|
this.$emit('attachment:deleted', id)
|
||||||
},
|
},
|
||||||
|
initOVE() {
|
||||||
|
$(window.iFrameModal).on('sequenceSaved', () => this.$emit('attachment:uploaded'));
|
||||||
|
},
|
||||||
initMarvinJS() {
|
initMarvinJS() {
|
||||||
// legacy logic from app/assets/javascripts/sitewide/marvinjs_editor.js
|
// legacy logic from app/assets/javascripts/sitewide/marvinjs_editor.js
|
||||||
MarvinJsEditor.initNewButton(
|
MarvinJsEditor.initNewButton(
|
||||||
|
|
|
@ -20,6 +20,11 @@
|
||||||
{{ attachment.attributes.wopi_context.button_text }}
|
{{ attachment.attributes.wopi_context.button_text }}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li v-if="attachment.attributes.asset_type == 'gene_sequence'">
|
||||||
|
<a class="ove-edit-button" @click="openOVEditor(attachment.attributes.edit_url)">
|
||||||
|
{{ i18n.t('open_vector_editor.edit_sequence') }}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
<li v-if="attachment.attributes.asset_type == 'marvinjs' && attachment.attributes.urls.marvin_js_start_edit">
|
<li v-if="attachment.attributes.asset_type == 'marvinjs' && attachment.attributes.urls.marvin_js_start_edit">
|
||||||
<a class="marvinjs-edit-button"
|
<a class="marvinjs-edit-button"
|
||||||
:data-sketch-id="attachment.id"
|
:data-sketch-id="attachment.id"
|
||||||
|
@ -129,6 +134,9 @@
|
||||||
deleteAttachment() {
|
deleteAttachment() {
|
||||||
this.deleteModal = false
|
this.deleteModal = false
|
||||||
this.$emit('attachment:delete')
|
this.$emit('attachment:delete')
|
||||||
|
},
|
||||||
|
openOVEditor(url) {
|
||||||
|
window.showIFrameModal(url);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,6 +61,11 @@
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="integration-block open-vector-editor-button">
|
||||||
|
<a @click="openOVEditor">
|
||||||
|
{{ i18n.t('open_vector_editor.new_sequence') }}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type='button' class='btn btn-secondary' @click="cancel">
|
<button type='button' class='btn btn-secondary' @click="cancel">
|
||||||
|
@ -148,6 +153,10 @@
|
||||||
HelperModule.flashAlertMsg(this.i18n.t('errors.general'), 'danger');
|
HelperModule.flashAlertMsg(this.i18n.t('errors.general'), 'danger');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
openOVEditor() {
|
||||||
|
$(this.$refs.modal).modal('hide');
|
||||||
|
window.showIFrameModal(this.step.attributes.new_sequence_url);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
:data-preview-url="attachment.attributes.urls.preview"
|
:data-preview-url="attachment.attributes.urls.preview"
|
||||||
>
|
>
|
||||||
<div class="attachment-preview" :class= "attachment.attributes.asset_type">
|
<div class="attachment-preview" :class= "attachment.attributes.asset_type">
|
||||||
<img v-if="attachment.attributes.medium_preview !== null"
|
<img v-if="attachment.attributes.medium_preview"
|
||||||
:src="attachment.attributes.medium_preview"
|
:src="attachment.attributes.medium_preview"
|
||||||
@error="ActiveStoragePreviews.reCheckPreview"
|
@error="ActiveStoragePreviews.reCheckPreview"
|
||||||
@load="ActiveStoragePreviews.showPreview"
|
@load="ActiveStoragePreviews.showPreview"
|
||||||
|
|
9
app/javascript/vue/shared/blobToBase64.js
Normal file
9
app/javascript/vue/shared/blobToBase64.js
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
export function blobToBase64(blob) {
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.readAsDataURL(blob);
|
||||||
|
return new Promise(resolve => {
|
||||||
|
reader.onloadend = () => {
|
||||||
|
resolve(reader.result);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
|
@ -17,6 +17,7 @@ class Asset < ApplicationRecord
|
||||||
# ActiveStorage configuration
|
# ActiveStorage configuration
|
||||||
has_one_attached :file
|
has_one_attached :file
|
||||||
has_one_attached :file_pdf_preview
|
has_one_attached :file_pdf_preview
|
||||||
|
has_one_attached :preview_image
|
||||||
|
|
||||||
# Asset validation
|
# Asset validation
|
||||||
# This could cause some problems if you create empty asset and want to
|
# This could cause some problems if you create empty asset and want to
|
||||||
|
@ -165,17 +166,21 @@ class Asset < ApplicationRecord
|
||||||
end
|
end
|
||||||
|
|
||||||
def previewable?
|
def previewable?
|
||||||
return false unless file.attached?
|
return false unless preview_image.attached? || file.attached?
|
||||||
|
|
||||||
previewable_document?(blob) || previewable_image?
|
previewable_document?(blob) || previewable_image?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def preview_attachment
|
||||||
|
preview_image.attached? ? preview_image : file
|
||||||
|
end
|
||||||
|
|
||||||
def medium_preview
|
def medium_preview
|
||||||
file.representation(resize_to_limit: Constants::MEDIUM_PIC_FORMAT)
|
preview_attachment.representation(resize_to_limit: Constants::MEDIUM_PIC_FORMAT)
|
||||||
end
|
end
|
||||||
|
|
||||||
def large_preview
|
def large_preview
|
||||||
file.representation(resize_to_limit: Constants::LARGE_PIC_FORMAT)
|
preview_attachment.representation(resize_to_limit: Constants::LARGE_PIC_FORMAT)
|
||||||
end
|
end
|
||||||
|
|
||||||
def file_name
|
def file_name
|
||||||
|
@ -449,6 +454,7 @@ class Asset < ApplicationRecord
|
||||||
end
|
end
|
||||||
|
|
||||||
def previewable_image?
|
def previewable_image?
|
||||||
|
preview_image.attached? ||
|
||||||
file.blob&.content_type =~ %r{^image/#{Regexp.union(Constants::WHITELISTED_IMAGE_TYPES)}}
|
file.blob&.content_type =~ %r{^image/#{Regexp.union(Constants::WHITELISTED_IMAGE_TYPES)}}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,9 @@ class AssetSerializer < ActiveModel::Serializer
|
||||||
attributes :file_name, :view_mode, :icon, :urls, :updated_at_formatted,
|
attributes :file_name, :view_mode, :icon, :urls, :updated_at_formatted,
|
||||||
:file_size, :medium_preview, :large_preview, :asset_type, :wopi,
|
:file_size, :medium_preview, :large_preview, :asset_type, :wopi,
|
||||||
:wopi_context, :pdf_previewable, :file_size_formatted, :asset_order,
|
:wopi_context, :pdf_previewable, :file_size_formatted, :asset_order,
|
||||||
:updated_at, :metadata, :image_editable, :image_context, :pdf, :attached
|
:updated_at, :metadata, :image_editable, :image_context, :pdf, :attached,
|
||||||
|
:edit_url, :preview_image
|
||||||
|
|
||||||
|
|
||||||
def icon
|
def icon
|
||||||
file_fa_icon_class(object)
|
file_fa_icon_class(object)
|
||||||
|
@ -37,6 +39,10 @@ class AssetSerializer < ActiveModel::Serializer
|
||||||
number_to_human_size(object.file_size)
|
number_to_human_size(object.file_size)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def preview_image
|
||||||
|
rails_representation_url(object.preview_image) if object.preview_image.attached?
|
||||||
|
end
|
||||||
|
|
||||||
def medium_preview
|
def medium_preview
|
||||||
rails_representation_url(object.medium_preview) if object.previewable?
|
rails_representation_url(object.medium_preview) if object.previewable?
|
||||||
end
|
end
|
||||||
|
@ -49,6 +55,10 @@ class AssetSerializer < ActiveModel::Serializer
|
||||||
object.file.metadata&.dig(:asset_type)
|
object.file.metadata&.dig(:asset_type)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def edit_url
|
||||||
|
edit_gene_sequence_asset_path(object.id) if object.file.metadata&.dig(:asset_type) == 'gene_sequence'
|
||||||
|
end
|
||||||
|
|
||||||
def metadata
|
def metadata
|
||||||
object.file.metadata
|
object.file.metadata
|
||||||
end
|
end
|
||||||
|
|
|
@ -9,7 +9,8 @@ class StepSerializer < ActiveModel::Serializer
|
||||||
|
|
||||||
attributes :name, :position, :completed, :attachments_manageble, :urls, :assets_view_mode,
|
attributes :name, :position, :completed, :attachments_manageble, :urls, :assets_view_mode,
|
||||||
:marvinjs_enabled, :marvinjs_context, :created_by, :created_at, :assets_order,
|
:marvinjs_enabled, :marvinjs_context, :created_by, :created_at, :assets_order,
|
||||||
:wopi_enabled, :wopi_context, :comments_count, :unseen_comments, :storage_limit
|
:wopi_enabled, :wopi_context, :comments_count, :unseen_comments, :storage_limit,
|
||||||
|
:new_sequence_url
|
||||||
|
|
||||||
def marvinjs_enabled
|
def marvinjs_enabled
|
||||||
MarvinJsService.enabled?
|
MarvinJsService.enabled?
|
||||||
|
@ -24,6 +25,10 @@ class StepSerializer < ActiveModel::Serializer
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def new_sequence_url
|
||||||
|
new_gene_sequence_asset_url(parent_type: 'Step', parent_id: object.id)
|
||||||
|
end
|
||||||
|
|
||||||
def comments_count
|
def comments_count
|
||||||
object.comments.count
|
object.comments.count
|
||||||
end
|
end
|
||||||
|
|
26
app/views/gene_sequence_assets/edit.html.erb
Normal file
26
app/views/gene_sequence_assets/edit.html.erb
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<%= javascript_include_tag 'i18n_bundle' %>
|
||||||
|
<%= stylesheet_link_tag 'sn_icon_font' %>
|
||||||
|
<%= stylesheet_link_tag 'tailwind' %>
|
||||||
|
<%= stylesheet_link_tag 'open_vector_editor' %>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="open-vector-editor" data-behaviour="vue">
|
||||||
|
<open-vector-editor
|
||||||
|
file-url="<%= @file_url %>"
|
||||||
|
file-name="<%= @file_name %>"
|
||||||
|
parent-id="<%= @parent.id %>"
|
||||||
|
parent-type="<%= @parent.class.name %>"
|
||||||
|
update-url="<%= @asset ? gene_sequence_asset_url(@asset) : gene_sequence_assets_url(parent_type: params[:parent_type], parent_id: params[:parent_id]) %>"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<%= javascript_include_tag 'open_vector_editor' %>
|
||||||
|
<%= javascript_include_tag 'vue_components_open_vector_editor' %>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -106,6 +106,7 @@
|
||||||
<%= render "shared/file_preview/modal" %>
|
<%= render "shared/file_preview/modal" %>
|
||||||
<%= render "shared/file_edit_modal" %>
|
<%= render "shared/file_edit_modal" %>
|
||||||
<%= render "shared/marvinjs_modal" %>
|
<%= render "shared/marvinjs_modal" %>
|
||||||
|
<%= render "shared/iframe_modal" %>
|
||||||
<%= render "users/sessions/session_expire_modal" %>
|
<%= render "users/sessions/session_expire_modal" %>
|
||||||
<%= render "users/sessions/session_end_modal" %>
|
<%= render "users/sessions/session_end_modal" %>
|
||||||
<%= render "label_printers/label_printer_modal" %>
|
<%= render "label_printers/label_printer_modal" %>
|
||||||
|
|
3
app/views/shared/_iframe_modal.html.erb
Normal file
3
app/views/shared/_iframe_modal.html.erb
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<div id="iFrameModal" class="fixed top-0 left-0 right-0 z-[2000] w-full h-full bg-sn-white hidden">
|
||||||
|
<iframe id="iFrameModalFrame" class="w-full h-full border-0"></iframe>
|
||||||
|
</div>
|
|
@ -138,3 +138,9 @@ Rails.application.config.assets.precompile += %w(big.min.js)
|
||||||
|
|
||||||
# JQuery related includes
|
# JQuery related includes
|
||||||
Rails.application.config.assets.precompile += %w(jquery_bundle.js)
|
Rails.application.config.assets.precompile += %w(jquery_bundle.js)
|
||||||
|
|
||||||
|
# Separate icon font file
|
||||||
|
Rails.application.config.assets.precompile += %w(sn_icon_font.css)
|
||||||
|
|
||||||
|
# Separate translations file
|
||||||
|
Rails.application.config.assets.precompile += %w(i18n_bundle.js)
|
||||||
|
|
|
@ -3636,6 +3636,11 @@ en:
|
||||||
checmical_drawing: "Chemical drawings"
|
checmical_drawing: "Chemical drawings"
|
||||||
no_sketches_found: "No Sketches Found"
|
no_sketches_found: "No Sketches Found"
|
||||||
|
|
||||||
|
open_vector_editor:
|
||||||
|
new_sequence: "New sequence"
|
||||||
|
edit_sequence: "Edit sequence"
|
||||||
|
default_sequence_name: "New sequence"
|
||||||
|
|
||||||
pdf_preview:
|
pdf_preview:
|
||||||
fit_to_screen: 'Fit to screen'
|
fit_to_screen: 'Fit to screen'
|
||||||
pages:
|
pages:
|
||||||
|
|
|
@ -975,4 +975,6 @@ Rails.application.routes.draw do
|
||||||
get 'wopi/files/:id', to: 'wopi#file_get_endpoint', as: 'wopi_rest_endpoint'
|
get 'wopi/files/:id', to: 'wopi#file_get_endpoint', as: 'wopi_rest_endpoint'
|
||||||
post 'wopi/files/:id', to: 'wopi#post_file_endpoint'
|
post 'wopi/files/:id', to: 'wopi#post_file_endpoint'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
resources :gene_sequence_assets, only: %i(new create edit update)
|
||||||
end
|
end
|
||||||
|
|
|
@ -14,6 +14,7 @@ const entryList = {
|
||||||
emoji_button: './app/javascript/packs/emoji_button.js',
|
emoji_button: './app/javascript/packs/emoji_button.js',
|
||||||
fontawesome: './app/javascript/packs/fontawesome.scss',
|
fontawesome: './app/javascript/packs/fontawesome.scss',
|
||||||
prism: './app/javascript/packs/prism.js',
|
prism: './app/javascript/packs/prism.js',
|
||||||
|
open_vector_editor: './app/javascript/packs/open_vector_editor.js',
|
||||||
tiny_mce: './app/javascript/packs/tiny_mce.js',
|
tiny_mce: './app/javascript/packs/tiny_mce.js',
|
||||||
tiny_mce_styles: './app/javascript/packs/tiny_mce_styles.scss',
|
tiny_mce_styles: './app/javascript/packs/tiny_mce_styles.scss',
|
||||||
tui_image_editor: './app/javascript/packs/tui_image_editor.js',
|
tui_image_editor: './app/javascript/packs/tui_image_editor.js',
|
||||||
|
@ -33,7 +34,8 @@ const entryList = {
|
||||||
vue_repository_assign_items_to_task_modal: './app/javascript/packs/vue/assign_items_to_task_modal.js',
|
vue_repository_assign_items_to_task_modal: './app/javascript/packs/vue/assign_items_to_task_modal.js',
|
||||||
vue_navigation_top_menu: './app/javascript/packs/vue/navigation/top_menu.js',
|
vue_navigation_top_menu: './app/javascript/packs/vue/navigation/top_menu.js',
|
||||||
vue_navigation_navigator: './app/javascript/packs/vue/navigation/navigator.js',
|
vue_navigation_navigator: './app/javascript/packs/vue/navigation/navigator.js',
|
||||||
vue_components_action_toolbar: './app/javascript/packs/vue/action_toolbar.js'
|
vue_components_action_toolbar: './app/javascript/packs/vue/action_toolbar.js',
|
||||||
|
vue_components_open_vector_editor: './app/javascript/packs/vue/open_vector_editor.js'
|
||||||
}
|
}
|
||||||
|
|
||||||
// Engine pack loading based on https://github.com/rails/webpacker/issues/348#issuecomment-635480949
|
// Engine pack loading based on https://github.com/rails/webpacker/issues/348#issuecomment-635480949
|
||||||
|
|
|
@ -48,6 +48,7 @@
|
||||||
"@joeattardi/emoji-button": "^4.6.2",
|
"@joeattardi/emoji-button": "^4.6.2",
|
||||||
"ajv": "6.12.6",
|
"ajv": "6.12.6",
|
||||||
"autoprefixer": "10.4.14",
|
"autoprefixer": "10.4.14",
|
||||||
|
"axios": "^1.4.0",
|
||||||
"babel-loader": "^8.2.5",
|
"babel-loader": "^8.2.5",
|
||||||
"babel-plugin-macros": "^3.1.0",
|
"babel-plugin-macros": "^3.1.0",
|
||||||
"bootstrap-sass": "^3.3.7",
|
"bootstrap-sass": "^3.3.7",
|
||||||
|
|
28
yarn.lock
28
yarn.lock
|
@ -1900,6 +1900,15 @@ available-typed-arrays@^1.0.5:
|
||||||
resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7"
|
resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7"
|
||||||
integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==
|
integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==
|
||||||
|
|
||||||
|
axios@^1.4.0:
|
||||||
|
version "1.4.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/axios/-/axios-1.4.0.tgz#38a7bf1224cd308de271146038b551d725f0be1f"
|
||||||
|
integrity sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==
|
||||||
|
dependencies:
|
||||||
|
follow-redirects "^1.15.0"
|
||||||
|
form-data "^4.0.0"
|
||||||
|
proxy-from-env "^1.1.0"
|
||||||
|
|
||||||
axobject-query@^0.1.0:
|
axobject-query@^0.1.0:
|
||||||
version "0.1.0"
|
version "0.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-0.1.0.tgz#62f59dbc59c9f9242759ca349960e7a2fe3c36c0"
|
resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-0.1.0.tgz#62f59dbc59c9f9242759ca349960e7a2fe3c36c0"
|
||||||
|
@ -3601,6 +3610,11 @@ focus-trap@^5.1.0:
|
||||||
tabbable "^4.0.0"
|
tabbable "^4.0.0"
|
||||||
xtend "^4.0.1"
|
xtend "^4.0.1"
|
||||||
|
|
||||||
|
follow-redirects@^1.15.0:
|
||||||
|
version "1.15.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13"
|
||||||
|
integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==
|
||||||
|
|
||||||
for-each@^0.3.3:
|
for-each@^0.3.3:
|
||||||
version "0.3.3"
|
version "0.3.3"
|
||||||
resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e"
|
resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e"
|
||||||
|
@ -3617,6 +3631,15 @@ form-data@^3.0.0:
|
||||||
combined-stream "^1.0.8"
|
combined-stream "^1.0.8"
|
||||||
mime-types "^2.1.12"
|
mime-types "^2.1.12"
|
||||||
|
|
||||||
|
form-data@^4.0.0:
|
||||||
|
version "4.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452"
|
||||||
|
integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==
|
||||||
|
dependencies:
|
||||||
|
asynckit "^0.4.0"
|
||||||
|
combined-stream "^1.0.8"
|
||||||
|
mime-types "^2.1.12"
|
||||||
|
|
||||||
fraction.js@^4.2.0:
|
fraction.js@^4.2.0:
|
||||||
version "4.2.0"
|
version "4.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.2.0.tgz#448e5109a313a3527f5a3ab2119ec4cf0e0e2950"
|
resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.2.0.tgz#448e5109a313a3527f5a3ab2119ec4cf0e0e2950"
|
||||||
|
@ -5753,6 +5776,11 @@ prop-types@^15.8.1:
|
||||||
object-assign "^4.1.1"
|
object-assign "^4.1.1"
|
||||||
react-is "^16.13.1"
|
react-is "^16.13.1"
|
||||||
|
|
||||||
|
proxy-from-env@^1.1.0:
|
||||||
|
version "1.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"
|
||||||
|
integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==
|
||||||
|
|
||||||
pseudomap@^1.0.2:
|
pseudomap@^1.0.2:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3"
|
resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3"
|
||||||
|
|
Loading…
Reference in a new issue