PluginProbe ʕ •ᴥ•ʔ
Gallery : FooGallery / 3.3.2
Gallery : FooGallery v3.3.2
3.3.2 3.3.3 3.3.0 3.1.31 3.1.32 3.1.34 3.1.36 3.2.0 3.2.6 trunk 1.10.3 2.0.24 2.1.34 2.2.44 2.3.3 2.4.32 3.0.6 3.1.11 3.1.12 3.1.13 3.1.20 3.1.25 3.1.26 3.1.26.1 3.1.26.2
foogallery / extensions / import-export / class-foogallery-import-export.php
foogallery / extensions / import-export Last commit date
class-foogallery-export-view-helper.php 3 days ago class-foogallery-import-export-extension.php 3 days ago class-foogallery-import-export.php 3 days ago class-foogallery-import-view-helper.php 3 days ago functions.php 3 days ago view-import-export.php 3 days ago
class-foogallery-import-export.php
561 lines
1 <?php
2
3 if ( ! defined( 'ABSPATH' ) ) {
4 exit;
5 }
6
7 /**
8 * FooGallery - Import Export Class
9 */
10
11 if ( ! class_exists( 'FooGallery_Import_Export' ) ) {
12
13 require_once plugin_dir_path( __FILE__ ) . 'functions.php';
14
15 /**
16 * Class FooGallery_Import_Export
17 */
18 class FooGallery_Import_Export {
19 const IMPORT_JOB_TTL = 21600; // 6 hours
20 const IMPORT_JOB_USER_META_KEY = 'foogallery_import_export_last_job';
21
22 /**
23 * Wire up everything we need to run the extension
24 */
25 public function __construct() {
26 if ( is_admin() ) {
27 //add_action( 'add_meta_boxes_foogallery', array( $this, 'add_export_metabox' ) );
28 add_action( 'foogallery_admin_menu_after', array( $this, 'add_import_export_menu' ) );
29 add_action( 'wp_ajax_foogallery_gallery_export', array( $this, 'ajax_generate_export' ) );
30 add_action( 'wp_ajax_foogallery_gallery_import', array( $this, 'ajax_import_galleries' ) );
31 }
32 }
33
34 /**
35 * Output a log message
36 *
37 * @param string $message The message to show.
38 * @param bool $line_break If a line break should also be output.
39 */
40 public function log_message( $message, $line_break = true ) {
41 echo esc_html( $message );
42 if ( $line_break ) {
43 echo '<br />';
44 }
45 }
46
47 /**
48 * Import galleries
49 */
50 public function ajax_import_galleries() {
51 if ( ! current_user_can( 'manage_options' ) ) {
52 wp_send_json_error(
53 array( 'message' => __( 'You do not have permission to import galleries.', 'foogallery' ) ),
54 403
55 );
56 }
57
58 check_ajax_referer( 'foogallery_gallery_import' );
59
60 $mode = 'start';
61 if ( isset( $_POST['mode'] ) ) {
62 $mode = sanitize_key( wp_unslash( $_POST['mode'] ) );
63 }
64
65 if ( 'status' === $mode ) {
66 $this->ajax_import_status();
67 } elseif ( 'delete' === $mode ) {
68 $this->ajax_import_delete();
69 } elseif ( 'step' === $mode ) {
70 $this->ajax_import_step();
71 } else {
72 $this->ajax_import_start();
73 }
74 }
75
76 private function ajax_import_status() {
77 $job_id = $this->get_last_import_job_id();
78 if ( empty( $job_id ) ) {
79 wp_send_json_success( array( 'has_job' => false ) );
80 }
81
82 $job = $this->load_import_job( $job_id );
83 if ( false === $job ) {
84 $this->clear_last_import_job_id();
85 wp_send_json_success( array( 'has_job' => false ) );
86 }
87
88 $data = $this->format_import_job_progress( $job_id, $job );
89 $data['has_job'] = true;
90 $data['can_delete'] = true;
91 wp_send_json_success( $data );
92 }
93
94 private function ajax_import_delete() {
95 $job_id = '';
96 if ( isset( $_POST['job_id'] ) ) {
97 $job_id = sanitize_text_field( wp_unslash( $_POST['job_id'] ) );
98 }
99 if ( empty( $job_id ) ) {
100 $job_id = $this->get_last_import_job_id();
101 }
102
103 if ( empty( $job_id ) ) {
104 wp_send_json_success( array( 'deleted' => false ) );
105 }
106
107 $this->delete_import_job( $job_id );
108 $this->clear_last_import_job_id( $job_id );
109
110 wp_send_json_success( array( 'deleted' => true ) );
111 }
112
113 private function ajax_import_start() {
114 if ( ! isset( $_POST['data'] ) || empty( $_POST['data'] ) ) {
115 wp_send_json_error( array( 'message' => __( 'No import data provided!', 'foogallery' ) ), 400 );
116 }
117
118 $galleries = json_decode( wp_unslash( $_POST['data'] ), true );
119 if ( null === $galleries || ! is_array( $galleries ) ) {
120 wp_send_json_error( array( 'message' => __( 'The import data could not be interpreted.', 'foogallery' ) ), 400 );
121 }
122
123 $job_id = function_exists( 'wp_generate_uuid4' ) ? wp_generate_uuid4() : uniqid( 'foogallery_import_', true );
124 $job = $this->build_import_job( $galleries );
125
126 $this->save_import_job( $job_id, $job );
127 $this->set_last_import_job_id( $job_id );
128
129 $data = $this->run_import_step_with_lock( $job_id );
130 wp_send_json_success( $data );
131 }
132
133 private function ajax_import_step() {
134 if ( ! isset( $_POST['job_id'] ) || empty( $_POST['job_id'] ) ) {
135 wp_send_json_error( array( 'message' => __( 'Missing import job ID.', 'foogallery' ) ), 400 );
136 }
137
138 $job_id = sanitize_text_field( wp_unslash( $_POST['job_id'] ) );
139 $job = $this->load_import_job( $job_id );
140 if ( false === $job ) {
141 wp_send_json_error( array( 'message' => __( 'Import job not found or expired.', 'foogallery' ) ), 404 );
142 }
143
144 $this->set_last_import_job_id( $job_id );
145
146 $data = $this->run_import_step_with_lock( $job_id );
147 wp_send_json_success( $data );
148 }
149
150 private function run_import_step_with_lock( $job_id ) {
151 $job = $this->load_import_job( $job_id );
152 if ( false === $job ) {
153 return array(
154 'job_id' => $job_id,
155 'complete' => true,
156 'message' => __( 'Import job not found or expired.', 'foogallery' ),
157 );
158 }
159
160 $now = time();
161 if ( isset( $job['lock_until'] ) && intval( $job['lock_until'] ) > $now ) {
162 return array_merge( $this->format_import_job_progress( $job_id, $job ), array(
163 'busy' => true,
164 'message' => __( 'Import is busy, retrying...', 'foogallery' ),
165 ) );
166 }
167
168 $job['lock_until'] = $now + 20;
169 $this->save_import_job( $job_id, $job );
170
171 $result = $this->run_import_step( $job );
172
173 unset( $job['lock_until'] );
174 $this->save_import_job( $job_id, $job );
175
176 return array_merge( $this->format_import_job_progress( $job_id, $job ), $result );
177 }
178
179 private function build_import_job( array $galleries ) {
180 $attachment_tasks = array();
181
182 foreach ( $galleries as $gallery_index => $gallery ) {
183 if ( ! is_array( $gallery ) ) {
184 continue;
185 }
186
187 if ( ! isset( $gallery['datasource_name'] ) || 'media_library' !== $gallery['datasource_name'] ) {
188 continue;
189 }
190
191 if ( ! isset( $gallery['attachments'] ) || ! is_array( $gallery['attachments'] ) ) {
192 continue;
193 }
194
195 foreach ( $gallery['attachments'] as $old_attachment_id => $attachment ) {
196 $attachment_tasks[] = array(
197 'g' => $gallery_index,
198 'id' => (string) $old_attachment_id,
199 );
200 }
201 }
202
203 $total = count( $attachment_tasks ) + count( $galleries );
204
205 return array(
206 'created_at' => time(),
207 'stage' => count( $attachment_tasks ) > 0 ? 'attachments' : 'galleries',
208 'galleries' => $galleries,
209 'attachment_tasks' => $attachment_tasks,
210 'attachment_cursor' => 0,
211 'gallery_cursor' => 0,
212 'new_attachments' => array(),
213 'done' => 0,
214 'total' => $total,
215 );
216 }
217
218 private function run_import_step( array &$job ) {
219 $stage = isset( $job['stage'] ) ? $job['stage'] : 'attachments';
220
221 if ( 'done' === $stage ) {
222 return array(
223 'complete' => true,
224 'message' => __( 'Import complete.', 'foogallery' ),
225 );
226 }
227
228 if ( 'attachments' === $stage ) {
229 $cursor = intval( $job['attachment_cursor'] );
230 $tasks = isset( $job['attachment_tasks'] ) && is_array( $job['attachment_tasks'] ) ? $job['attachment_tasks'] : array();
231
232 if ( $cursor >= count( $tasks ) ) {
233 $job['stage'] = 'galleries';
234 return array(
235 'complete' => false,
236 'message' => __( 'Attachment import finished. Creating galleries...', 'foogallery' ),
237 );
238 }
239
240 $task = $tasks[ $cursor ];
241 $job['attachment_cursor'] = $cursor + 1;
242 $job['done'] = intval( $job['done'] ) + 1;
243
244 $gallery_index = isset( $task['g'] ) ? intval( $task['g'] ) : -1;
245 $old_id = isset( $task['id'] ) ? (string) $task['id'] : '';
246
247 if ( $gallery_index < 0 || empty( $old_id ) || ! isset( $job['galleries'][ $gallery_index ] ) ) {
248 return array(
249 'complete' => false,
250 'message' => __( 'Skipping invalid attachment task.', 'foogallery' ),
251 );
252 }
253
254 $gallery = $job['galleries'][ $gallery_index ];
255 if ( ! isset( $gallery['attachments'][ $old_id ] ) || ! is_array( $gallery['attachments'][ $old_id ] ) ) {
256 return array(
257 'complete' => false,
258 'message' => __( 'Skipping missing attachment data.', 'foogallery' ),
259 );
260 }
261
262 $attachment = $gallery['attachments'][ $old_id ];
263 $url = isset( $attachment['url'] ) ? $attachment['url'] : '';
264 if ( empty( $url ) ) {
265 return array(
266 'complete' => false,
267 'message' => __( 'Skipping attachment with missing URL.', 'foogallery' ),
268 );
269 }
270
271 $imported_attachment_id = $this->find_attachment( $url );
272 if ( 0 === $imported_attachment_id ) {
273 $imported_attachment_id = foogallery_import_attachment( $attachment );
274 if ( is_wp_error( $imported_attachment_id ) ) {
275 return array(
276 'complete' => false,
277 /* translators: %s: Attachment import error message. */
278 'message' => sprintf( __( 'Attachment import failed: %s', 'foogallery' ), $imported_attachment_id->get_error_message() ),
279 );
280 }
281
282 $job['new_attachments'][ $old_id ] = intval( $imported_attachment_id );
283 return array(
284 'complete' => false,
285 /* translators: %s: Imported attachment URL. */
286 'message' => sprintf( __( 'Imported attachment: %s', 'foogallery' ), $url ),
287 );
288 }
289
290 $job['new_attachments'][ $old_id ] = intval( $imported_attachment_id );
291 return array(
292 'complete' => false,
293 /* translators: %s: Existing attachment URL. */
294 'message' => sprintf( __( 'Attachment already exists: %s', 'foogallery' ), $url ),
295 );
296 }
297
298 $cursor = intval( $job['gallery_cursor'] );
299 $galleries = isset( $job['galleries'] ) && is_array( $job['galleries'] ) ? $job['galleries'] : array();
300
301 if ( $cursor >= count( $galleries ) ) {
302 $job['stage'] = 'done';
303 return array(
304 'complete' => true,
305 'message' => __( 'Import complete.', 'foogallery' ),
306 );
307 }
308
309 $gallery = $galleries[ $cursor ];
310 $job['gallery_cursor'] = $cursor + 1;
311 $job['done'] = intval( $job['done'] ) + 1;
312
313 if ( ! is_array( $gallery ) ) {
314 return array(
315 'complete' => false,
316 'message' => __( 'Skipping invalid gallery.', 'foogallery' ),
317 );
318 }
319
320 $datasource_name = isset( $gallery['datasource_name'] ) ? $gallery['datasource_name'] : '';
321 $gallery_id = isset( $gallery['ID'] ) ? $gallery['ID'] : '';
322 $name = isset( $gallery['name'] ) ? $gallery['name'] : '';
323 $template = isset( $gallery['template'] ) ? $gallery['template'] : '';
324
325 $search_gallery = FooGallery::get_by_id( $gallery_id );
326 if ( false !== $search_gallery && $search_gallery->ID === intval( $gallery_id ) && $search_gallery->gallery_template === $template && $search_gallery->name === $name && 'publish' === $search_gallery->post_status ) {
327 return array(
328 'complete' => false,
329 /* translators: %s: Existing gallery name. */
330 'message' => sprintf( __( 'Gallery already exists so skipping: %s', 'foogallery' ), $name ),
331 );
332 }
333
334 $gallery_data = array(
335 'post_title' => $name,
336 'post_status' => 'publish',
337 'post_type' => FOOGALLERY_CPT_GALLERY,
338 'meta_input' => array(
339 FOOGALLERY_META_TEMPLATE => $template,
340 FOOGALLERY_META_SETTINGS => isset( $gallery['settings'] ) ? $gallery['settings'] : array(),
341 FOOGALLERY_META_SORT => isset( $gallery['sorting'] ) ? $gallery['sorting'] : array(),
342 FOOGALLERY_META_DATASOURCE => $datasource_name,
343 FOOGALLERY_META_RETINA => isset( $gallery['retina'] ) ? $gallery['retina'] : '',
344 FOOGALLERY_META_CUSTOM_CSS => isset( $gallery['custom_css'] ) ? foogallery_sanitize_full( $gallery['custom_css'] ) : '',
345 ),
346 );
347
348 if ( 'media_library' === $datasource_name ) {
349 $new_attachment_ids = array();
350 if ( isset( $gallery['attachment_ids'] ) && is_array( $gallery['attachment_ids'] ) ) {
351 foreach ( $gallery['attachment_ids'] as $old_attachment_id ) {
352 $key = (string) $old_attachment_id;
353 if ( isset( $job['new_attachments'][ $key ] ) ) {
354 $new_attachment_ids[] = $job['new_attachments'][ $key ];
355 }
356 }
357 }
358 $gallery_data['meta_input'][ FOOGALLERY_META_ATTACHMENTS ] = $new_attachment_ids;
359 } else {
360 if ( isset( $gallery['datasource_value'] ) ) {
361 $gallery_data['meta_input'][ FOOGALLERY_META_DATASOURCE_VALUE ] = $gallery['datasource_value'];
362 }
363 }
364
365 $new_gallery_id = wp_insert_post( $gallery_data, true );
366 if ( is_wp_error( $new_gallery_id ) ) {
367 return array(
368 'complete' => false,
369 /* translators: 1: Gallery name, 2: Error message. */
370 'message' => sprintf( __( 'Error creating gallery "%1$s": %2$s', 'foogallery' ), $name, $new_gallery_id->get_error_message() ),
371 );
372 }
373
374 return array(
375 'complete' => false,
376 /* translators: 1: Gallery name, 2: Gallery ID. */
377 'message' => sprintf( __( 'Created gallery "%1$s" (ID: %2$s)', 'foogallery' ), $name, $new_gallery_id ),
378 );
379 }
380
381 private function format_import_job_progress( $job_id, array $job ) {
382 $done = isset( $job['done'] ) ? intval( $job['done'] ) : 0;
383 $total = isset( $job['total'] ) ? intval( $job['total'] ) : 0;
384 $stage = isset( $job['stage'] ) ? $job['stage'] : 'attachments';
385
386 $percent = 0;
387 if ( $total > 0 ) {
388 $percent = (int) floor( ( $done / $total ) * 100 );
389 if ( $percent > 100 ) {
390 $percent = 100;
391 }
392 }
393
394 return array(
395 'job_id' => $job_id,
396 'stage' => $stage,
397 'done' => $done,
398 'total' => $total,
399 'percent' => $percent,
400 'complete' => 'done' === $stage,
401 );
402 }
403
404 private function get_last_import_job_id() {
405 $user_id = get_current_user_id();
406 if ( $user_id <= 0 ) {
407 return '';
408 }
409 $job_id = get_user_meta( $user_id, self::IMPORT_JOB_USER_META_KEY, true );
410 return is_string( $job_id ) ? $job_id : '';
411 }
412
413 private function set_last_import_job_id( $job_id ) {
414 $user_id = get_current_user_id();
415 if ( $user_id <= 0 ) {
416 return;
417 }
418 update_user_meta( $user_id, self::IMPORT_JOB_USER_META_KEY, $job_id );
419 }
420
421 private function clear_last_import_job_id( $job_id_to_clear = '' ) {
422 $user_id = get_current_user_id();
423 if ( $user_id <= 0 ) {
424 return;
425 }
426
427 if ( empty( $job_id_to_clear ) ) {
428 delete_user_meta( $user_id, self::IMPORT_JOB_USER_META_KEY );
429 return;
430 }
431
432 $current = $this->get_last_import_job_id();
433 if ( $current === $job_id_to_clear ) {
434 delete_user_meta( $user_id, self::IMPORT_JOB_USER_META_KEY );
435 }
436 }
437
438 private function get_import_job_transient_key( $job_id ) {
439 return 'foogallery_import_job_' . $job_id;
440 }
441
442 private function load_import_job( $job_id ) {
443 $job = get_transient( $this->get_import_job_transient_key( $job_id ) );
444 if ( ! is_array( $job ) ) {
445 return false;
446 }
447 return $job;
448 }
449
450 private function save_import_job( $job_id, array $job ) {
451 set_transient( $this->get_import_job_transient_key( $job_id ), $job, self::IMPORT_JOB_TTL );
452 }
453
454 private function delete_import_job( $job_id ) {
455 delete_transient( $this->get_import_job_transient_key( $job_id ) );
456 }
457
458 public function find_attachment( $url ) {
459 $imported_attachment_id = attachment_url_to_postid( $url );
460 if ( $imported_attachment_id === 0 ) {
461 $found_attachments = get_posts( array(
462 'post_type' => 'attachment',
463 'meta_key' => '_foogallery_imported_from',
464 'meta_value' => $url,
465 ) );
466
467 if ( is_array( $found_attachments ) && count( $found_attachments ) > 0 ) {
468 $imported_attachment_id = $found_attachments[0]->ID;
469 }
470 }
471
472 return $imported_attachment_id;
473 }
474
475 /**
476 * Generate the export data
477 */
478 public function ajax_generate_export() {
479 if ( check_admin_referer( 'foogallery_gallery_export' ) ) {
480 if ( ! current_user_can( 'manage_options' ) ) {
481 wp_send_json_error( array(
482 'message' => __( 'You do not have permission to export galleries.', 'foogallery' ),
483 ), 403 );
484 }
485
486 if ( isset( $_POST['galleries'] ) ) {
487 $galleries = array_map( 'sanitize_text_field', wp_unslash( $_POST['galleries'] ) );
488 echo foogallery_generate_export_json( $galleries ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- JSON output
489 }
490 }
491 wp_die( '', '', array( 'response' => null ) );
492 }
493
494 /**
495 * Registers the test menu and page
496 */
497 public function add_import_export_menu() {
498 foogallery_add_submenu_page(
499 __( 'Import / Export', 'foogallery' ),
500 'manage_options',
501 'foogallery_import_export',
502 array( $this, 'render_import_export_page' )
503 );
504 }
505
506 /**
507 * Renders Import / Export page
508 */
509 public function render_import_export_page() {
510 require_once 'class-foogallery-export-view-helper.php';
511 require_once 'class-foogallery-import-view-helper.php';
512 require_once 'view-import-export.php';
513 }
514
515 /**
516 * Add a metabox to the gallery for exporting
517 *
518 * @param WP_Post $post the post we are dealing with.
519 */
520 public function add_export_metabox( $post ) {
521 add_meta_box(
522 'foogallery_export',
523 __( 'Export', 'foogallery' ),
524 array( $this, 'render_export_metabox' ),
525 FOOGALLERY_CPT_GALLERY,
526 'normal',
527 'low'
528 );
529 }
530
531 /**
532 * Render the export metabox on the gallery edit page
533 *
534 * @param WP_Post $post the post we are dealing with.
535 */
536 public function render_export_metabox( $post ) {
537 $export = foogallery_generate_export_json( $post->ID );
538 ?>
539 <style>
540 .foogallery_metabox_export {
541 width: 100%;
542 height: 50em;
543 }
544 </style>
545 <p>
546 <?php echo esc_html( __( 'Below is a JSON export of your gallery.', 'foogallery' ) ); ?>
547 </p>
548 <table id="table_styling" class="form-table">
549 <tbody>
550 <tr>
551 <td>
552 <textarea class="foogallery_metabox_export" type="text"><?php echo esc_html( $export ); ?></textarea>
553 </td>
554 </tr>
555 </tbody>
556 </table>
557 <?php
558 }
559 }
560 }
561