feat: image upload / display
This commit is contained in:
		@@ -1,9 +1,11 @@
 | 
				
			|||||||
<template lang='pug'>
 | 
					<template lang='pug'>
 | 
				
			||||||
  .editor-markdown
 | 
					  .editor-markdown
 | 
				
			||||||
    v-toolbar.editor-markdown-toolbar(dense, color='primary', dark, flat, style='overflow-x: hidden;')
 | 
					    v-toolbar.editor-markdown-toolbar(dense, color='primary', dark, flat, style='overflow-x: hidden;')
 | 
				
			||||||
      v-btn.animated.fadeInLeft(v-if='isModalShown', flat, @click='closeAllModal')
 | 
					      template(v-if='isModalShown')
 | 
				
			||||||
        v-icon(left) close
 | 
					        v-spacer
 | 
				
			||||||
        span Back to Editor
 | 
					        v-btn.animated.fadeInRight(flat, @click='closeAllModal')
 | 
				
			||||||
 | 
					          v-icon(left) remove_circle_outline
 | 
				
			||||||
 | 
					          span Back to Editor
 | 
				
			||||||
      template(v-else)
 | 
					      template(v-else)
 | 
				
			||||||
        v-tooltip(bottom, color='primary')
 | 
					        v-tooltip(bottom, color='primary')
 | 
				
			||||||
          v-btn.animated.fadeIn(icon, slot='activator', @click='toggleMarkup({ start: `**` })').mx-0
 | 
					          v-btn.animated.fadeIn(icon, slot='activator', @click='toggleMarkup({ start: `**` })').mx-0
 | 
				
			||||||
@@ -469,8 +471,12 @@ export default {
 | 
				
			|||||||
    this.$root.$on('editorInsert', opts => {
 | 
					    this.$root.$on('editorInsert', opts => {
 | 
				
			||||||
      switch (opts.kind) {
 | 
					      switch (opts.kind) {
 | 
				
			||||||
        case 'IMAGE':
 | 
					        case 'IMAGE':
 | 
				
			||||||
 | 
					          let img = ``
 | 
				
			||||||
 | 
					          if (opts.align && opts.align !== '') {
 | 
				
			||||||
 | 
					            img += `{.align-${opts.align}}`
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
          this.insertAtCursor({
 | 
					          this.insertAtCursor({
 | 
				
			||||||
            content: ``
 | 
					            content: img
 | 
				
			||||||
          })
 | 
					          })
 | 
				
			||||||
          break
 | 
					          break
 | 
				
			||||||
        case 'BINARY':
 | 
					        case 'BINARY':
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,9 +8,8 @@
 | 
				
			|||||||
              .d-flex
 | 
					              .d-flex
 | 
				
			||||||
                v-toolbar.radius-7(color='teal lighten-5', dense, flat, height='44')
 | 
					                v-toolbar.radius-7(color='teal lighten-5', dense, flat, height='44')
 | 
				
			||||||
                  .body-2.teal--text Assets
 | 
					                  .body-2.teal--text Assets
 | 
				
			||||||
                v-btn.ml-3.my-0.radius-7(outline, large, color='teal', disabled, :icon='$vuetify.breakpoint.xsOnly')
 | 
					                v-btn.ml-3.my-0.radius-7(outline, large, color='teal', icon, @click='refresh')
 | 
				
			||||||
                  v-icon(:left='$vuetify.breakpoint.mdAndUp') keyboard_arrow_up
 | 
					                  v-icon cached
 | 
				
			||||||
                  span.hidden-sm-and-down Parent Folder
 | 
					 | 
				
			||||||
                v-dialog(v-model='newFolderDialog', max-width='550')
 | 
					                v-dialog(v-model='newFolderDialog', max-width='550')
 | 
				
			||||||
                  v-btn.my-0.mr-0.radius-7(outline, large, color='teal', :icon='$vuetify.breakpoint.xsOnly', slot='activator')
 | 
					                  v-btn.my-0.mr-0.radius-7(outline, large, color='teal', :icon='$vuetify.breakpoint.xsOnly', slot='activator')
 | 
				
			||||||
                    v-icon(:left='$vuetify.breakpoint.mdAndUp') add
 | 
					                    v-icon(:left='$vuetify.breakpoint.mdAndUp') add
 | 
				
			||||||
@@ -28,13 +27,20 @@
 | 
				
			|||||||
                        @keyup.enter='createFolder'
 | 
					                        @keyup.enter='createFolder'
 | 
				
			||||||
                        @keyup.esc='newFolderDialog = false'
 | 
					                        @keyup.esc='newFolderDialog = false'
 | 
				
			||||||
                        ref='folderNameIpt'
 | 
					                        ref='folderNameIpt'
 | 
				
			||||||
                        hint='Lowercase. No spaces allowed.'
 | 
					 | 
				
			||||||
                        persistent-hint
 | 
					 | 
				
			||||||
                        )
 | 
					                        )
 | 
				
			||||||
 | 
					                      .caption.grey--text.text--darken-1.pl-5 Must follow the asset folder #[a(href='https://docs-beta.requarks.io/guide/assets#naming-restrictions', target='_blank') naming rules].
 | 
				
			||||||
                    v-card-chin
 | 
					                    v-card-chin
 | 
				
			||||||
                      v-spacer
 | 
					                      v-spacer
 | 
				
			||||||
                      v-btn(flat, @click='newFolderDialog = false') Cancel
 | 
					                      v-btn(flat, @click='newFolderDialog = false') Cancel
 | 
				
			||||||
                      v-btn(color='primary', @click='createFolder', :disabled='!isFolderNameValid') Create
 | 
					                      v-btn(color='primary', @click='createFolder', :disabled='!isFolderNameValid', :loading='newFolderLoading') Create
 | 
				
			||||||
 | 
					              template(v-if='folders.length > 0 || currentFolderId > 0')
 | 
				
			||||||
 | 
					                .pt-2
 | 
				
			||||||
 | 
					                  v-btn.is-icon.mx-1(color='grey darken-2', outline, :dark='currentFolderId > 0', @click='upFolder()', :disabled='currentFolderId === 0')
 | 
				
			||||||
 | 
					                    v-icon keyboard_arrow_up
 | 
				
			||||||
 | 
					                  v-btn.btn-normalcase.mx-1(v-for='folder of folders', :key='folder.id', depressed,  color='grey darken-2', dark, @click='downFolder(folder)')
 | 
				
			||||||
 | 
					                    v-icon(left) folder
 | 
				
			||||||
 | 
					                    span {{ folder.name }}
 | 
				
			||||||
 | 
					                v-divider.mt-2
 | 
				
			||||||
              v-data-table(
 | 
					              v-data-table(
 | 
				
			||||||
                :items='assets'
 | 
					                :items='assets'
 | 
				
			||||||
                :headers='headers'
 | 
					                :headers='headers'
 | 
				
			||||||
@@ -63,34 +69,55 @@
 | 
				
			|||||||
                      v-menu(offset-x)
 | 
					                      v-menu(offset-x)
 | 
				
			||||||
                        v-btn.ma-0(icon, slot='activator')
 | 
					                        v-btn.ma-0(icon, slot='activator')
 | 
				
			||||||
                          v-icon(color='grey darken-2') more_horiz
 | 
					                          v-icon(color='grey darken-2') more_horiz
 | 
				
			||||||
                        v-list.py-0
 | 
					                        v-list.py-0(style='border-top: 5px solid #444;')
 | 
				
			||||||
                          v-list-tile(@click='')
 | 
					                          v-list-tile(@click='')
 | 
				
			||||||
                            v-list-tile-avatar
 | 
					                            v-list-tile-avatar
 | 
				
			||||||
                              v-icon(color='teal') short_text
 | 
					                              v-icon(color='teal') short_text
 | 
				
			||||||
                            v-list-tile-content Properties
 | 
					                            v-list-tile-content Properties
 | 
				
			||||||
                          v-divider
 | 
					                          v-divider
 | 
				
			||||||
                          template(v-if='props.item.kind === `IMAGE`')
 | 
					                          template(v-if='props.item.kind === `IMAGE`')
 | 
				
			||||||
 | 
					                            v-list-tile(@click='')
 | 
				
			||||||
 | 
					                              v-list-tile-avatar
 | 
				
			||||||
 | 
					                                v-icon(color='green') image_search
 | 
				
			||||||
 | 
					                              v-list-tile-content Preview
 | 
				
			||||||
 | 
					                            v-divider
 | 
				
			||||||
                            v-list-tile(@click='')
 | 
					                            v-list-tile(@click='')
 | 
				
			||||||
                              v-list-tile-avatar
 | 
					                              v-list-tile-avatar
 | 
				
			||||||
                                v-icon(color='indigo') crop_rotate
 | 
					                                v-icon(color='indigo') crop_rotate
 | 
				
			||||||
                              v-list-tile-content Edit
 | 
					                              v-list-tile-content Edit
 | 
				
			||||||
                            v-divider
 | 
					                            v-divider
 | 
				
			||||||
 | 
					                            v-list-tile(@click='')
 | 
				
			||||||
 | 
					                              v-list-tile-avatar
 | 
				
			||||||
 | 
					                                v-icon(color='purple') offline_bolt
 | 
				
			||||||
 | 
					                              v-list-tile-content Optimize
 | 
				
			||||||
 | 
					                            v-divider
 | 
				
			||||||
                          v-list-tile(@click='')
 | 
					                          v-list-tile(@click='')
 | 
				
			||||||
                            v-list-tile-avatar
 | 
					                            v-list-tile-avatar
 | 
				
			||||||
                              v-icon(color='blue') keyboard
 | 
					                              v-icon(color='orange') keyboard
 | 
				
			||||||
                            v-list-tile-content Rename / Move
 | 
					                            v-list-tile-content Rename
 | 
				
			||||||
 | 
					                          v-divider
 | 
				
			||||||
 | 
					                          v-list-tile(@click='')
 | 
				
			||||||
 | 
					                            v-list-tile-avatar
 | 
				
			||||||
 | 
					                              v-icon(color='blue') forward
 | 
				
			||||||
 | 
					                            v-list-tile-content Move
 | 
				
			||||||
                          v-divider
 | 
					                          v-divider
 | 
				
			||||||
                          v-list-tile(@click='')
 | 
					                          v-list-tile(@click='')
 | 
				
			||||||
                            v-list-tile-avatar
 | 
					                            v-list-tile-avatar
 | 
				
			||||||
                              v-icon(color='red') delete
 | 
					                              v-icon(color='red') delete
 | 
				
			||||||
                            v-list-tile-content Delete
 | 
					                            v-list-tile-content Delete
 | 
				
			||||||
                template(slot='no-data')
 | 
					                template(slot='no-data')
 | 
				
			||||||
                  v-alert.ma-3(icon='warning', :value='true', outline) No assets to display.
 | 
					                  v-alert.mt-3.radius-7(icon='folder_open', :value='true', outline, color='teal') This asset folder is empty.
 | 
				
			||||||
              .text-xs-center.py-2(v-if='this.pageTotal > 1')
 | 
					              .text-xs-center.py-2(v-if='this.pageTotal > 1')
 | 
				
			||||||
                v-pagination(v-model='pagination.page', :length='pageTotal')
 | 
					                v-pagination(v-model='pagination.page', :length='pageTotal')
 | 
				
			||||||
              .d-flex.mt-3
 | 
					              .d-flex.mt-3
 | 
				
			||||||
                v-toolbar.radius-7(flat, color='grey lighten-4', dense, height='44')
 | 
					                v-toolbar.radius-7(flat, color='grey lighten-4', dense, height='44')
 | 
				
			||||||
                  .body-2 / #[em root]
 | 
					                  template(v-if='folderTree.length > 0')
 | 
				
			||||||
 | 
					                    .body-2
 | 
				
			||||||
 | 
					                      span.mr-1 /
 | 
				
			||||||
 | 
					                      template(v-for='folder of folderTree')
 | 
				
			||||||
 | 
					                        span(:key='folder.id') {{folder.name}}
 | 
				
			||||||
 | 
					                        span.mx-1 /
 | 
				
			||||||
 | 
					                  .body-2(v-else) / #[em root]
 | 
				
			||||||
                  template(v-if='$vuetify.breakpoint.smAndUp')
 | 
					                  template(v-if='$vuetify.breakpoint.smAndUp')
 | 
				
			||||||
                    v-spacer
 | 
					                    v-spacer
 | 
				
			||||||
                    .body-1.grey--text.text--darken-1 {{assets.length}} files
 | 
					                    .body-1.grey--text.text--darken-1 {{assets.length}} files
 | 
				
			||||||
@@ -166,6 +193,8 @@ import vueFilePond from 'vue-filepond'
 | 
				
			|||||||
import 'filepond/dist/filepond.min.css'
 | 
					import 'filepond/dist/filepond.min.css'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import listAssetQuery from 'gql/editor/editor-media-query-list.gql'
 | 
					import listAssetQuery from 'gql/editor/editor-media-query-list.gql'
 | 
				
			||||||
 | 
					import listFolderAssetQuery from 'gql/editor/editor-media-query-folder-list.gql'
 | 
				
			||||||
 | 
					import createAssetFolderMutation from 'gql/editor/editor-media-mutation-folder-create.gql'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const FilePond = vueFilePond()
 | 
					const FilePond = vueFilePond()
 | 
				
			||||||
const localeSegmentRegex = /^[A-Z]{2}(-[A-Z]{2})?$/i
 | 
					const localeSegmentRegex = /^[A-Z]{2}(-[A-Z]{2})?$/i
 | 
				
			||||||
@@ -183,21 +212,27 @@ export default {
 | 
				
			|||||||
  },
 | 
					  },
 | 
				
			||||||
  data() {
 | 
					  data() {
 | 
				
			||||||
    return {
 | 
					    return {
 | 
				
			||||||
 | 
					      folders: [],
 | 
				
			||||||
 | 
					      folderTree: [],
 | 
				
			||||||
      files: [],
 | 
					      files: [],
 | 
				
			||||||
      assets: [],
 | 
					      assets: [],
 | 
				
			||||||
      pagination: {},
 | 
					      pagination: {},
 | 
				
			||||||
      remoteImageUrl: '',
 | 
					      remoteImageUrl: '',
 | 
				
			||||||
      imageAlignments: [
 | 
					      imageAlignments: [
 | 
				
			||||||
        { text: 'None', value: '' },
 | 
					        { text: 'None', value: '' },
 | 
				
			||||||
 | 
					        { text: 'Left', value: 'left' },
 | 
				
			||||||
        { text: 'Centered', value: 'center' },
 | 
					        { text: 'Centered', value: 'center' },
 | 
				
			||||||
        { text: 'Right', value: 'right' },
 | 
					        { text: 'Right', value: 'right' },
 | 
				
			||||||
        { text: 'Absolute Top Right', value: 'abstopright' }
 | 
					        { text: 'Absolute Top Right', value: 'abstopright' }
 | 
				
			||||||
      ],
 | 
					      ],
 | 
				
			||||||
      imageAlignment: '',
 | 
					      imageAlignment: '',
 | 
				
			||||||
 | 
					      currentFolderId: 0,
 | 
				
			||||||
      currentFileId: null,
 | 
					      currentFileId: null,
 | 
				
			||||||
 | 
					      previousFolderId: 0,
 | 
				
			||||||
      loading: false,
 | 
					      loading: false,
 | 
				
			||||||
      newFolderDialog: false,
 | 
					      newFolderDialog: false,
 | 
				
			||||||
      newFolderName: ''
 | 
					      newFolderName: '',
 | 
				
			||||||
 | 
					      newFolderLoading: false
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  computed: {
 | 
					  computed: {
 | 
				
			||||||
@@ -219,7 +254,7 @@ export default {
 | 
				
			|||||||
        { text: 'Filename', value: 'filename' },
 | 
					        { text: 'Filename', value: 'filename' },
 | 
				
			||||||
        this.$vuetify.breakpoint.lgAndUp && { text: 'Type', value: 'ext', width: 50 },
 | 
					        this.$vuetify.breakpoint.lgAndUp && { text: 'Type', value: 'ext', width: 50 },
 | 
				
			||||||
        this.$vuetify.breakpoint.mdAndUp && { text: 'File Size', value: 'fileSize', width: 110 },
 | 
					        this.$vuetify.breakpoint.mdAndUp && { text: 'File Size', value: 'fileSize', width: 110 },
 | 
				
			||||||
        this.$vuetify.breakpoint.mdAndUp && { text: 'Added', value: 'createdAt', width: 150 },
 | 
					        this.$vuetify.breakpoint.mdAndUp && { text: 'Added', value: 'createdAt', width: 175 },
 | 
				
			||||||
        this.$vuetify.breakpoint.smAndUp && { text: 'Actions', value: '', width: 40, sortable: false, align:'right' }
 | 
					        this.$vuetify.breakpoint.smAndUp && { text: 'Actions', value: '', width: 40, sortable: false, align:'right' }
 | 
				
			||||||
      ])
 | 
					      ])
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
@@ -242,19 +277,17 @@ export default {
 | 
				
			|||||||
        throw new TypeError('Expected a number')
 | 
					        throw new TypeError('Expected a number')
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      var exponent
 | 
					      let exponent
 | 
				
			||||||
      var unit
 | 
					      let unit
 | 
				
			||||||
      var neg = num < 0
 | 
					      let neg = num < 0
 | 
				
			||||||
      var units = ['B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
 | 
					      let units = ['B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (neg) {
 | 
					      if (neg) {
 | 
				
			||||||
        num = -num
 | 
					        num = -num
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					 | 
				
			||||||
      if (num < 1) {
 | 
					      if (num < 1) {
 | 
				
			||||||
        return (neg ? '-' : '') + num + ' B'
 | 
					        return (neg ? '-' : '') + num + ' B'
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					 | 
				
			||||||
      exponent = Math.min(Math.floor(Math.log(num) / Math.log(1000)), units.length - 1)
 | 
					      exponent = Math.min(Math.floor(Math.log(num) / Math.log(1000)), units.length - 1)
 | 
				
			||||||
      num = (num / Math.pow(1000, exponent)).toFixed(2) * 1
 | 
					      num = (num / Math.pow(1000, exponent)).toFixed(2) * 1
 | 
				
			||||||
      unit = units[exponent]
 | 
					      unit = units[exponent]
 | 
				
			||||||
@@ -263,12 +296,22 @@ export default {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  methods: {
 | 
					  methods: {
 | 
				
			||||||
 | 
					    async refresh() {
 | 
				
			||||||
 | 
					      await this.$apollo.queries.assets.refetch()
 | 
				
			||||||
 | 
					      this.$store.commit('showNotification', {
 | 
				
			||||||
 | 
					          message: 'List of assets refreshed successfully.',
 | 
				
			||||||
 | 
					          style: 'success',
 | 
				
			||||||
 | 
					          icon: 'check'
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    insert () {
 | 
					    insert () {
 | 
				
			||||||
      const asset = _.find(this.assets, ['id', this.currentFileId])
 | 
					      const asset = _.find(this.assets, ['id', this.currentFileId])
 | 
				
			||||||
 | 
					      const assetPath = this.folderTree.map(f => f.slug).join('/')
 | 
				
			||||||
      this.$root.$emit('editorInsert', {
 | 
					      this.$root.$emit('editorInsert', {
 | 
				
			||||||
        kind: asset.kind,
 | 
					        kind: asset.kind,
 | 
				
			||||||
        path: `/${asset.filename}`,
 | 
					        path: this.currentFolderId > 0 ? `/${assetPath}/${asset.filename}` : `/${asset.filename}`,
 | 
				
			||||||
        text: asset.filename
 | 
					        text: asset.filename,
 | 
				
			||||||
 | 
					        align: this.imageAlignment
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
      this.activeModal = ''
 | 
					      this.activeModal = ''
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
@@ -286,7 +329,7 @@ export default {
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
      for (let file of files) {
 | 
					      for (let file of files) {
 | 
				
			||||||
        file.setMetadata({
 | 
					        file.setMetadata({
 | 
				
			||||||
          path: 'test'
 | 
					          folderId: this.currentFolderId
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      await this.$refs.pond.processFiles()
 | 
					      await this.$refs.pond.processFiles()
 | 
				
			||||||
@@ -305,15 +348,68 @@ export default {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
      await this.$apollo.queries.assets.refetch()
 | 
					      await this.$apollo.queries.assets.refetch()
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    downFolder(folder) {
 | 
				
			||||||
 | 
					      this.folderTree.push(folder)
 | 
				
			||||||
 | 
					      this.currentFolderId = folder.id
 | 
				
			||||||
 | 
					      this.currentFileId = null
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    upFolder() {
 | 
				
			||||||
 | 
					      this.folderTree.pop()
 | 
				
			||||||
 | 
					      const parentFolder = _.last(this.folderTree)
 | 
				
			||||||
 | 
					      this.currentFolderId = parentFolder ? parentFolder.id : 0
 | 
				
			||||||
 | 
					      this.currentFileId = null
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    async createFolder() {
 | 
					    async createFolder() {
 | 
				
			||||||
 | 
					      this.$store.commit(`loadingStart`, 'editor-media-createfolder')
 | 
				
			||||||
 | 
					      this.newFolderLoading = true
 | 
				
			||||||
 | 
					      try {
 | 
				
			||||||
 | 
					        const resp = await this.$apollo.mutate({
 | 
				
			||||||
 | 
					          mutation: createAssetFolderMutation,
 | 
				
			||||||
 | 
					          variables: {
 | 
				
			||||||
 | 
					            parentFolderId: this.currentFolderId,
 | 
				
			||||||
 | 
					            slug: this.newFolderName
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        if (_.get(resp, 'data.assets.createFolder.responseResult.succeeded', false)) {
 | 
				
			||||||
 | 
					          await this.$apollo.queries.folders.refetch()
 | 
				
			||||||
 | 
					          this.$store.commit('showNotification', {
 | 
				
			||||||
 | 
					            message: 'Asset folder created successfully.',
 | 
				
			||||||
 | 
					            style: 'success',
 | 
				
			||||||
 | 
					            icon: 'check'
 | 
				
			||||||
 | 
					          })
 | 
				
			||||||
 | 
					          this.newFolderDialog = false
 | 
				
			||||||
 | 
					          this.newFolderName = ''
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					          this.$store.commit('pushGraphError', new Error(_.get(resp, 'data.assets.createFolder.responseResult.message')))
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      } catch (err) {
 | 
				
			||||||
 | 
					        this.$store.commit('pushGraphError', err)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      this.newFolderLoading = false
 | 
				
			||||||
 | 
					      this.$store.commit(`loadingStop`, 'editor-media-createfolder')
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  apollo: {
 | 
					  apollo: {
 | 
				
			||||||
 | 
					    folders: {
 | 
				
			||||||
 | 
					      query: listFolderAssetQuery,
 | 
				
			||||||
 | 
					      variables() {
 | 
				
			||||||
 | 
					        return {
 | 
				
			||||||
 | 
					          parentFolderId: this.currentFolderId
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      fetchPolicy: 'network-only',
 | 
				
			||||||
 | 
					      update: (data) => data.assets.folders,
 | 
				
			||||||
 | 
					      watchLoading (isLoading) {
 | 
				
			||||||
 | 
					        this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'editor-media-folders-list-refresh')
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    assets: {
 | 
					    assets: {
 | 
				
			||||||
      query: listAssetQuery,
 | 
					      query: listAssetQuery,
 | 
				
			||||||
      variables: {
 | 
					      variables() {
 | 
				
			||||||
        kind: 'ALL'
 | 
					        return {
 | 
				
			||||||
 | 
					          folderId: this.currentFolderId,
 | 
				
			||||||
 | 
					          kind: 'ALL'
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      throttle: 1000,
 | 
					      throttle: 1000,
 | 
				
			||||||
      fetchPolicy: 'network-only',
 | 
					      fetchPolicy: 'network-only',
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -97,7 +97,17 @@
 | 
				
			|||||||
                        li Unordered List Item 1
 | 
					                        li Unordered List Item 1
 | 
				
			||||||
                        li Unordered List Item 2
 | 
					                        li Unordered List Item 2
 | 
				
			||||||
                        li Unordered List Item 3
 | 
					                        li Unordered List Item 3
 | 
				
			||||||
 | 
					              .body-2.mt-3 Images
 | 
				
			||||||
 | 
					              v-layout(row)
 | 
				
			||||||
 | 
					                v-flex(xs6)
 | 
				
			||||||
 | 
					                  v-card.editor-markdown-help-source(flat)
 | 
				
			||||||
 | 
					                    v-card-text
 | 
				
			||||||
 | 
					                      div 
 | 
				
			||||||
 | 
					                v-icon chevron_right
 | 
				
			||||||
 | 
					                v-flex(xs6)
 | 
				
			||||||
 | 
					                  v-card.editor-markdown-help-result(flat)
 | 
				
			||||||
 | 
					                    v-card-text
 | 
				
			||||||
 | 
					                      img(src='https://via.placeholder.com/150x50.png')
 | 
				
			||||||
        v-flex(xs12, lg6, xl4)
 | 
					        v-flex(xs12, lg6, xl4)
 | 
				
			||||||
          v-card.radius-7.animated.fadeInUp.wait-p1s(light)
 | 
					          v-card.radius-7.animated.fadeInUp.wait-p1s(light)
 | 
				
			||||||
            v-card-text
 | 
					            v-card-text
 | 
				
			||||||
@@ -105,6 +115,17 @@
 | 
				
			|||||||
                v-toolbar.radius-7(color='teal lighten-5', dense, flat, height='44')
 | 
					                v-toolbar.radius-7(color='teal lighten-5', dense, flat, height='44')
 | 
				
			||||||
                  v-icon.mr-3(color='teal') info
 | 
					                  v-icon.mr-3(color='teal') info
 | 
				
			||||||
                  .body-2.teal--text Markdown Reference (continued)
 | 
					                  .body-2.teal--text Markdown Reference (continued)
 | 
				
			||||||
 | 
					              .body-2.mt-3 Links
 | 
				
			||||||
 | 
					              v-layout(row)
 | 
				
			||||||
 | 
					                v-flex(xs6)
 | 
				
			||||||
 | 
					                  v-card.editor-markdown-help-source(flat)
 | 
				
			||||||
 | 
					                    v-card-text
 | 
				
			||||||
 | 
					                      div [Link Text](https://wiki.js.org)
 | 
				
			||||||
 | 
					                v-icon chevron_right
 | 
				
			||||||
 | 
					                v-flex(xs6)
 | 
				
			||||||
 | 
					                  v-card.editor-markdown-help-result(flat)
 | 
				
			||||||
 | 
					                    v-card-text
 | 
				
			||||||
 | 
					                      .caption: a(href='https://wiki.js.org', target='_blank') Link Text
 | 
				
			||||||
              .body-2.mt-3 Superscript
 | 
					              .body-2.mt-3 Superscript
 | 
				
			||||||
              v-layout(row)
 | 
					              v-layout(row)
 | 
				
			||||||
                v-flex(xs6)
 | 
					                v-flex(xs6)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										12
									
								
								client/graph/editor/editor-media-mutation-folder-create.gql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								client/graph/editor/editor-media-mutation-folder-create.gql
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
				
			|||||||
 | 
					mutation ($parentFolderId: Int!, $slug: String!) {
 | 
				
			||||||
 | 
					  assets {
 | 
				
			||||||
 | 
					    createFolder(parentFolderId:$parentFolderId, slug: $slug) {
 | 
				
			||||||
 | 
					      responseResult {
 | 
				
			||||||
 | 
					        succeeded
 | 
				
			||||||
 | 
					        errorCode
 | 
				
			||||||
 | 
					        slug
 | 
				
			||||||
 | 
					        message
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										9
									
								
								client/graph/editor/editor-media-query-folder-list.gql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								client/graph/editor/editor-media-query-folder-list.gql
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					query ($parentFolderId: Int!) {
 | 
				
			||||||
 | 
					  assets {
 | 
				
			||||||
 | 
					    folders(parentFolderId:$parentFolderId) {
 | 
				
			||||||
 | 
					      id
 | 
				
			||||||
 | 
					      name
 | 
				
			||||||
 | 
					      slug
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
query ($root: String, $kind: AssetKind!) {
 | 
					query ($folderId: Int!, $kind: AssetKind!) {
 | 
				
			||||||
  assets {
 | 
					  assets {
 | 
				
			||||||
    list(root:$root, kind: $kind) {
 | 
					    list(folderId:$folderId, kind: $kind) {
 | 
				
			||||||
      id
 | 
					      id
 | 
				
			||||||
      filename
 | 
					      filename
 | 
				
			||||||
      ext
 | 
					      ext
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -52,3 +52,7 @@
 | 
				
			|||||||
    transform: scale(.7) rotateX(-180deg);
 | 
					    transform: scale(.7) rotateX(-180deg);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.btn-normalcase {
 | 
				
			||||||
 | 
					  text-transform: none;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,6 +3,7 @@
 | 
				
			|||||||
.contents {
 | 
					.contents {
 | 
				
			||||||
  color: mc('grey', '800');
 | 
					  color: mc('grey', '800');
 | 
				
			||||||
  padding-bottom: 50px;
 | 
					  padding-bottom: 50px;
 | 
				
			||||||
 | 
					  position: relative;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @at-root .theme--dark & {
 | 
					  @at-root .theme--dark & {
 | 
				
			||||||
    color: mc('grey', '300');
 | 
					    color: mc('grey', '300');
 | 
				
			||||||
@@ -309,7 +310,8 @@
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        &::before {
 | 
					        &::before {
 | 
				
			||||||
          color: mc('grey', '400');
 | 
					          content: '';
 | 
				
			||||||
 | 
					          display: none;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        @at-root .theme--dark & {
 | 
					        @at-root .theme--dark & {
 | 
				
			||||||
@@ -457,4 +459,29 @@
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // ---------------------------------
 | 
				
			||||||
 | 
					  // IMAGES
 | 
				
			||||||
 | 
					  // ---------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  img {
 | 
				
			||||||
 | 
					    &.align-left {
 | 
				
			||||||
 | 
					      float: left;
 | 
				
			||||||
 | 
					      margin: 0 1rem 1rem 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    &.align-right {
 | 
				
			||||||
 | 
					      float: right;
 | 
				
			||||||
 | 
					      margin: 0 0 1rem 1rem;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    &.align-center {
 | 
				
			||||||
 | 
					      display: block;
 | 
				
			||||||
 | 
					      max-width: 100%;
 | 
				
			||||||
 | 
					      margin: auto;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    &.align-abstopright {
 | 
				
			||||||
 | 
					      position: absolute;
 | 
				
			||||||
 | 
					      top: -90px;
 | 
				
			||||||
 | 
					      right: 1rem;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -41,11 +41,17 @@ router.post('/u', multer({
 | 
				
			|||||||
    })
 | 
					    })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  let folderPath = ''
 | 
					  // Get folder Id
 | 
				
			||||||
 | 
					  let folderId = null
 | 
				
			||||||
  try {
 | 
					  try {
 | 
				
			||||||
    const folderRaw = _.get(req, 'body.mediaUpload', false)
 | 
					    const folderRaw = _.get(req, 'body.mediaUpload', false)
 | 
				
			||||||
    if (folderRaw) {
 | 
					    if (folderRaw) {
 | 
				
			||||||
      folderPath = _.get(JSON.parse(folderRaw), 'path', false)
 | 
					      folderId = _.get(JSON.parse(folderRaw), 'folderId', null)
 | 
				
			||||||
 | 
					      if (folderId === 0) {
 | 
				
			||||||
 | 
					        folderId = null
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      throw new Error('Missing File Metadata')
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  } catch (err) {
 | 
					  } catch (err) {
 | 
				
			||||||
    return res.status(400).json({
 | 
					    return res.status(400).json({
 | 
				
			||||||
@@ -54,17 +60,34 @@ router.post('/u', multer({
 | 
				
			|||||||
    })
 | 
					    })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (!WIKI.auth.checkAccess(req.user, ['write:assets'], { path: `${folderPath}/${fileMeta.originalname}`})) {
 | 
					  // Build folder hierarchy
 | 
				
			||||||
 | 
					  let hierarchy = []
 | 
				
			||||||
 | 
					  if (folderId) {
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      hierarchy = await WIKI.models.assetFolders.getHierarchy(folderId)
 | 
				
			||||||
 | 
					    } catch (err) {
 | 
				
			||||||
 | 
					      return res.status(400).json({
 | 
				
			||||||
 | 
					        succeeded: false,
 | 
				
			||||||
 | 
					        message: 'Failed to fetch folder hierarchy.'
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Check if user can upload at path
 | 
				
			||||||
 | 
					  const assetPath = (folderId) ? opts.hierarchy.map(h => h.slug).join('/') + `/${fileMeta.originalname}` : fileMeta.originalname
 | 
				
			||||||
 | 
					  if (!WIKI.auth.checkAccess(req.user, ['write:assets'], { path: assetPath })) {
 | 
				
			||||||
    return res.status(403).json({
 | 
					    return res.status(403).json({
 | 
				
			||||||
      succeeded: false,
 | 
					      succeeded: false,
 | 
				
			||||||
      message: 'You are not authorized to upload files to this folder.'
 | 
					      message: 'You are not authorized to upload files to this folder.'
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Process upload file
 | 
				
			||||||
  await WIKI.models.assets.upload({
 | 
					  await WIKI.models.assets.upload({
 | 
				
			||||||
    ...fileMeta,
 | 
					    ...fileMeta,
 | 
				
			||||||
    originalname: sanitize(fileMeta.originalname).toLowerCase(),
 | 
					    originalname: sanitize(fileMeta.originalname).toLowerCase(),
 | 
				
			||||||
    folder: folderPath,
 | 
					    folderId: folderId,
 | 
				
			||||||
 | 
					    hierarchy,
 | 
				
			||||||
    userId: req.user.id
 | 
					    userId: req.user.id
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
  res.send('ok')
 | 
					  res.send('ok')
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,8 +1,8 @@
 | 
				
			|||||||
 | 
					const sanitize = require('sanitize-filename')
 | 
				
			||||||
 | 
					const graphHelper = require('../../helpers/graph')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* global WIKI */
 | 
					/* global WIKI */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const gql = require('graphql')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
module.exports = {
 | 
					module.exports = {
 | 
				
			||||||
  Query: {
 | 
					  Query: {
 | 
				
			||||||
    async assets() { return {} }
 | 
					    async assets() { return {} }
 | 
				
			||||||
@@ -13,7 +13,7 @@ module.exports = {
 | 
				
			|||||||
  AssetQuery: {
 | 
					  AssetQuery: {
 | 
				
			||||||
    async list(obj, args, context) {
 | 
					    async list(obj, args, context) {
 | 
				
			||||||
      let cond = {
 | 
					      let cond = {
 | 
				
			||||||
        folderId: null
 | 
					        folderId: args.folderId === 0 ? null : args.folderId
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      if (args.kind !== 'ALL') {
 | 
					      if (args.kind !== 'ALL') {
 | 
				
			||||||
        cond.kind = args.kind.toLowerCase()
 | 
					        cond.kind = args.kind.toLowerCase()
 | 
				
			||||||
@@ -23,9 +23,40 @@ module.exports = {
 | 
				
			|||||||
        ...a,
 | 
					        ...a,
 | 
				
			||||||
        kind: a.kind.toUpperCase()
 | 
					        kind: a.kind.toUpperCase()
 | 
				
			||||||
      }))
 | 
					      }))
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    async folders(obj, args, context) {
 | 
				
			||||||
 | 
					      const result = await WIKI.models.assetFolders.query().where({
 | 
				
			||||||
 | 
					        parentId: args.parentFolderId === 0 ? null : args.parentFolderId
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					      // TODO: Filter by page rules
 | 
				
			||||||
 | 
					      return result
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  AssetMutation: {
 | 
					  AssetMutation: {
 | 
				
			||||||
 | 
					    async createFolder(obj, args, context) {
 | 
				
			||||||
 | 
					      try {
 | 
				
			||||||
 | 
					        const folderSlug = sanitize(args.slug).toLowerCase()
 | 
				
			||||||
 | 
					        const parentFolderId = args.parentFolderId === 0 ? null : args.parentFolderId
 | 
				
			||||||
 | 
					        const result =  await WIKI.models.assetFolders.query().where({
 | 
				
			||||||
 | 
					          parentId: parentFolderId,
 | 
				
			||||||
 | 
					          slug: folderSlug
 | 
				
			||||||
 | 
					        }).first()
 | 
				
			||||||
 | 
					        if (!result) {
 | 
				
			||||||
 | 
					          await WIKI.models.assetFolders.query().insert({
 | 
				
			||||||
 | 
					            slug: folderSlug,
 | 
				
			||||||
 | 
					            name: folderSlug,
 | 
				
			||||||
 | 
					            parentId: parentFolderId
 | 
				
			||||||
 | 
					          })
 | 
				
			||||||
 | 
					          return {
 | 
				
			||||||
 | 
					            responseResult: graphHelper.generateSuccess('Asset Folder has been created successfully.')
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					          throw new WIKI.Error.AssetFolderExists()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      } catch (err) {
 | 
				
			||||||
 | 
					        return graphHelper.generateError(err)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    // deleteFile(obj, args) {
 | 
					    // deleteFile(obj, args) {
 | 
				
			||||||
    //   return WIKI.models.File.destroy({
 | 
					    //   return WIKI.models.File.destroy({
 | 
				
			||||||
    //     where: {
 | 
					    //     where: {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,9 +16,13 @@ extend type Mutation {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
type AssetQuery {
 | 
					type AssetQuery {
 | 
				
			||||||
  list(
 | 
					  list(
 | 
				
			||||||
    root: String
 | 
					    folderId: Int!
 | 
				
			||||||
    kind: AssetKind
 | 
					    kind: AssetKind!
 | 
				
			||||||
  ): [AssetItem] @auth(requires: ["manage:system", "read:assets"])
 | 
					  ): [AssetItem] @auth(requires: ["manage:system", "read:assets"])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  folders(
 | 
				
			||||||
 | 
					    parentFolderId: Int!
 | 
				
			||||||
 | 
					  ): [AssetFolder] @auth(requires: ["manage:system", "read:assets"])
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# -----------------------------------------------
 | 
					# -----------------------------------------------
 | 
				
			||||||
@@ -26,9 +30,11 @@ type AssetQuery {
 | 
				
			|||||||
# -----------------------------------------------
 | 
					# -----------------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type AssetMutation {
 | 
					type AssetMutation {
 | 
				
			||||||
  upload(
 | 
					  createFolder(
 | 
				
			||||||
    data: Upload!
 | 
					    parentFolderId: Int!
 | 
				
			||||||
  ): DefaultResponse
 | 
					    slug: String!
 | 
				
			||||||
 | 
					    name: String
 | 
				
			||||||
 | 
					  ): DefaultResponse @auth(requires: ["manage:system", "write:assets"])
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# -----------------------------------------------
 | 
					# -----------------------------------------------
 | 
				
			||||||
@@ -51,6 +57,8 @@ type AssetItem {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
type AssetFolder {
 | 
					type AssetFolder {
 | 
				
			||||||
  id: Int!
 | 
					  id: Int!
 | 
				
			||||||
 | 
					  slug: String!
 | 
				
			||||||
 | 
					  name: String
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
enum AssetKind {
 | 
					enum AssetKind {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,10 @@
 | 
				
			|||||||
const CustomError = require('custom-error-instance')
 | 
					const CustomError = require('custom-error-instance')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module.exports = {
 | 
					module.exports = {
 | 
				
			||||||
 | 
					  AssetFolderExists: CustomError('AssetFolderExists', {
 | 
				
			||||||
 | 
					    message: 'An asset folder with the same name already exists.',
 | 
				
			||||||
 | 
					    code: 2001
 | 
				
			||||||
 | 
					  }),
 | 
				
			||||||
  AuthAccountBanned: CustomError('AuthAccountBanned', {
 | 
					  AuthAccountBanned: CustomError('AuthAccountBanned', {
 | 
				
			||||||
    message: 'Your account has been disabled.',
 | 
					    message: 'Your account has been disabled.',
 | 
				
			||||||
    code: 1016
 | 
					    code: 1016
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -32,4 +32,12 @@ module.exports = class AssetFolder extends Model {
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  static async getHierarchy(folderId) {
 | 
				
			||||||
 | 
					    return WIKI.models.knex.withRecursive('ancestors', qb => {
 | 
				
			||||||
 | 
					      qb.select('id', 'name', 'slug', 'parentId').from('assetFolders').where('id', folderId).union(sqb => {
 | 
				
			||||||
 | 
					        sqb.select('a.id', 'a.name', 'a.slug', 'a.parentId').from('assetFolders AS a').join('ancestors', 'ancestors.parentId', 'a.id')
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					    }).select('*').from('ancestors')
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -67,7 +67,8 @@ module.exports = class Asset extends Model {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  static async upload(opts) {
 | 
					  static async upload(opts) {
 | 
				
			||||||
    const fileInfo = path.parse(opts.originalname)
 | 
					    const fileInfo = path.parse(opts.originalname)
 | 
				
			||||||
    const fileHash = assetHelper.generateHash(`${opts.folder}/${opts.originalname}`)
 | 
					    const folderPath = opts.hierarchy.map(h => h.slug).join('/')
 | 
				
			||||||
 | 
					    const fileHash = opts.folderId ? assetHelper.generateHash(`${folderPath}/${opts.originalname}`) : assetHelper.generateHash(opts.originalname)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Create asset entry
 | 
					    // Create asset entry
 | 
				
			||||||
    const asset = await WIKI.models.assets.query().insert({
 | 
					    const asset = await WIKI.models.assets.query().insert({
 | 
				
			||||||
@@ -77,7 +78,7 @@ module.exports = class Asset extends Model {
 | 
				
			|||||||
      kind: _.startsWith(opts.mimetype, 'image/') ? 'image' : 'binary',
 | 
					      kind: _.startsWith(opts.mimetype, 'image/') ? 'image' : 'binary',
 | 
				
			||||||
      mime: opts.mimetype,
 | 
					      mime: opts.mimetype,
 | 
				
			||||||
      fileSize: opts.size,
 | 
					      fileSize: opts.size,
 | 
				
			||||||
      folderId: null,
 | 
					      folderId: opts.folderId,
 | 
				
			||||||
      authorId: opts.userId
 | 
					      authorId: opts.userId
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user