diff --git a/client/components/editor/editor-markdown.vue b/client/components/editor/editor-markdown.vue index 4ca6e192..10abdcbc 100644 --- a/client/components/editor/editor-markdown.vue +++ b/client/components/editor/editor-markdown.vue @@ -170,8 +170,12 @@ import _ from 'lodash' import { get, sync } from 'vuex-pathify' import markdownHelp from './markdown/help.vue' import gql from 'graphql-tag' +import Cookies from 'js-cookie' import DOMPurify from 'dompurify' +import listFolderAssetQuery from 'gql/editor/editor-media-query-folder-list.gql' +import createAssetFolderMutation from 'gql/editor/editor-media-mutation-folder-create.gql' + /* global siteConfig, siteLangs */ // ======================================== @@ -432,22 +436,91 @@ export default { onCmInput: _.debounce(function (newContent) { this.processContent(newContent) }, 600), - onCmPaste (cm, ev) { - // const clipItems = (ev.clipboardData || ev.originalEvent.clipboardData).items - // for (let clipItem of clipItems) { - // if (_.startsWith(clipItem.type, 'image/')) { - // const file = clipItem.getAsFile() - // const reader = new FileReader() - // reader.onload = evt => { - // this.$store.commit(`loadingStart`, 'editor-paste-image') - // this.insertAfter({ - // content: `![${file.name}](${evt.target.result})`, - // newLine: true - // }) - // } - // reader.readAsDataURL(file) - // } - // } + async onCmPaste (cm, ev) { + const clipItems = (ev.clipboardData || ev.originalEvent.clipboardData).items + // In Chromium kernel the paste event will be released after callback return + // Since this is an async function, the file must be get before 'await' keyword + const files = Array.from(clipItems).filter(item=>_.startsWith(item.type, 'image/')).map(item=>item.getAsFile()) + // upload all file + for (const file of files) { + // get parent directory + let folderID = 0 + let folderPath = "/assets/" + this.locale + try { + if(this.path.includes("/")){ + // path to lowercase, the wikijs foldre does not support case sensitivity. + folderPath += "/" + this.path.toLowerCase().split('/').slice(0,-1).join('/') + } + // try to get the folder id, create it if does not exists. + folderID = await this.createFolder(folderPath) + } catch (err) { + this.$store.commit('pushGraphError', err) + return + } + const now = new Date() + // file name format yyyy-MM-dd_random.png + let filename = `${now.getFullYear()}-${now.getMonth()+1}-${now.getDate()}_${parseInt(Math.random() * (10**5))}` + filename += file.name.slice(file.name.lastIndexOf('.')) + // upload file + const form = new FormData() + form.append('mediaUpload', JSON.stringify({ 'folderId': folderID })) + form.append('mediaUpload', file, filename) + const jwtToken = Cookies.get('jwt') + this.$store.commit('showNotification', { + message: this.$t('editor:assets.uploading'), + style: 'primary', + icon: 'primary' + }) + try { + const resp = await fetch('/u', { method: 'POST', body: form, headers: { 'Authorization': `Bearer ${jwtToken}` }}) + if( resp.status != 200 ){ + throw resp.statusText + } + this.insertAtCursor({content: `![${filename}](${folderPath}/${filename})`}) + } catch (err) { + return this.$store.commit('showNotification', { + message: this.$t('editor:assets.uploadFailed'), + style: 'error', + icon: 'error' + }) + } + } + }, + async createFolder(path) { + let folderID = 0 + for(const folderName of path.split("/").slice(1)){ + let folders = await this.getSubFolder(folderID) + // api case-insensitive for folder name + let folder = folders.find(folder => folder.slug === folderName) + if (!folder) { + const resp = await this.$apollo.mutate({ + mutation: createAssetFolderMutation, + variables: { + parentFolderId: folderID, + slug: folderName + } + }) + if (! _.get(resp, 'data.assets.createFolder.responseResult.succeeded', false)) { + const message = _.get(resp, 'data.assets.createFolder.responseResult.message') + this.$store.commit('pushGraphError', new Error(message)) + throw new Error(message); + } + folders = await this.getSubFolder(folderID) + folder = folders.find(folder => folder.slug === folderName) + } + folderID = folder.id + } + return folderID + }, + async getSubFolder(folderID){ + const resp = await this.$apollo.query({ + query: listFolderAssetQuery, + variables: { + parentFolderId: folderID, + }, + fetchPolicy: 'network-only' + }) + return _.get(resp, "data.assets.folders", []) }, processContent (newContent) { linesMap = []