PluginProbe
Filter Gallery / trunk
Filter Gallery vtrunk
1.1.5 1.1.4 1.1.3 1.1.2 trunk 0.0.1 0.0.2 0.0.3 0.0.4 0.0.5 0.0.6 0.0.7 0.0.8 0.0.9 0.1.0 0.1.1 0.1.2 0.1.3 0.1.4 0.1.5 0.1.6 0.1.7 0.1.8 0.1.9 0.2.0 All 28 releases
filter-gallery / admin / import-export.php

import-export.php in Filter Gallery trunk, at admin/import-export.php

436 lines 17.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if (!defined('ABSPATH')) {
3 exit;
4 }
5 $ufg_loader_title = __('Loading Import / Export Dashboard...', 'filter-gallery');
6 $ufg_loader_subtitle = __('Please wait while settings configurations load', 'filter-gallery');
7 require_once 'loader.php';
8 ?>
9 <div class="wrap ufg-docs-page" style="margin: 32px 20px 0 10px;">
10 <style>
11 .ufg-docs-page * { box-sizing: border-box; }
12 html { scroll-behavior: smooth; }
13 .ufg-docs-page .max-w-7xl { max-width: 80rem; margin-left: auto; margin-right: auto; }
14 .ufg-docs-page .flex { display: flex; }
15 .ufg-docs-page .flex-col { flex-direction: column; }
16 .ufg-docs-page .items-center { align-items: center; }
17 .ufg-docs-page .justify-between { justify-content: space-between; }
18 .ufg-docs-page .grid { display: grid; grid-template-columns: 1fr; gap: 2rem; }
19 .ufg-docs-page .gap-6 { gap: 1.5rem; }
20 @media (min-width: 1024px) {
21 .ufg-docs-page .lg\:grid-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
22 .ufg-docs-page .lg\:flex-row { flex-direction: row; }
23 }
24 .ufg-docs-page .bg-white { background-color: #ffffff; }
25 .ufg-docs-page .rounded-2xl { border-radius: 1rem; }
26 .ufg-docs-page .rounded-3xl { border-radius: 1.5rem; }
27 .ufg-docs-page .shadow-sm { box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06); }
28 .ufg-docs-page .p-6 { padding: 1.5rem; }
29 .ufg-docs-page .p-8 { padding: 2rem; }
30 .ufg-docs-page .border { border: 1px solid #e2e8f0; }
31 .ufg-docs-page .text-gray-900 { color: #1a202c; }
32 .ufg-docs-page .text-gray-600 { color: #4a5568; }
33 .ufg-docs-page .text-blue-600 { color: #2563eb; }
34 .ufg-docs-page .font-black { font-weight: 900; }
35 .ufg-docs-page .font-bold { font-weight: 700; }
36 .ufg-docs-page .font-medium { font-weight: 500; }
37 .ufg-docs-page .tracking-tight { letter-spacing: -0.025em; }
38 .ufg-docs-page .no-underline { text-decoration: none; }
39 .ufg-docs-page .space-y-4 > * + * { margin-top: 1rem; }
40 .ufg-docs-page .space-y-6 > * + * { margin-top: 1.5rem; }
41 .ufg-docs-page .space-x-5 > * + * { margin-left: 1.25rem; }
42
43 /* Header Icon */
44 .ufg-header-icon {
45 width: 56px;
46 height: 56px;
47 background: #2563eb;
48 display: flex;
49 align-items: center;
50 justify-content: center;
51 border-radius: 16px;
52 color: white;
53 box-shadow: 0 4px 14px 0 rgba(37, 99, 235, 0.39);
54 }
55
56 /* Specific UI elements */
57 .ufg-btn-primary {
58 background: #2563eb; color: white; padding: 10px 20px; border-radius: 8px; font-weight: 700; font-size: 14px; text-transform: uppercase; letter-spacing: 1px; display: inline-flex; align-items: center; box-shadow: 0 4px 10px rgba(37, 99, 235, 0.3); border: none; cursor: pointer; transition: all 0.2s;
59 }
60 .ufg-btn-primary:hover:not(:disabled) { transform: translateY(-2px); box-shadow: 0 6px 14px rgba(37, 99, 235, 0.4); }
61 .ufg-btn-primary:disabled { opacity: 0.6; cursor: not-allowed; box-shadow: none; transform: none; }
62
63 .ufg-ie-gallery-list {
64 max-height: 250px;
65 overflow-y: auto;
66 border: 1px solid #e2e8f0;
67 border-radius: 12px;
68 padding: 12px;
69 background: #f8fafc;
70 margin-bottom: 24px;
71 }
72 .ufg-ie-gallery-item, .ufg-ie-select-all {
73 display: flex;
74 align-items: center;
75 gap: 12px;
76 padding: 10px 14px;
77 border-radius: 8px;
78 cursor: pointer;
79 transition: background 0.15s;
80 }
81 .ufg-ie-gallery-item:hover { background: #e2e8f0; }
82 .ufg-ie-gallery-item code { color: #718096; font-size: 12px; background: transparent; padding: 0; }
83 .ufg-ie-select-all { border-bottom: 1px solid #e2e8f0; margin-bottom: 6px; padding-bottom: 14px; font-weight: 700; }
84 .ufg-ie-empty { color: #a0aec0; text-align: center; padding: 30px; }
85
86 .ufg-ie-upload-area {
87 border: 2px dashed #cbd5e0;
88 border-radius: 16px;
89 padding: 40px 20px;
90 text-align: center;
91 margin-bottom: 24px;
92 transition: all 0.2s;
93 cursor: pointer;
94 background: #f8fafc;
95 }
96 .ufg-ie-upload-area.dragover { border-color: #2563eb; background: #ebf8ff; }
97 .ufg-ie-upload-area .dashicons { display: block; margin: 0 auto 12px; color: #a0aec0; font-size: 48px; width: 48px; height: 48px; }
98 .ufg-ie-filename { color: #2563eb; font-weight: 700; margin-top: 12px; }
99 .ufg-ie-import-options { margin-bottom: 24px; padding: 14px 16px; background: #edf2f7; border-radius: 8px; font-weight: 500; font-size: 14px; color: #4a5568; }
100
101 .ufg-ie-status {
102 margin-top: 32px;
103 background: #ffffff;
104 border: 1px solid #e2e8f0;
105 border-radius: 1.5rem;
106 padding: 2rem;
107 box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1);
108 }
109 .ufg-ie-progress-bar {
110 height: 12px;
111 background: #edf2f7;
112 border-radius: 6px;
113 overflow: hidden;
114 margin: 16px 0 24px;
115 }
116 .ufg-ie-progress-fill {
117 height: 100%;
118 background: linear-gradient(90deg, #2563eb, #60a5fa);
119 border-radius: 6px;
120 transition: width 0.3s ease;
121 }
122 .ufg-ie-log {
123 max-height: 250px;
124 overflow-y: auto;
125 font-family: 'JetBrains Mono', monospace, Consolas;
126 font-size: 13px;
127 color: #4a5568;
128 line-height: 1.8;
129 padding: 16px;
130 background: #f8fafc;
131 border-radius: 12px;
132 border: 1px solid #e2e8f0;
133 }
134 .ufg-ie-log .success { color: #16a34a; font-weight: 600; }
135 .ufg-ie-log .error { color: #dc2626; font-weight: 600; }
136 .ufg-ie-log .warning { color: #d97706; }
137 .ufg-ie-log .info { color: #2563eb; }
138
139 input[type="checkbox"] { width: 18px; height: 18px; border-radius: 4px; border-color: #cbd5e0; }
140 input[type="checkbox"]:checked { background-color: #2563eb; border-color: #2563eb; }
141 </style>
142
143 <div class="max-w-7xl">
144 <!-- Header -->
145 <div class="flex flex-col lg:flex-row lg:items-center justify-between mb-10 flex-wrap gap-6" style="border-bottom: 2px solid #edf2f7; padding-bottom: 32px;">
146 <div class="flex items-center space-x-5">
147 <div class="ufg-header-icon">
148 <span class="dashicons dashicons-update" style="font-size: 32px; width: 32px; height: 32px; transform: rotate(90deg);"></span>
149 </div>
150 <div>
151 <h1 class="font-black text-gray-900 tracking-tight" style="font-size: 42px; margin: 0; line-height: 1;">Import / Export <span style="font-size: 14px; background: #2563eb; color: white; padding: 4px 8px; border-radius: 6px; vertical-align: middle; margin-left: 10px;">v<?php echo esc_html(UFG_VERSION); ?></span></h1>
152 <p class="text-gray-600 font-medium" style="margin-top: 8px; font-size: 16px;">Transfer your filterable galleries seamlessly between WordPress sites.</p>
153 </div>
154 </div>
155
156 <div class="flex items-center gap-4 flex-wrap">
157 <a href="<?php echo esc_url(admin_url('admin.php?page=filter-gallery-pro')); ?>" class="no-underline ufg-btn-primary" style="background: #1a202c; box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);">
158 Back to Dashboard
159 </a>
160 </div>
161 </div>
162
163 <div class="grid lg:grid-cols-2 gap-6">
164 <!-- EXPORT SECTION -->
165 <div class="bg-white border rounded-3xl p-8 shadow-sm">
166 <h2 class="font-black text-gray-900" style="font-size: 28px; margin: 0 0 16px 0;">Export Galleries</h2>
167 <p class="text-gray-600 font-medium mb-6">Select galleries to export. The exported JSON file will include all gallery settings, filters, and image URLs.</p>
168
169 <div class="ufg-ie-gallery-list" id="ufg-export-gallery-list">
170 <?php
171 global $wpdb;
172 $ufg_gallery_key = "ufg_gallery_";
173 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- direct query is required here to scan for option patterns for gallery export
174 $ufg_all_galleries = $wpdb->get_results(
175 $wpdb->prepare(
176 "SELECT option_name FROM `{$wpdb->prefix}options` WHERE `option_name` LIKE %s ORDER BY option_id ASC",
177 '%' . $wpdb->esc_like($ufg_gallery_key) . '%'
178 )
179 );
180 if (!empty($ufg_all_galleries)) {
181 echo '<label class="ufg-ie-select-all"><input type="checkbox" id="ufg-export-select-all"> Select All</label>';
182 foreach ($ufg_all_galleries as $gallery) {
183 $option_name = $gallery->option_name;
184 $underscore_pos = strrpos($option_name, '_');
185 $gallery_id = substr($option_name, ($underscore_pos + 1));
186 $details = get_option("ufg_details_" . $gallery_id);
187 $gallery_name = isset($details['gallery_name']) ? $details['gallery_name'] : 'Gallery #' . $gallery_id;
188 echo '<label class="ufg-ie-gallery-item">';
189 echo '<input type="checkbox" name="ufg_export_ids[]" value="' . intval($gallery_id) . '"> ';
190 echo '<span>' . esc_html($gallery_name) . ' <code>[ufg id="' . intval($gallery_id) . '"]</code></span>';
191 echo '</label>';
192 }
193 } else {
194 echo '<p class="ufg-ie-empty">No galleries found.</p>';
195 }
196 ?>
197 </div>
198 <button type="button" id="ufg-export-btn" class="ufg-btn-primary" <?php echo empty($ufg_all_galleries) ? 'disabled' : ''; ?>>
199 <span class="dashicons dashicons-download" style="margin-right:8px; line-height: 1.3;"></span> Export Selected
200 </button>
201 </div>
202
203 <!-- IMPORT SECTION -->
204 <div class="bg-white border rounded-3xl p-8 shadow-sm">
205 <h2 class="font-black text-gray-900" style="font-size: 28px; margin: 0 0 16px 0;">Import Galleries</h2>
206 <p class="text-gray-600 font-medium mb-6">Upload a previously exported JSON file to import galleries. Images will be downloaded automatically.</p>
207
208 <div class="ufg-ie-upload-area" id="ufg-import-drop-zone">
209 <div>
210 <span class="dashicons dashicons-media-default"></span>
211 <p class="font-bold text-gray-900" style="font-size: 16px; margin: 10px 0 6px;">Drag & drop your .json file here</p>
212 <p class="text-gray-600 font-medium" style="margin-bottom: 16px;">or click to browse</p>
213 <label class="ufg-btn-primary" style="background:#cbd5e0; color:#1a202c; box-shadow:none; padding:8px 16px; font-size:12px;" for="ufg-import-file">Choose File</label>
214 <input type="file" id="ufg-import-file" accept=".json" style="display:none;">
215 <p class="ufg-ie-filename" id="ufg-import-filename"></p>
216 </div>
217 </div>
218
219 <div class="ufg-ie-import-options" id="ufg-import-options" style="display:none;">
220 <label class="flex items-center cursor-pointer gap-2">
221 <input type="checkbox" id="ufg-import-skip-images" value="1">
222 Skip image download (only import settings & filters)
223 </label>
224 </div>
225
226 <button type="button" id="ufg-import-btn" class="ufg-btn-primary" disabled>
227 <span class="dashicons dashicons-upload" style="margin-right:8px; line-height: 1.3;"></span> Import Galleries
228 </button>
229 </div>
230 </div>
231
232 <!-- STATUS / LOG -->
233 <div class="ufg-ie-status" id="ufg-ie-status" style="display:none;">
234 <h3 class="font-black text-gray-900" id="ufg-ie-status-title" style="margin:0 0 8px; font-size: 20px;">Processing...</h3>
235 <div class="ufg-ie-progress-bar">
236 <div class="ufg-ie-progress-fill" id="ufg-ie-progress-fill" style="width:0%"></div>
237 </div>
238 <div class="ufg-ie-log" id="ufg-ie-log"></div>
239 </div>
240 </div>
241 </div>
242
243 <script>
244 jQuery(document).ready(function($) {
245
246 // Select All toggle
247 $('#ufg-export-select-all').on('change', function() {
248 $('input[name="ufg_export_ids[]"]').prop('checked', $(this).is(':checked'));
249 });
250
251 // EXPORT
252 $('#ufg-export-btn').on('click', function() {
253 var ids = [];
254 $('input[name="ufg_export_ids[]"]:checked').each(function() {
255 ids.push($(this).val());
256 });
257 if (ids.length === 0) {
258 alert('Please select at least one gallery to export.');
259 return;
260 }
261 var $btn = $(this);
262 $btn.prop('disabled', true).html('<span class="dashicons dashicons-update-alt" style="margin-right:8px; line-height: 1.3;"></span> Exporting...');
263 showStatus('Exporting ' + ids.length + ' gallery(ies)...');
264 addLog('Starting export for gallery IDs: ' + ids.join(', '), 'info');
265
266 $.ajax({
267 url: ajaxurl,
268 type: 'POST',
269 data: {
270 action: 'ufg_export_galleries',
271 nonce: '<?php echo esc_js(wp_create_nonce("ufg-import-export")); ?>',
272 gallery_ids: ids
273 },
274 success: function(response) {
275 if (response.success) {
276 // Trigger download
277 var blob = new Blob([JSON.stringify(response.data, null, 2)], {type: 'application/json'});
278 var url = URL.createObjectURL(blob);
279 var a = document.createElement('a');
280 var date = new Date().toISOString().slice(0,10);
281 a.href = url;
282 a.download = 'filter-gallery-export-' + date + '.json';
283 document.body.appendChild(a);
284 a.click();
285 document.body.removeChild(a);
286 URL.revokeObjectURL(url);
287 addLog('Export completed! ' + response.data.galleries.length + ' gallery(ies) exported.', 'success');
288 setProgress(100);
289 } else {
290 addLog('Export failed: ' + (response.data || 'Unknown error'), 'error');
291 }
292 $btn.prop('disabled', false).html('<span class="dashicons dashicons-download" style="margin-right:8px; line-height: 1.3;"></span> Export Selected');
293 },
294 error: function(xhr, status, err) {
295 addLog('Export request failed: ' + err, 'error');
296 $btn.prop('disabled', false).html('<span class="dashicons dashicons-download" style="margin-right:8px; line-height: 1.3;"></span> Export Selected');
297 }
298 });
299 });
300
301 // IMPORT — File handling
302 var importFile = null;
303
304 $('#ufg-import-drop-zone').on('click', function(e) {
305 if(e.target.tagName.toLowerCase() !== 'label' && e.target.tagName.toLowerCase() !== 'input') {
306 $('#ufg-import-file').click();
307 }
308 });
309
310 $('#ufg-import-file').on('change', function(e) {
311 if (e.target.files.length) {
312 setImportFile(e.target.files[0]);
313 }
314 });
315
316 $('#ufg-import-drop-zone').on('dragover', function(e) {
317 e.preventDefault();
318 $(this).addClass('dragover');
319 }).on('dragleave drop', function(e) {
320 e.preventDefault();
321 $(this).removeClass('dragover');
322 if (e.type === 'drop' && e.originalEvent.dataTransfer.files.length) {
323 setImportFile(e.originalEvent.dataTransfer.files[0]);
324 }
325 });
326
327 function setImportFile(file) {
328 if (!file.name.endsWith('.json')) {
329 alert('Please select a valid .json file.');
330 return;
331 }
332 importFile = file;
333 $('#ufg-import-filename').text('Selected: ' + file.name + ' (' + formatBytes(file.size) + ')');
334 $('#ufg-import-btn').prop('disabled', false);
335 $('#ufg-import-options').slideDown(200);
336 }
337
338 function formatBytes(bytes) {
339 if (bytes < 1024) return bytes + ' B';
340 if (bytes < 1048576) return (bytes / 1024).toFixed(1) + ' KB';
341 return (bytes / 1048576).toFixed(1) + ' MB';
342 }
343
344 // IMPORT — Process
345 $('#ufg-import-btn').on('click', function() {
346 if (!importFile) return;
347 var $btn = $(this);
348 $btn.prop('disabled', true).html('<span class="dashicons dashicons-update-alt" style="margin-right:8px; line-height: 1.3;"></span> Reading file...');
349 showStatus('Importing galleries...');
350
351 var reader = new FileReader();
352 reader.onload = function(e) {
353 try {
354 var data = JSON.parse(e.target.result);
355 } catch (err) {
356 addLog('Invalid JSON file: ' + err.message, 'error');
357 $btn.prop('disabled', false).html('<span class="dashicons dashicons-upload" style="margin-right:8px; line-height: 1.3;"></span> Import Galleries');
358 return;
359 }
360
361 if (!data.plugin || data.plugin !== 'filter-gallery-pro' || !data.galleries) {
362 addLog('This file is not a valid Filter Gallery Pro export.', 'error');
363 $btn.prop('disabled', false).html('<span class="dashicons dashicons-upload" style="margin-right:8px; line-height: 1.3;"></span> Import Galleries');
364 return;
365 }
366
367 addLog('Found ' + data.galleries.length + ' gallery(ies) in export file (v' + data.version + ' from ' + data.source_url + ')', 'info');
368 $btn.html('<span class="dashicons dashicons-update-alt" style="margin-right:8px; line-height: 1.3;"></span> Importing...');
369
370 var skipImages = $('#ufg-import-skip-images').is(':checked') ? 1 : 0;
371
372 // Import galleries one by one
373 importNext(data.galleries, 0, skipImages, $btn);
374 };
375 reader.readAsText(importFile);
376 });
377
378 function importNext(galleries, idx, skipImages, $btn) {
379 if (idx >= galleries.length) {
380 addLog('All ' + galleries.length + ' gallery(ies) imported successfully!', 'success');
381 setProgress(100);
382 $btn.prop('disabled', false).html('<span class="dashicons dashicons-upload" style="margin-right:8px; line-height: 1.3;"></span> Import Galleries');
383 return;
384 }
385
386 var g = galleries[idx];
387 var pct = Math.round(((idx) / galleries.length) * 100);
388 setProgress(pct);
389 var gName = (g.details && g.details.gallery_name) ? g.details.gallery_name : 'Gallery #' + g.original_id;
390 addLog('Importing "' + gName + '" (' + (idx + 1) + '/' + galleries.length + ')...', 'info');
391
392 $.ajax({
393 url: ajaxurl,
394 type: 'POST',
395 data: {
396 action: 'ufg_import_gallery',
397 nonce: '<?php echo esc_js(wp_create_nonce("ufg-import-export")); ?>',
398 gallery_data: JSON.stringify(g),
399 skip_images: skipImages
400 },
401 timeout: 120000,
402 success: function(response) {
403 if (response.success) {
404 addLog('✓ "' + gName + '" imported as gallery #' + response.data.new_id +
405 (response.data.images_imported > 0 ? ' (' + response.data.images_imported + ' images)' : "") +
406 (response.data.images_failed > 0 ? ' (' + response.data.images_failed + ' images failed)' : ""),
407 response.data.images_failed > 0 ? 'warning' : 'success'
408 );
409 } else {
410 addLog('✗ Failed to import "' + gName + '": ' + (response.data || 'Unknown error'), 'error');
411 }
412 importNext(galleries, idx + 1, skipImages, $btn);
413 },
414 error: function(xhr, status, err) {
415 addLog('✗ Request failed for "' + gName + '": ' + err, 'error');
416 importNext(galleries, idx + 1, skipImages, $btn);
417 }
418 });
419 }
420
421 function showStatus(title) {
422 $('#ufg-ie-status').slideDown(300);
423 $('#ufg-ie-status-title').text(title);
424 $('#ufg-ie-log').empty();
425 setProgress(0);
426 }
427 function addLog(msg, cls) {
428 $('#ufg-ie-log').append('<div class="' + (cls || '') + '">' + msg + '</div>');
429 $('#ufg-ie-log').scrollTop($('#ufg-ie-log')[0].scrollHeight);
430 }
431 function setProgress(pct) {
432 $('#ufg-ie-progress-fill').css('width', pct + '%');
433 }
434 });
435 </script>
436