refactor: editor-file -> vue component (UI, core, new folder, rename, insert)
This commit is contained in:
+12
-2
@@ -12,6 +12,8 @@ import io from 'socket-io-client'
|
|||||||
import i18next from 'i18next'
|
import i18next from 'i18next'
|
||||||
import i18nextXHR from 'i18next-xhr-backend'
|
import i18nextXHR from 'i18next-xhr-backend'
|
||||||
import VueI18Next from '@panter/vue-i18next'
|
import VueI18Next from '@panter/vue-i18next'
|
||||||
|
import 'jquery-contextmenu'
|
||||||
|
import 'jquery-simple-upload'
|
||||||
import 'jquery-smooth-scroll'
|
import 'jquery-smooth-scroll'
|
||||||
import 'jquery-sticky'
|
import 'jquery-sticky'
|
||||||
|
|
||||||
@@ -19,8 +21,8 @@ import 'jquery-sticky'
|
|||||||
// Load minimal lodash
|
// Load minimal lodash
|
||||||
// ====================================
|
// ====================================
|
||||||
|
|
||||||
import concat from 'lodash/concat'
|
|
||||||
import cloneDeep from 'lodash/cloneDeep'
|
import cloneDeep from 'lodash/cloneDeep'
|
||||||
|
import concat from 'lodash/concat'
|
||||||
import debounce from 'lodash/debounce'
|
import debounce from 'lodash/debounce'
|
||||||
import deburr from 'lodash/deburr'
|
import deburr from 'lodash/deburr'
|
||||||
import delay from 'lodash/delay'
|
import delay from 'lodash/delay'
|
||||||
@@ -29,6 +31,7 @@ import find from 'lodash/find'
|
|||||||
import findKey from 'lodash/findKey'
|
import findKey from 'lodash/findKey'
|
||||||
import forEach from 'lodash/forEach'
|
import forEach from 'lodash/forEach'
|
||||||
import includes from 'lodash/includes'
|
import includes from 'lodash/includes'
|
||||||
|
import isBoolean from 'lodash/isBoolean'
|
||||||
import isEmpty from 'lodash/isEmpty'
|
import isEmpty from 'lodash/isEmpty'
|
||||||
import isNil from 'lodash/isNil'
|
import isNil from 'lodash/isNil'
|
||||||
import join from 'lodash/join'
|
import join from 'lodash/join'
|
||||||
@@ -39,8 +42,10 @@ import pullAt from 'lodash/pullAt'
|
|||||||
import reject from 'lodash/reject'
|
import reject from 'lodash/reject'
|
||||||
import slice from 'lodash/slice'
|
import slice from 'lodash/slice'
|
||||||
import split from 'lodash/split'
|
import split from 'lodash/split'
|
||||||
import trim from 'lodash/trim'
|
import startCase from 'lodash/startCase'
|
||||||
|
import toString from 'lodash/toString'
|
||||||
import toUpper from 'lodash/toUpper'
|
import toUpper from 'lodash/toUpper'
|
||||||
|
import trim from 'lodash/trim'
|
||||||
|
|
||||||
// ====================================
|
// ====================================
|
||||||
// Load Helpers
|
// Load Helpers
|
||||||
@@ -56,6 +61,7 @@ import alertComponent from './components/alert.vue'
|
|||||||
import anchorComponent from './components/anchor.vue'
|
import anchorComponent from './components/anchor.vue'
|
||||||
import colorPickerComponent from './components/color-picker.vue'
|
import colorPickerComponent from './components/color-picker.vue'
|
||||||
import editorCodeblockComponent from './components/editor-codeblock.vue'
|
import editorCodeblockComponent from './components/editor-codeblock.vue'
|
||||||
|
import editorFileComponent from './components/editor-file.vue'
|
||||||
import editorVideoComponent from './components/editor-video.vue'
|
import editorVideoComponent from './components/editor-video.vue'
|
||||||
import loadingSpinnerComponent from './components/loading-spinner.vue'
|
import loadingSpinnerComponent from './components/loading-spinner.vue'
|
||||||
import modalCreatePageComponent from './components/modal-create-page.vue'
|
import modalCreatePageComponent from './components/modal-create-page.vue'
|
||||||
@@ -88,6 +94,7 @@ const _ = {
|
|||||||
findKey,
|
findKey,
|
||||||
forEach,
|
forEach,
|
||||||
includes,
|
includes,
|
||||||
|
isBoolean,
|
||||||
isEmpty,
|
isEmpty,
|
||||||
isNil,
|
isNil,
|
||||||
join,
|
join,
|
||||||
@@ -98,6 +105,8 @@ const _ = {
|
|||||||
reject,
|
reject,
|
||||||
slice,
|
slice,
|
||||||
split,
|
split,
|
||||||
|
startCase,
|
||||||
|
toString,
|
||||||
toUpper,
|
toUpper,
|
||||||
trim
|
trim
|
||||||
}
|
}
|
||||||
@@ -159,6 +168,7 @@ $(() => {
|
|||||||
contentView: contentViewComponent,
|
contentView: contentViewComponent,
|
||||||
editor: editorComponent,
|
editor: editorComponent,
|
||||||
editorCodeblock: editorCodeblockComponent,
|
editorCodeblock: editorCodeblockComponent,
|
||||||
|
editorFile: editorFileComponent,
|
||||||
editorVideo: editorVideoComponent,
|
editorVideo: editorVideoComponent,
|
||||||
loadingSpinner: loadingSpinnerComponent,
|
loadingSpinner: loadingSpinnerComponent,
|
||||||
modalCreatePage: modalCreatePageComponent,
|
modalCreatePage: modalCreatePageComponent,
|
||||||
|
|||||||
@@ -0,0 +1,413 @@
|
|||||||
|
<template lang="pug">
|
||||||
|
transition(:duration="400")
|
||||||
|
.modal(v-show='isShown', v-cloak)
|
||||||
|
transition(name='modal-background')
|
||||||
|
.modal-background(v-show='isShown')
|
||||||
|
.modal-container
|
||||||
|
transition(name='modal-content')
|
||||||
|
.modal-content.is-expanded(v-show='isShown')
|
||||||
|
header.is-green
|
||||||
|
span {{ $t('editor.filetitle') }}
|
||||||
|
p.modal-notify(v-bind:class='{ "is-active": isLoading }')
|
||||||
|
span {{ isLoadingText }}
|
||||||
|
i
|
||||||
|
.modal-toolbar.is-green
|
||||||
|
a.button(v-on:click='newFolder')
|
||||||
|
i.icon-folder2
|
||||||
|
span {{ $t('editor.newfolder') }}
|
||||||
|
a.button#btn-editor-file-upload
|
||||||
|
i.icon-cloud-upload
|
||||||
|
span {{ $t('editor.fileupload') }}
|
||||||
|
label
|
||||||
|
input(type='file', multiple)
|
||||||
|
section.is-gapless
|
||||||
|
.columns.is-stretched
|
||||||
|
.column.is-one-quarter.modal-sidebar.is-green(style={'max-width':'350px'})
|
||||||
|
.model-sidebar-header {{ $t('editor.folders') }}
|
||||||
|
ul.model-sidebar-list
|
||||||
|
li(v-for='fld in folders')
|
||||||
|
a(v-on:click='selectFolder(fld)', v-bind:class='{ "is-active": currentFolder === fld }')
|
||||||
|
i.icon-folder2
|
||||||
|
span / {{ fld }}
|
||||||
|
.column.editor-modal-file-choices
|
||||||
|
figure(v-for='fl in files', v-bind:class='{ "is-active": currentFile === fl._id }', v-on:click='selectFile(fl._id)', v-bind:data-uid='fl._id')
|
||||||
|
i(class='icon-file')
|
||||||
|
span: strong {{ fl.filename }}
|
||||||
|
span {{ fl.mime }}
|
||||||
|
span {{ filesize(fl.filesize) }}
|
||||||
|
em(v-show='files.length < 1')
|
||||||
|
i.icon-marquee-minus
|
||||||
|
| {{ $t('editor.filefolderempty') }}
|
||||||
|
footer
|
||||||
|
a.button.is-grey.is-outlined(v-on:click='cancel') {{ $t('editor.discard') }}
|
||||||
|
a.button.is-green(v-on:click='insertFileLink') {{ $t('editor.fileinsert') }}
|
||||||
|
|
||||||
|
.modal.is-superimposed(v-show='newFolderShow')
|
||||||
|
transition(name='modal-background')
|
||||||
|
.modal-background
|
||||||
|
.modal-container(v-show='newFolderShow')
|
||||||
|
transition(name='modal-content')
|
||||||
|
.modal-content(v-show='newFolderShow')
|
||||||
|
header.is-light-blue {{ $t('modal.newfoldertitle') }}
|
||||||
|
section
|
||||||
|
label.label {{ $t('modal.newfoldername') }}
|
||||||
|
p.control.is-fullwidth
|
||||||
|
input.input(type='text', v-bind:placeholder='$t("modal.newfoldernameplaceholder")', v-model='newFolderName', ref='editorFileNewFolderInput', v-on:keyup.enter='newFolderCreate', v-on:keyup.esc='newFolderDiscard')
|
||||||
|
span.help.is-danger(v-show='newFolderError') {{ $t('modal.newfolderinvalid') }}
|
||||||
|
footer
|
||||||
|
a.button.is-grey.is-outlined(v-on:click='newFolderDiscard') {{ $t('modal.discard') }}
|
||||||
|
a.button.is-light-blue(v-on:click='newFolderCreate') {{ $t('modal.create') }}
|
||||||
|
|
||||||
|
.modal.is-superimposed(v-show='renameFileShow')
|
||||||
|
.modal-background
|
||||||
|
.modal-container
|
||||||
|
.modal-content
|
||||||
|
header.is-indigo {{ $t('modal.renamefiletitle') }}
|
||||||
|
section
|
||||||
|
label.label {{ $t('modal.renamefilename') }}
|
||||||
|
p.control.is-fullwidth
|
||||||
|
input.input#txt-editor-file-rename(type='text', v-bind:placeholder='$t("modal.renamefilenameplaceholder")', v-model='renameFileFilename', ref='editorFileRenameInput', v-on:keyup.enter='renameFileGo', v-on:keyup.esc='renameFileDiscard')
|
||||||
|
span.help.is-danger.is-hidden {{ $t('modal.renamefileinvalid') }}
|
||||||
|
footer
|
||||||
|
a.button.is-grey.is-outlined(v-on:click='renameFileDiscard') {{ $t('modal.discard') }}
|
||||||
|
a.button.is-light-blue(v-on:click='renameFileGo') {{ $t('modal.renamefile') }}
|
||||||
|
|
||||||
|
.modal.is-superimposed(v-show='deleteFileShow')
|
||||||
|
.modal-background
|
||||||
|
.modal-container
|
||||||
|
.modal-content
|
||||||
|
header.is-red {{ $t('modal.deletefiletitle') }}
|
||||||
|
section
|
||||||
|
span {{ $t('modal.deletefilewarn') }} #[strong {{deleteFileFilename}}]?
|
||||||
|
footer
|
||||||
|
a.button.is-grey.is-outlined(v-on:click='deleteFileWarn(false)') {{ $t('modal.discard') }}
|
||||||
|
a.button.is-red(v-on:click='deleteFileGo') {{ $t('modal.delete') }}
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'editor-file',
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
isLoading: false,
|
||||||
|
isLoadingText: '',
|
||||||
|
newFolderName: '',
|
||||||
|
newFolderShow: false,
|
||||||
|
newFolderError: false,
|
||||||
|
folders: [],
|
||||||
|
currentFolder: '',
|
||||||
|
currentFile: '',
|
||||||
|
files: [],
|
||||||
|
uploadSucceeded: false,
|
||||||
|
postUploadChecks: 0,
|
||||||
|
renameFileShow: false,
|
||||||
|
renameFileId: '',
|
||||||
|
renameFileFilename: '',
|
||||||
|
deleteFileShow: false,
|
||||||
|
deleteFileId: '',
|
||||||
|
deleteFileFilename: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
isShown () {
|
||||||
|
return this.$store.state.editorFile.shown
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
init () {
|
||||||
|
this.refreshFolders()
|
||||||
|
},
|
||||||
|
cancel () {
|
||||||
|
this.$store.dispatch('editorFile/close')
|
||||||
|
},
|
||||||
|
filesize (rawSize) {
|
||||||
|
return this.$helpers.common.filesize(rawSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------
|
||||||
|
// INSERT LINK TO FILE
|
||||||
|
// -------------------------------------------
|
||||||
|
|
||||||
|
selectFile(fileId) {
|
||||||
|
this.currentFile = fileId
|
||||||
|
},
|
||||||
|
insertFileLink() {
|
||||||
|
let selFile = this._.find(this.files, ['_id', this.currentFile])
|
||||||
|
selFile.normalizedPath = (selFile.folder === 'f:') ? selFile.filename : selFile.folder.slice(2) + '/' + selFile.filename
|
||||||
|
selFile.titleGuess = this._.startCase(selFile.basename)
|
||||||
|
|
||||||
|
let fileText = '[' + selFile.titleGuess + '](/uploads/' + selFile.normalizedPath + ' "' + selFile.titleGuess + '")'
|
||||||
|
|
||||||
|
this.$store.dispatch('editor/insert', fileText)
|
||||||
|
this.$store.dispatch('alert', {
|
||||||
|
style: 'blue',
|
||||||
|
icon: 'paper',
|
||||||
|
msg: this.$t('editor.filesuccess')
|
||||||
|
})
|
||||||
|
this.cancel()
|
||||||
|
},
|
||||||
|
|
||||||
|
// -------------------------------------------
|
||||||
|
// NEW FOLDER
|
||||||
|
// -------------------------------------------
|
||||||
|
|
||||||
|
newFolder() {
|
||||||
|
let self = this
|
||||||
|
this.newFolderName = ''
|
||||||
|
this.newFolderError = false
|
||||||
|
this.newFolderShow = true
|
||||||
|
this._.delay(() => { self.$refs.editorFileNewFolderInput.focus() }, 400)
|
||||||
|
},
|
||||||
|
newFolderDiscard() {
|
||||||
|
this.newFolderShow = false
|
||||||
|
},
|
||||||
|
newFolderCreate() {
|
||||||
|
let self = this
|
||||||
|
let regFolderName = new RegExp('^[a-z0-9][a-z0-9-]*[a-z0-9]$')
|
||||||
|
this.newFolderName = this._.kebabCase(this._.trim(this.newFolderName))
|
||||||
|
|
||||||
|
if (this._.isEmpty(this.newFolderName) || !regFolderName.test(this.newFolderName)) {
|
||||||
|
this.newFolderError = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.newFolderDiscard()
|
||||||
|
this.isLoadingText = this.$t('modal.newfolderloading')
|
||||||
|
this.isLoading = true
|
||||||
|
|
||||||
|
this.$nextTick(() => {
|
||||||
|
socket.emit('uploadsCreateFolder', { foldername: self.newFolderName }, (data) => {
|
||||||
|
self.folders = data
|
||||||
|
self.currentFolder = self.newFolderName
|
||||||
|
self.files = []
|
||||||
|
self.isLoading = false
|
||||||
|
self.$store.dispatch('alert', {
|
||||||
|
style: 'blue',
|
||||||
|
icon: 'folder2',
|
||||||
|
msg: self.$t('modal.newfoldersuccess', { name: self.newFolderName })
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// -------------------------------------------
|
||||||
|
// RENAME FILE
|
||||||
|
// -------------------------------------------
|
||||||
|
|
||||||
|
renameFile() {
|
||||||
|
let self = this
|
||||||
|
let c = this._.find(this.files, [ '_id', this.renameFileId ])
|
||||||
|
this.renameFileFilename = c.basename || ''
|
||||||
|
this.renameFileShow = true
|
||||||
|
this._.delay(() => {
|
||||||
|
self.$refs.editorFileRenameInput.select()
|
||||||
|
}, 100)
|
||||||
|
},
|
||||||
|
renameFileDiscard() {
|
||||||
|
this.renameFileShow = false
|
||||||
|
},
|
||||||
|
renameFileGo() {
|
||||||
|
let self = this
|
||||||
|
this.renameFileDiscard()
|
||||||
|
this.isLoadingText = this.$t('modal.renamefileloading')
|
||||||
|
this.isLoading = true
|
||||||
|
|
||||||
|
this.$nextTick(() => {
|
||||||
|
socket.emit('uploadsRenameFile', { uid: self.renameFileId, folder: self.currentFolder, filename: self.renameFileFilename }, (data) => {
|
||||||
|
if (data.ok) {
|
||||||
|
self.waitChangeComplete(self.files.length, false)
|
||||||
|
} else {
|
||||||
|
self.isLoading = false
|
||||||
|
self.$store.dispatch('alert', {
|
||||||
|
style: 'red',
|
||||||
|
icon: 'square-cross',
|
||||||
|
msg: self.$t('modal.renamefileerror', { err: data.msg })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// -------------------------------------------
|
||||||
|
// MOVE FILE
|
||||||
|
// -------------------------------------------
|
||||||
|
|
||||||
|
moveFile(uid, fld) {
|
||||||
|
let self = this
|
||||||
|
this.isLoadingText = this.$t('editor.filemoveloading')
|
||||||
|
this.isLoading = true
|
||||||
|
this.$nextTick(() => {
|
||||||
|
socket.emit('uploadsMoveFile', { uid, folder: fld }, (data) => {
|
||||||
|
if (data.ok) {
|
||||||
|
self.loadFiles()
|
||||||
|
} else {
|
||||||
|
self.isLoading = false
|
||||||
|
self.$store.dispatch('alert', {
|
||||||
|
style: 'red',
|
||||||
|
icon: 'square-cross',
|
||||||
|
msg: self.$t('editor.filemoveerror', { err: data.msg })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// -------------------------------------------
|
||||||
|
// DELETE FILE
|
||||||
|
// -------------------------------------------
|
||||||
|
|
||||||
|
deleteFileWarn(show) {
|
||||||
|
if (show) {
|
||||||
|
let c = this._.find(this.files, [ '_id', this.deleteFileId ])
|
||||||
|
this.deleteFileFilename = c.filename || this.$t('editor.filedeletedefault')
|
||||||
|
}
|
||||||
|
this.deleteFileShow = show
|
||||||
|
},
|
||||||
|
deleteFileGo() {
|
||||||
|
let self = this
|
||||||
|
this.deleteFileWarn(false)
|
||||||
|
this.isLoadingText = this.$t('editor.filedeleteloading')
|
||||||
|
this.isLoading = true
|
||||||
|
this.$nextTick(() => {
|
||||||
|
socket.emit('uploadsDeleteFile', { uid: this.deleteFileId }, (data) => {
|
||||||
|
self.loadFiles()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// -------------------------------------------
|
||||||
|
// LOAD FROM REMOTE
|
||||||
|
// -------------------------------------------
|
||||||
|
|
||||||
|
selectFolder(fldName) {
|
||||||
|
this.currentFolder = fldName
|
||||||
|
this.loadFiles()
|
||||||
|
},
|
||||||
|
|
||||||
|
refreshFolders() {
|
||||||
|
let self = this
|
||||||
|
this.isLoadingText = this.$t('editor.foldersloading')
|
||||||
|
this.isLoading = true
|
||||||
|
this.currentFolder = ''
|
||||||
|
this.currentImage = ''
|
||||||
|
this.$nextTick(() => {
|
||||||
|
socket.emit('uploadsGetFolders', { }, (data) => {
|
||||||
|
self.folders = data
|
||||||
|
self.loadFiles()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
loadFiles(silent) {
|
||||||
|
let self = this
|
||||||
|
if (!silent) {
|
||||||
|
this.isLoadingText = this.$t('editor.fileloading')
|
||||||
|
this.isLoading = true
|
||||||
|
}
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
self.$nextTick(() => {
|
||||||
|
socket.emit('uploadsGetFiles', { folder: self.currentFolder }, (data) => {
|
||||||
|
self.files = data
|
||||||
|
if (!silent) {
|
||||||
|
self.isLoading = false
|
||||||
|
}
|
||||||
|
self.attachContextMenus()
|
||||||
|
resolve(true)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
waitChangeComplete(oldAmount, expectChange) {
|
||||||
|
let self = this
|
||||||
|
expectChange = (this._.isBoolean(expectChange)) ? expectChange : true
|
||||||
|
|
||||||
|
this.postUploadChecks++
|
||||||
|
this.isLoadingText = this.$t('editor.fileprocessing')
|
||||||
|
|
||||||
|
this.$nextTick(() => {
|
||||||
|
self.loadFiles(true).then(() => {
|
||||||
|
if ((self.files.length !== oldAmount) === expectChange) {
|
||||||
|
self.postUploadChecks = 0
|
||||||
|
self.isLoading = false
|
||||||
|
} else if (self.postUploadChecks > 5) {
|
||||||
|
self.postUploadChecks = 0
|
||||||
|
self.isLoading = false
|
||||||
|
self.$store.dispatch('alert', {
|
||||||
|
style: 'red',
|
||||||
|
icon: 'square-cross',
|
||||||
|
msg: self.$t('editor.fileerror')
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
self._.delay(() => {
|
||||||
|
self.waitChangeComplete(oldAmount, expectChange)
|
||||||
|
}, 1500)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// -------------------------------------------
|
||||||
|
// IMAGE CONTEXT MENU
|
||||||
|
// -------------------------------------------
|
||||||
|
|
||||||
|
attachContextMenus() {
|
||||||
|
let self = this
|
||||||
|
let moveFolders = this._.map(this.folders, (f) => {
|
||||||
|
return {
|
||||||
|
name: (f !== '') ? f : '/ (root)',
|
||||||
|
icon: 'icon-folder2',
|
||||||
|
callback: (key, opt) => {
|
||||||
|
let moveFileId = self._.toString($(opt.$trigger).data('uid'))
|
||||||
|
let moveFileDestFolder = self._.nth(self.folders, key)
|
||||||
|
self.moveFile(moveFileId, moveFileDestFolder)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
$.contextMenu('destroy', '.editor-modal-file-choices > figure')
|
||||||
|
$.contextMenu({
|
||||||
|
selector: '.editor-modal-file-choices > figure',
|
||||||
|
appendTo: '.editor-modal-file-choices',
|
||||||
|
position: (opt, x, y) => {
|
||||||
|
$(opt.$trigger).addClass('is-contextopen')
|
||||||
|
let trigPos = $(opt.$trigger).position()
|
||||||
|
let trigDim = { w: $(opt.$trigger).width() / 5, h: $(opt.$trigger).height() / 2 }
|
||||||
|
opt.$menu.css({ top: trigPos.top + trigDim.h, left: trigPos.left + trigDim.w })
|
||||||
|
},
|
||||||
|
events: {
|
||||||
|
hide: (opt) => {
|
||||||
|
$(opt.$trigger).removeClass('is-contextopen')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
items: {
|
||||||
|
rename: {
|
||||||
|
name: self.$t('editor.filerenameaction'),
|
||||||
|
icon: 'icon-edit',
|
||||||
|
callback: (key, opt) => {
|
||||||
|
self.renameFileId = self._.toString(opt.$trigger[0].dataset.uid)
|
||||||
|
self.renameFile()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
move: {
|
||||||
|
name: self.$t('editor.filemoveaction'),
|
||||||
|
icon: 'fa-folder-open-o',
|
||||||
|
items: moveFolders
|
||||||
|
},
|
||||||
|
delete: {
|
||||||
|
name: self.$t('editor.filedeleteaction'),
|
||||||
|
icon: 'icon-trash2',
|
||||||
|
callback: (key, opt) => {
|
||||||
|
self.deleteFileId = self._.toString(opt.$trigger[0].dataset.uid)
|
||||||
|
self.deleteFileWarn(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.$root.$on('editorFile/init', this.init)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
import filesize from 'filesize.js'
|
|
||||||
import $ from 'jquery'
|
import $ from 'jquery'
|
||||||
|
|
||||||
let mde
|
let mde
|
||||||
@@ -8,11 +7,6 @@ let mde
|
|||||||
export default {
|
export default {
|
||||||
name: 'editor',
|
name: 'editor',
|
||||||
props: ['currentPath'],
|
props: ['currentPath'],
|
||||||
filters: {
|
|
||||||
filesize(v) {
|
|
||||||
return this._.toUpper(filesize(v))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
data() {
|
data() {
|
||||||
return {}
|
return {}
|
||||||
},
|
},
|
||||||
@@ -124,11 +118,8 @@ export default {
|
|||||||
{
|
{
|
||||||
name: 'link',
|
name: 'link',
|
||||||
action: (editor) => {
|
action: (editor) => {
|
||||||
/* if(!mdeModalOpenState) {
|
|
||||||
mdeModalOpenState = true;
|
|
||||||
$('#modal-editor-link').slideToggle();
|
|
||||||
} */
|
|
||||||
window.alert('Coming soon!')
|
window.alert('Coming soon!')
|
||||||
|
// todo
|
||||||
},
|
},
|
||||||
className: 'icon-link2',
|
className: 'icon-link2',
|
||||||
title: 'Insert Link'
|
title: 'Insert Link'
|
||||||
@@ -136,9 +127,7 @@ export default {
|
|||||||
{
|
{
|
||||||
name: 'image',
|
name: 'image',
|
||||||
action: (editor) => {
|
action: (editor) => {
|
||||||
// if (!mdeModalOpenState) {
|
self.$store.dispatch('editorImage/open')
|
||||||
// vueImage.open()
|
|
||||||
// }
|
|
||||||
},
|
},
|
||||||
className: 'icon-image',
|
className: 'icon-image',
|
||||||
title: 'Insert Image'
|
title: 'Insert Image'
|
||||||
@@ -146,9 +135,7 @@ export default {
|
|||||||
{
|
{
|
||||||
name: 'file',
|
name: 'file',
|
||||||
action: (editor) => {
|
action: (editor) => {
|
||||||
// if (!mdeModalOpenState) {
|
self.$store.dispatch('editorFile/open')
|
||||||
// vueFile.open()
|
|
||||||
// }
|
|
||||||
},
|
},
|
||||||
className: 'icon-paper',
|
className: 'icon-paper',
|
||||||
title: 'Insert File'
|
title: 'Insert File'
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
'use strict'
|
||||||
|
|
||||||
|
import filesize from 'filesize.js'
|
||||||
|
import toUpper from 'lodash/toUpper'
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
/**
|
||||||
|
* Convert bytes to humanized form
|
||||||
|
* @param {number} rawSize Size in bytes
|
||||||
|
* @returns {string} Humanized file size
|
||||||
|
*/
|
||||||
|
filesize(rawSize) {
|
||||||
|
return toUpper(filesize(rawSize))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
const helpers = {
|
const helpers = {
|
||||||
|
common: require('./common'),
|
||||||
form: require('./form'),
|
form: require('./form'),
|
||||||
pages: require('./pages')
|
pages: require('./pages')
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import alert from './modules/alert'
|
|||||||
import anchor from './modules/anchor'
|
import anchor from './modules/anchor'
|
||||||
import editor from './modules/editor'
|
import editor from './modules/editor'
|
||||||
import editorCodeblock from './modules/editor-codeblock'
|
import editorCodeblock from './modules/editor-codeblock'
|
||||||
|
import editorFile from './modules/editor-file'
|
||||||
import editorVideo from './modules/editor-video'
|
import editorVideo from './modules/editor-video'
|
||||||
import modalCreatePage from './modules/modal-create-page'
|
import modalCreatePage from './modules/modal-create-page'
|
||||||
import modalCreateUser from './modules/modal-create-user'
|
import modalCreateUser from './modules/modal-create-user'
|
||||||
@@ -32,6 +33,7 @@ export default new Vuex.Store({
|
|||||||
anchor,
|
anchor,
|
||||||
editor,
|
editor,
|
||||||
editorCodeblock,
|
editorCodeblock,
|
||||||
|
editorFile,
|
||||||
editorVideo,
|
editorVideo,
|
||||||
modalCreatePage,
|
modalCreatePage,
|
||||||
modalCreateUser,
|
modalCreateUser,
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
'use strict'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
namespaced: true,
|
||||||
|
state: {
|
||||||
|
shown: false
|
||||||
|
},
|
||||||
|
getters: {},
|
||||||
|
mutations: {
|
||||||
|
shownChange: (state, shownState) => { state.shown = shownState }
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
open({ commit }, opts) {
|
||||||
|
commit('shownChange', true)
|
||||||
|
wikijs.$emit('editorFile/init')
|
||||||
|
},
|
||||||
|
close({ commit }) { commit('shownChange', false) }
|
||||||
|
}
|
||||||
|
}
|
||||||
+5
-5
@@ -40,7 +40,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"auto-load": "^2.1.0",
|
"auto-load": "^2.1.0",
|
||||||
"axios": "0.16.1",
|
"axios": "^0.16.2",
|
||||||
"bcryptjs-then": "^1.0.1",
|
"bcryptjs-then": "^1.0.1",
|
||||||
"bluebird": "^3.5.0",
|
"bluebird": "^3.5.0",
|
||||||
"body-parser": "^1.17.2",
|
"body-parser": "^1.17.2",
|
||||||
@@ -89,7 +89,7 @@
|
|||||||
"mime-types": "^2.1.15",
|
"mime-types": "^2.1.15",
|
||||||
"moment": "^2.18.1",
|
"moment": "^2.18.1",
|
||||||
"moment-timezone": "^0.5.13",
|
"moment-timezone": "^0.5.13",
|
||||||
"mongodb": "^2.2.27",
|
"mongodb": "^2.2.28",
|
||||||
"mongoose": "^4.10.4",
|
"mongoose": "^4.10.4",
|
||||||
"multer": "^1.3.0",
|
"multer": "^1.3.0",
|
||||||
"node-graceful": "^0.2.3",
|
"node-graceful": "^0.2.3",
|
||||||
@@ -105,7 +105,7 @@
|
|||||||
"passport-windowslive": "^1.0.2",
|
"passport-windowslive": "^1.0.2",
|
||||||
"passport.socketio": "^3.7.0",
|
"passport.socketio": "^3.7.0",
|
||||||
"pm2": "2.4.6",
|
"pm2": "2.4.6",
|
||||||
"pug": "^2.0.0-rc.1",
|
"pug": "^2.0.0-rc.2",
|
||||||
"read-chunk": "^2.0.0",
|
"read-chunk": "^2.0.0",
|
||||||
"remove-markdown": "^0.1.0",
|
"remove-markdown": "^0.1.0",
|
||||||
"request": "^2.81.0",
|
"request": "^2.81.0",
|
||||||
@@ -117,7 +117,7 @@
|
|||||||
"socket.io": "^2.0.2",
|
"socket.io": "^2.0.2",
|
||||||
"stopword": "^0.1.1",
|
"stopword": "^0.1.1",
|
||||||
"stream-to-promise": "^2.2.0",
|
"stream-to-promise": "^2.2.0",
|
||||||
"tar": "^3.1.3",
|
"tar": "^3.1.5",
|
||||||
"through2": "^2.0.3",
|
"through2": "^2.0.3",
|
||||||
"validator": "^7.0.0",
|
"validator": "^7.0.0",
|
||||||
"validator-as-promised": "^1.0.2",
|
"validator-as-promised": "^1.0.2",
|
||||||
@@ -136,7 +136,7 @@
|
|||||||
"eslint": "^3.19.0",
|
"eslint": "^3.19.0",
|
||||||
"eslint-config-standard": "^10.2.1",
|
"eslint-config-standard": "^10.2.1",
|
||||||
"eslint-plugin-import": "^2.3.0",
|
"eslint-plugin-import": "^2.3.0",
|
||||||
"eslint-plugin-node": "^4.2.2",
|
"eslint-plugin-node": "^5.0.0",
|
||||||
"eslint-plugin-promise": "^3.5.0",
|
"eslint-plugin-promise": "^3.5.0",
|
||||||
"eslint-plugin-standard": "^3.0.1",
|
"eslint-plugin-standard": "^3.0.1",
|
||||||
"fuse-box": "2.1.0-beta.10",
|
"fuse-box": "2.1.0-beta.10",
|
||||||
|
|||||||
@@ -1,53 +1,86 @@
|
|||||||
{
|
{
|
||||||
"editor": {
|
"editor": {
|
||||||
"discard": "Discard",
|
|
||||||
"codeblocktitle": "Insert Code Block",
|
|
||||||
"codeblockinsert": "Insert Code Block",
|
"codeblockinsert": "Insert Code Block",
|
||||||
"codeblocklanguage": "Language",
|
"codeblocklanguage": "Language",
|
||||||
"codeblockloading": "Loading code syntax for {{name}}",
|
"codeblockloading": "Loading code syntax for {{name}}",
|
||||||
"codeblockloadingerror": "Error: Unable to load language syntax.",
|
"codeblockloadingerror": "Error: Unable to load language syntax.",
|
||||||
"codeblocksuccess": "Your code block has been inserted.",
|
"codeblocksuccess": "Your code block has been inserted.",
|
||||||
"videotitle": "Insert Video",
|
"codeblocktitle": "Insert Code Block",
|
||||||
"videolinktitle": "Enter the link to the video to be embedded:",
|
"discard": "Discard",
|
||||||
"videoinsert": "Insert Video",
|
"filedeleteaction": "Delete",
|
||||||
"videonotsupported": "This URL is invalid or not supported!",
|
"filedeleteloading": "Deleting file...",
|
||||||
"videosupportedtitle": "The following are supported:",
|
"filedeletedefault": "this file",
|
||||||
|
"fileerror": "Unable to fetch updated listing.",
|
||||||
|
"filefolderempty": "This folder is empty.",
|
||||||
|
"fileinsert": "Insert Link to File",
|
||||||
|
"fileloading": "Fetching files...",
|
||||||
|
"filemoveaction": "Move to...",
|
||||||
|
"filemoveerror": "Move error: {{err}}",
|
||||||
|
"filemoveloading": "Moving file...",
|
||||||
|
"fileprocessing": "Processing...",
|
||||||
|
"filerenameaction": "Rename",
|
||||||
|
"filesuccess": "File link has been inserted.",
|
||||||
|
"filetitle": "Insert File",
|
||||||
|
"fileupload": "Upload File",
|
||||||
|
"folders": "Folders",
|
||||||
|
"foldersloading": "Fetching folders list...",
|
||||||
|
"newfolder": "New Folder",
|
||||||
"videoanymp4file": "Any standard MP4 file",
|
"videoanymp4file": "Any standard MP4 file",
|
||||||
"videosuccess": "The video code has been inserted."
|
"videoinsert": "Insert Video",
|
||||||
|
"videolinktitle": "Enter the link to the video to be embedded:",
|
||||||
|
"videonotsupported": "This URL is invalid or not supported!",
|
||||||
|
"videosuccess": "The video code has been inserted.",
|
||||||
|
"videosupportedtitle": "The following are supported:",
|
||||||
|
"videotitle": "Insert Video"
|
||||||
},
|
},
|
||||||
"modal": {
|
"modal": {
|
||||||
"abort": "Abort",
|
"abort": "Abort",
|
||||||
"anchortitle": "Copy link to this section",
|
|
||||||
"anchorsuccess": "The URL has been copied to your clipboard.",
|
|
||||||
"anchorerror": "Clipboard copy failed. Copy the URL manually.",
|
"anchorerror": "Clipboard copy failed. Copy the URL manually.",
|
||||||
|
"anchorsuccess": "The URL has been copied to your clipboard.",
|
||||||
|
"anchortitle": "Copy link to this section",
|
||||||
"copyclipboard": "Copy to Clipboard",
|
"copyclipboard": "Copy to Clipboard",
|
||||||
"create": "Create",
|
"create": "Create",
|
||||||
"createpagetitle": "Create New Page",
|
|
||||||
"createpagepath": "Enter the new page path:",
|
|
||||||
"createpageinvalid": "This page path is invalid!",
|
"createpageinvalid": "This page path is invalid!",
|
||||||
"createusertitle": "Create / Authorize User",
|
"createpagepath": "Enter the new page path:",
|
||||||
"createuseremail": "Email address:",
|
"createpagetitle": "Create New Page",
|
||||||
"createuseremailplaceholder": "e.g. john.doe@company.com",
|
|
||||||
"createuserprovider": "Provider:",
|
|
||||||
"createuserpassword": "Password:",
|
|
||||||
"createusername": "Full Name:",
|
|
||||||
"createusernameplaceholder": "e.g. John Doe",
|
|
||||||
"createuser": "Create User",
|
"createuser": "Create User",
|
||||||
"createuserauthorize": "Authorize User",
|
"createuserauthorize": "Authorize User",
|
||||||
"discard": "Discard",
|
"createuseremail": "Email address:",
|
||||||
"discardpagestay": "Stay on page",
|
"createuseremailplaceholder": "e.g. john.doe@company.com",
|
||||||
"discardpagetitle": "Discard?",
|
"createusername": "Full Name:",
|
||||||
"discardpagecreate": "Are you sure you want to leave this page and loose anything you wrote so far?",
|
"createusernameplaceholder": "e.g. John Doe",
|
||||||
"discardpageedit": "Are you sure you want to leave this page and loose any modifications?",
|
"createuserpassword": "Password:",
|
||||||
|
"createuserprovider": "Provider:",
|
||||||
|
"createusertitle": "Create / Authorize User",
|
||||||
"delete": "Delete",
|
"delete": "Delete",
|
||||||
|
"deletefiletitle": "Delete?",
|
||||||
|
"deletefilewarn": "Are you sure you want to delete",
|
||||||
"deleteusertitle": "Delete User Account?",
|
"deleteusertitle": "Delete User Account?",
|
||||||
"deleteuserwarning": "Are you sure you want to delete this user account? This action cannot be undone!",
|
"deleteuserwarning": "Are you sure you want to delete this user account? This action cannot be undone!",
|
||||||
|
"discard": "Discard",
|
||||||
|
"discardpagecreate": "Are you sure you want to leave this page and loose anything you wrote so far?",
|
||||||
|
"discardpageedit": "Are you sure you want to leave this page and loose any modifications?",
|
||||||
|
"discardpagestay": "Stay on page",
|
||||||
|
"discardpagetitle": "Discard?",
|
||||||
"move": "Move",
|
"move": "Move",
|
||||||
"movepagetitle": "Move Page",
|
|
||||||
"movepagepath": "Enter the new page path:",
|
|
||||||
"movepageinvalid": "This page path is invalid or not allowed!",
|
"movepageinvalid": "This page path is invalid or not allowed!",
|
||||||
|
"movepagepath": "Enter the new page path:",
|
||||||
|
"movepageplaceholder": "page-name",
|
||||||
|
"movepagetitle": "Move Page",
|
||||||
"movepagewarning": "Note that moving or renaming pages can lead to broken links. Make sure to edit any page that links to this page afterwards!",
|
"movepagewarning": "Note that moving or renaming pages can lead to broken links. Make sure to edit any page that links to this page afterwards!",
|
||||||
"movepageplaceholder": "page-name"
|
"newfolderinvalid": "This folder name is invalid!",
|
||||||
|
"newfolderloading": "Creating new folder...",
|
||||||
|
"newfoldername": "Enter the new folder name:",
|
||||||
|
"newfoldernameplaceholder": "folder name",
|
||||||
|
"newfoldersuccess": "New folder {{name}} created.",
|
||||||
|
"newfoldertitle": "New Folder",
|
||||||
|
"renamefile": "Rename",
|
||||||
|
"renamefileerror": "Rename error: {{err}}",
|
||||||
|
"renamefileinvalid": "This filename is invalid!",
|
||||||
|
"renamefileloading": "Renaming file...",
|
||||||
|
"renamefilename": "Enter the new filename (without the extension) of the file:",
|
||||||
|
"renamefilenameplaceholder": "filename",
|
||||||
|
"renamefiletitle": "Rename File"
|
||||||
},
|
},
|
||||||
"nav": {
|
"nav": {
|
||||||
"home": "Home"
|
"home": "Home"
|
||||||
@@ -61,9 +94,9 @@
|
|||||||
"savechanges": "Save Changes"
|
"savechanges": "Save Changes"
|
||||||
},
|
},
|
||||||
"search": {
|
"search": {
|
||||||
"placeholder": "Search...",
|
"didyoumean": "Did you mean...?",
|
||||||
"results": "Search Results",
|
|
||||||
"nomatch": "No results matching your query",
|
"nomatch": "No results matching your query",
|
||||||
"didyoumean": "Did you mean...?"
|
"placeholder": "Search...",
|
||||||
|
"results": "Search Results"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -18,6 +18,7 @@ block content
|
|||||||
.editor-area
|
.editor-area
|
||||||
textarea(ref='editorTextArea', v-pre)= pageData.markdown
|
textarea(ref='editorTextArea', v-pre)= pageData.markdown
|
||||||
|
|
||||||
|
editor-file
|
||||||
editor-video
|
editor-video
|
||||||
editor-codeblock
|
editor-codeblock
|
||||||
modal-discard-page(mode='edit', current-path=pageData.meta.path)
|
modal-discard-page(mode='edit', current-path=pageData.meta.path)
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ block content
|
|||||||
|
|
||||||
source-view(inline-template, entrypath=pageData.meta.path, v-cloak)
|
source-view(inline-template, entrypath=pageData.meta.path, v-cloak)
|
||||||
.ace-container
|
.ace-container
|
||||||
#source-display= pageData.markdown
|
#source-display(v-pre)= pageData.markdown
|
||||||
|
|
||||||
modal-create-page(basepath=pageData.meta.path)
|
modal-create-page(basepath=pageData.meta.path)
|
||||||
modal-move-page(current-path=pageData.meta.path)
|
modal-move-page(current-path=pageData.meta.path)
|
||||||
|
|||||||
Reference in New Issue
Block a user