%PDF- %PDF-
Direktori : /home/dopla/www/wp-content/plugins/ml-slider/admin/assets/js/app/settings/pages/ |
Current File : /home/dopla/www/wp-content/plugins/ml-slider/admin/assets/js/app/settings/pages/Import.vue |
<template> <div> <split-layout> <template slot="header">{{ __('Import', 'ml-slider') }}</template> <template slot="description"> {{ __('Easily import slideshows generated by MetaSlider. This requires a file generated from the Export tab.', 'ml-slider') }} </template> <template slot="fields"> <file-button name="import" accept=".json" @loaded="loadSlideshowsFromFile" :disabled="processing"> <template slot="header">{{ __('Load slideshows', 'ml-slider') }}</template> <template slot="description">{{ sprintf(__('If you have an export file, you may upload it here to be processed. Information about each slideshow will be presented below. You will be able to confirm before importing.', 'ml-slider'), slideshowsToImport) }}</template> <template v-if="importing" slot="button">{{ __('Importing...', 'ml-slider') }}</template> <template v-else-if="processing" slot="button">{{ __('Processing...', 'ml-slider') }}</template> <template v-else slot="button"> <svg class="w-5 -ml-1 pr-1" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12" /> </svg> {{ __('Upload file', 'ml-slider') }} </template> </file-button> </template> </split-layout> <split-layout v-if="Object.keys(slideshowsList).length" :loading="importing || processing"> <template slot="header">{{ __('Slideshows', 'ml-slider') }}</template> <template slot="description"> <pre> {{ sprintf(__('Date: %s', 'ml-slider'), fileDate()) }} {{ sprintf(__('Version: v%s', 'ml-slider'), metadata.version) }} </pre> <div v-if="!procesingImages && !missingImages.length"> {{ __('All images required to import are accounted for.', 'ml-slider') }} </div> <transition name="settings-fade" mode="in-out"> <div v-if="!procesingImages && missingImages.length"> <h4>{{ __('The following images were not found:', 'ml-slider') }}</h4> <div> <div v-for="slideshow in missingImages" :key="slideshow"> <p class="font-bold underline mb-1"> {{ slideshowsList[slideshow].title }} </p> <ul class="mb-4"> <li v-for="slide in slidesMissingImages(slideshow)" :key="slide.original_id" class="mb-0"> {{ slide.image ? slide.image : __('No image name provided' , 'ml-slider') }} </li> </ul> </div> </div> </div> </transition> </template> <template slot="description3"> <transition name="settings-fade" mode="in-out"> <div v-if="!procesingImages && missingImages.length"> {{ __('Note: You can still import slideshows that contain missing images. You will just need to manually update or delete these slides from their individual slideshow edit pages.', 'ml-slider') }} </div> </transition> </template> <template slot="fields"> <div class="mb-10"> <action-button @click="importSlideshows" :disabled="!slideshowsToImport.length"> <template slot="header">{{ sprintf(__('Import %s slideshows', 'ml-slider'), slideshowsToImport.length) }}</template> <template slot="description">{{ __('Select the slideshows you wish to import below, then press here to import them.', 'ml-slider') }}</template> <template slot="button">{{ __('Import', 'ml-slider') }}</template> </action-button> <switch-single-input v-if="Object.keys(slideshowsList).length > 10" :value="slideshowsToImport.length > 0" @change="toggleSlideshowsToImport($event)"> <template slot="header">{{ __('Toggle all slideshows') }}</template> <template slot="description">{{ __('Select or deselect all slideshows') }}</template> </switch-single-input> </div> <template v-for="(slideshow, index) in slideshowsList"> <switch-single-input :key="index" v-model="slideshowsListSelection[index]" class="transition-all duration-150 ease-linear" :style="{ filter: slideshowsListSelection[index] ? 'none' : 'grayscale(1)' }"> <template slot="header"> <input :value="slideshow.title || 'Title not found'" class="-ml-2 hover:bg-gray-lighter hover:border border-gray-light px-2 py-1 text-lg" @change="slideshowsList[index].title = $event.target.value"> </template> <template slot="description"> <div v-if="slideshow.slides" class="pl-3 inline-flex flex-row-reverse justify-end relative z-0 overflow-hidden"> <div v-for="slide in slideshow.slides" :key="slide.original_id" class="relative -ml-3 z-30 inline-block h-12 w-12 text-white border border-gray-light shadow-solid rounded-full"> <div v-if="'post_feed' === slide.meta['ml-slider_type']" class="bg-blue border border-blue flex items-center justify-center text-lg text-white rounded-full h-full tipsy-tooltip-top" :original-title="__('Post Feed slide', 'ml-slider')" :title="__('Post Feed slide', 'ml-slider')"> P </div> <div v-else-if="'external' === slide.meta['ml-slider_type']" class="bg-blue-light border border-blue-light flex items-center justify-center text-lg text-white rounded-full h-full tipsy-tooltip-top" :original-title="__('External slide', 'ml-slider')" :title="__('External slide', 'ml-slider')"> E </div> <div v-else-if="!slide.id && !procesingImages" :style="{ 'animation-delay': [(500 * index * Math.random()) + 'ms'] }" class="gradient border border-white rounded-full h-full flex justify-center items-center text-red tipsy-tooltip-top" :original-title="sprintf(__('Image not found<br>%s', 'ml-slider'), slide.image)" :title="sprintf(__('Image not found<br>%s', 'ml-slider'), slide.image)"> x </div> <div v-else-if="!slide.id" :style="{ 'animation-delay': [(500 * index * Math.random()) + 'ms'] }" class="gradient border border-white text-white rounded-full h-full"/> <img v-else :src="imageThumbnails[slide.id]" class="gradient border border-white rounded-full h-full inline-block" alt=""> </div> <div class="relative -ml-3 z-50 inline-block bg-gray-lighter flex items-center justify-center text-lg text-gray-dark h-12 w-12 rounded-full shadow-solid border border-gray-light"> {{ slideshow.slides.length }} </div> </div> <div v-else> {{ __('No slides found', 'ml-slider') }} </div> </template> </switch-single-input> </template> </template> </split-layout> </div> </template> <script> import { Image, Slideshow } from '../../api' import Swal from 'sweetalert2' import { default as SplitLayout } from '../layouts/_split' import { default as SwitchSingle } from '../inputs/_switchSingle' import { default as ActionButton } from '../inputs/_actionButton' import { default as FileButton } from '../inputs/_fileButton' import { default as fileDownload } from 'js-file-download' import { DateTime } from "luxon" export default { components: { 'split-layout' : SplitLayout, 'switch-single-input' : SwitchSingle, 'file-button' : FileButton, 'action-button' : ActionButton, }, computed: { slideshowsToImport() { if (!Object.keys(this.slideshowsListSelection).length) return [] let ids = [] Object.keys(this.slideshowsListSelection).forEach(slideshowId => { this.slideshowsListSelection[slideshowId] && ids.push(slideshowId) }) return ids }, missingImages() { // Only check slideshows they want to import return this.slideshowsToImport.filter(index => { if (!this.slideshowsList[index] || !this.slideshowsList[index].slides) return false let slides = this.slideshowsList[index].slides.filter(slide => { if (['external', 'post_feed'].indexOf(slide.meta['ml-slider_type']) > -1) return false return !slide.id }) return slides.length }) } }, watch: { slideshowsList: { immediate: false, handler: function(slideshowsFromFile) { // TODO: check if any images are even missing IDs (only needed if they upload a new file) let images = [] Object.keys(slideshowsFromFile).forEach(index => { if (this.slideshowsList[index].slides) { let imagesNames = this.slideshowsList[index].slides.map(slide => [slide.image, slide.image_alt]) images.push(...imagesNames) } }) if (images.length) { images = images.flat().filter(image => image.length) images = [...new Set(images)] images && this.findImages(images) } } }, }, props: {}, data() { return { metadata: '', slideshowsList: {}, slideshowsListSelection: {}, imageThumbnails: {}, processing: false, procesingImages: false, importing: false, userSawProcessingImagesMessage: false } }, created() {}, mounted() {}, methods: { loadSlideshowsFromFile(data) { this.slideshowsList = {} this.slideshowsListSelection = {} // TODO: test with uploading different export file if (!data) { this.notifyWarning( 'metaslider-importing-slideshows-bad-data', this.__('The data in this file does not appear to be valid.', 'ml-slider'), true) } this.processing = true try { data = JSON.parse(data) this.metadata = data.metadata delete data.metadata this.slideshowsList = data const slideshowsListSelection = {} for (const [key, slideshow] of Object.entries(this.slideshowsList)) { slideshowsListSelection[key] = true } this.slideshowsListSelection = slideshowsListSelection this.notifySuccess( 'metaslider/all-slideshows-from-file-loaded', this.sprintf( this.__('Found %s slideshows', 'ml-slider'), Object.keys(this.slideshowsList).length ), true) } catch (error) { this.slideshowsList = {} this.notifyError('metaslider/all-slideshows-from-file-error', error.message, true) } this.processing = false }, findImages(filenames) { this.procesingImages = true Image.findIdFromFilename(JSON.stringify(filenames)).then(response => { const images = response.data.data // Create lookup table for thumbnails Object.keys(images).forEach(filename => { images[filename] && this.$set(this.imageThumbnails, images[filename].id, images[filename].thumbnail) }) // Set the ID on the slides so they will be properly imported Object.keys(this.slideshowsList).forEach(slideshow => { if (this.slideshowsList[slideshow].slides) { Object.keys(this.slideshowsList[slideshow].slides).forEach(slide => { let filename = this.slideshowsList[slideshow].slides[slide].image let filenameAlt = this.slideshowsList[slideshow].slides[slide].image_alt if (images[filename]) { this.$set(this.slideshowsList[slideshow].slides[slide], 'id', images[filename].id) } else if (images[filenameAlt]) { this.$set(this.slideshowsList[slideshow].slides[slide], 'id', images[filenameAlt].id) } }) } }) this.procesingImages = false // Only show this if the user attempted to import while still processing if (this.userSawProcessingImagesMessage) { this.notifyInfo( 'metaslider-finding-images-success', this.__('Image search complete', 'ml-slider'), true) } }).catch(error => { this.notifyError('metaslider/import-from-file-error', error, true) }) }, async importSlideshows() { if (this.procesingImages) { this.userSawProcessingImagesMessage = true this.notifyWarning( 'metaslider-importing-slideshows-still-processing-images', this.__('We are still searching for your images. Please wait.', 'ml-slider'), true) return } if (!this.slideshowsToImport.length) { this.notifyWarning( 'metaslider-importing-slideshows-no-slideshows', this.__('You have no slideshows to import', 'ml-slider'), true) } this.importing = true this.processing = true const slideshowData = [] this.slideshowsToImport.forEach(key => { slideshowData.push(this.slideshowsList[key]) }) // If images are missing, give the user information and the choice to proceed const readyToImport = this.missingImages.length ? await Swal.fire({ title: this.__('Some images are missing', 'ml-slider'), html: '<p class="text-base">' + this.__('When images are missing you will have to manually update or delete the slide after the import completes.', 'ml-slider') + '</p>', confirmButtonText: this.__('Continue', 'ml-slider'), showCancelButton: true, icon: 'warning', iconHtml: '<div class="dashicons dashicons-warning" style="transform: scale(3.5);"></div>', customClass: 'shadow-lg', }) : { value: true } // If the user clicked cancel if (!readyToImport.value) { this.importing = false this.processing = false return } this.notifyInfo( 'metaslider-importing-slideshows', this.sprintf( this.__('Importing %s slideshows...', 'ml-slider'), this.slideshowsToImport.length ), true) Slideshow.import(JSON.stringify([...slideshowData])).then(response => { this.notifySuccess( 'metaslider-importing-slideshows-success', this.__('Import successful', 'ml-slider'), true) }).catch(error => { this.notifyError('metaslider/import-error', error, true) }).finally(() => { this.importing = false this.processing = false }) }, fileDate() { return DateTime.fromISO(new Date((this.metadata.date)).toISOString()).toFormat('yyyy/MM/dd') }, randomBgColor() { const bgColors = ['bg-gray-dark', 'bg-gray-light', 'bg-gray-lighter', 'bg-gray-lightest'] return bgColors[Math.floor(Math.random() * bgColors.length)] }, slidesMissingImages(slideshow) { return this.slideshowsList[slideshow].slides.filter(slide => { if (['post_feed', 'external'].indexOf(slide.meta['ml-slider_type']) > -1) return false return !slide.id }) }, toggleSlideshowsToImport(state) { Object.keys(this.slideshowsListSelection).forEach(slideshow => this.slideshowsListSelection[slideshow] = state) }, } } </script> <style scoped> .gradient { animation-duration: 3s; animation-fill-mode: forwards; animation-iteration-count: infinite; animation-name: placeHolderShimmer; animation-timing-function: linear; background: #f1f1f1; background: linear-gradient(to right, #f1f1f1 8%, #f8fafc 38%, #f1f1f1 54%); background-size: 1000px 640px; position: relative; } @keyframes placeHolderShimmer { 0%{ background-position: -468px 0 } 100%{ background-position: 468px 0 } } </style>