|
|
<template lang='pug'> v-card v-toolbar(flat, color='primary', dark, dense) .subtitle-1 {{ $t('admin:utilities.exportTitle') }} v-card-text .text-center img.animated.fadeInUp.wait-p1s(src='/_assets/svg/icon-big-parcel.svg') .body-2 Export to tarball / file system v-divider.my-4 .body-2 What do you want to export? v-checkbox( v-for='choice of entityChoices' :key='choice.key' :label='choice.label' :value='choice.key' color='deep-orange darken-2' hide-details v-model='entities' ) template(v-slot:label) div strong.deep-orange--text.text--darken-2 {{choice.label}} .text-caption {{choice.hint}} v-text-field.mt-7( outlined label='Target Folder Path' hint='Either an absolute path or relative to the Wiki.js installation folder, where exported content will be saved to. Note that the folder MUST be empty!' persistent-hint v-model='filePath' )
v-alert.mt-3(color='deep-orange', outlined, icon='mdi-alert', prominent) .body-2 Depending on your selection, the archive could contain sensitive data such as site configuration keys and hashed user passwords. Ensure the exported archive is treated accordingly. .body-2 For example, you may want to encrypt the archive if stored for backup purposes.
v-card-chin v-btn.px-3(depressed, color='deep-orange darken-2', :disabled='entities.length < 1', @click='startExport').ml-0 v-icon(left, color='white') mdi-database-export span.white--text Start Export v-dialog( v-model='isLoading' persistent max-width='350' ) v-card(color='deep-orange darken-2', dark) v-card-text.pa-10.text-center self-building-square-spinner.animated.fadeIn( :animation-duration='4500' :size='40' color='#FFF' style='margin: 0 auto;' ) .mt-5.body-1.white--text Exporting... .caption Please wait, this may take a while v-progress-linear.mt-5( color='white' :value='progress' stream rounded :buffer-value='0' ) v-dialog( v-model='isSuccess' persistent max-width='350' ) v-card(color='green darken-2', dark) v-card-text.pa-10.text-center v-icon(size='60') mdi-check-circle-outline .my-5.body-1.white--text Export completed v-card-actions.green.darken-1 v-spacer v-btn.px-5( color='white' outlined @click='isSuccess = false' ) Close v-spacer v-dialog( v-model='isFailed' persistent max-width='800' ) v-card(color='red darken-2', dark) v-toolbar(color='red darken-2', dense) v-icon mdi-alert .body-2.pl-3 Export failed v-spacer v-btn.px-5( color='white' text @click='isFailed = false' ) Close v-card-text.pa-5.red.darken-4.white--text span {{errorMessage}} </template>
<script> import { SelfBuildingSquareSpinner } from 'epic-spinners'
import gql from 'graphql-tag' import _get from 'lodash/get'
export default { components: { SelfBuildingSquareSpinner }, data() { return { entities: [], filePath: './data/export', isLoading: false, isSuccess: false, isFailed: false, errorMessage: '', progress: 0 } }, computed: { entityChoices () { return [ { key: 'assets', label: 'Assets', hint: 'Media files such as images, documents, etc.' }, { key: 'comments', label: 'Comments', hint: 'Comments made using the default comment module only.' }, { key: 'navigation', label: 'Navigation', hint: 'Sidebar links when using Static or Custom Navigation.' }, { key: 'pages', label: 'Pages', hint: 'Page content, tags and related metadata.' }, { key: 'history', label: 'Pages History', hint: 'All previous versions of pages and their related metadata.' }, { key: 'settings', label: 'Settings', hint: 'Site configuration and modules settings.' }, { key: 'groups', label: 'User Groups', hint: 'Group permissions and page rules.' }, { key: 'users', label: 'Users', hint: 'Users metadata and their group memberships.' } ] } }, methods: { async checkProgress () { try { const respStatus = await this.$apollo.query({ query: gql`
{ system { exportStatus { status progress message startedAt } } } `,
fetchPolicy: 'network-only' }) const respStatusObj = _get(respStatus, 'data.system.exportStatus', {}) if (!respStatusObj) { throw new Error('An unexpected error occured.') } else { switch (respStatusObj.status) { case 'error': { throw new Error(respStatusObj.message || 'An unexpected error occured.') } case 'running': { this.progress = respStatusObj.progress || 0 window.requestAnimationFrame(() => { setTimeout(() => { this.checkProgress() }, 5000) }) break } case 'success': { this.isLoading = false this.isSuccess = true break } default: { throw new Error('Invalid export status.') } } } } catch (err) { this.errorMessage = err.message this.isLoading = false this.isFailed = true } }, async startExport () { this.isFailed = false this.isSuccess = false this.isLoading = true this.progress = 0
setTimeout(async () => { try { // -> Initiate export
const respExport = await this.$apollo.mutate({ mutation: gql`
mutation ( $entities: [String]! $path: String! ) { system { export ( entities: $entities path: $path ) { responseResult { succeeded message } } } } `,
variables: { entities: this.entities, path: this.filePath } })
const respExportObj = _get(respExport, 'data.system.export', {}) if (!_get(respExportObj, 'responseResult.succeeded', false)) { this.errorMessage = _get(respExportObj, 'responseResult.message', 'An unexpected error occurred') this.isLoading = false this.isFailed = true return }
// -> Check for progress
this.checkProgress() } catch (err) { this.$store.commit('pushGraphError', err) this.isLoading = false } }, 1500) } } } </script>
<style lang='scss'>
</style>
|