%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>