PluginProbe
Automatic YouTube Gallery – Embed Auto-Updating YouTube Video Galleries, Feeds, Playlists & Channels / trunk
Automatic YouTube Gallery – Embed Auto-Updating YouTube Video Galleries, Feeds, Playlists & Channels vtrunk
2.9.1 2.9.0 trunk 1.0.0 1.1.0 1.2.0 1.3.0 1.4.0 1.5.0 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 2.0.0 2.1.0 2.2.0 2.3.2 2.3.3 2.3.5 2.3.6 2.3.8 2.3.9 2.4.3 All 37 releases
automatic-youtube-gallery / admin / admin.php

admin.php in Automatic YouTube Gallery – Embed Auto-Updating YouTube Video Galleries, Feeds, Playlists & Channels trunk, at admin/admin.php

623 lines 22.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * The admin-specific functionality of the plugin.
5 *
6 * @link https://plugins360.com
7 * @since 1.0.0
8 *
9 * @package Automatic_YouTube_Gallery
10 */
11
12 // Exit if accessed directly
13 if ( ! defined( 'WPINC' ) ) {
14 die;
15 }
16
17 /**
18 * AYG_Admin class.
19 *
20 * @since 1.0.0
21 */
22 class AYG_Admin {
23
24 /**
25 * Gallery list table instance, shared between bulk-action processing
26 * and rendering, to avoid instantiating it twice per request.
27 *
28 * @since 2.8.0
29 * @var AYG_Admin_Gallery_List_Table
30 */
31 private $gallery_list_table;
32
33 /**
34 * Insert missing plugin options.
35 *
36 * @since 1.6.4
37 */
38 public function insert_missing_options() {
39 if ( AYG_VERSION !== get_option( 'ayg_version' ) ) {
40 $defaults = ayg_get_default_settings();
41
42 // Insert the gallery settings
43 $gallery_settings = get_option( 'ayg_gallery_settings' );
44
45 if ( ! is_array( $gallery_settings ) || empty( $gallery_settings ) ) {
46 $gallery_settings = $defaults['ayg_gallery_settings'];
47 update_option( 'ayg_gallery_settings', $gallery_settings );
48 }
49
50 // Insert the strings settings
51 if ( false == get_option( 'ayg_strings_settings' ) ) {
52 $strings_settings = array(
53 'more_button_label' => ! empty( $gallery_settings['more_button_label'] ) ? $gallery_settings['more_button_label'] : $defaults['ayg_strings_settings']['more_button_label'],
54 'previous_button_label' => ! empty( $gallery_settings['previous_button_label'] ) ? $gallery_settings['previous_button_label'] : $defaults['ayg_strings_settings']['previous_button_label'],
55 'next_button_label' => ! empty( $gallery_settings['next_button_label'] ) ? $gallery_settings['next_button_label'] : $defaults['ayg_strings_settings']['next_button_label'],
56 'show_more_label' => $defaults['ayg_strings_settings']['show_more_label'],
57 'show_less_label' => $defaults['ayg_strings_settings']['show_less_label'],
58 );
59
60 add_option( 'ayg_strings_settings', $strings_settings );
61 }
62
63 // Update the player settings
64 $player_settings = get_option( 'ayg_player_settings' );
65
66 if ( ! is_array( $player_settings ) || empty( $player_settings ) ) {
67 $player_settings = $defaults['ayg_player_settings'];
68 update_option( 'ayg_player_settings', $player_settings );
69 }
70
71 if ( ! array_key_exists( 'player_type', $player_settings ) ) {
72 $player_settings['player_type'] = $defaults['ayg_player_settings']['player_type'];
73 $player_settings['player_color'] = $defaults['ayg_player_settings']['player_color'];
74
75 update_option( 'ayg_player_settings', $player_settings );
76 }
77
78 // Insert the livestream settings
79 if ( false == get_option( 'ayg_livestream_settings' ) ) {
80 add_option( 'ayg_livestream_settings', $defaults['ayg_livestream_settings'] );
81 }
82
83 // Insert the privacy settings
84 if ( false == get_option( 'ayg_privacy_settings' ) ) {
85 add_option( 'ayg_privacy_settings', $defaults['ayg_privacy_settings'] );
86 }
87
88 // Create custom database tables
89 ayg_db_create_custom_tables();
90
91 // Delete the plugin cache
92 ayg_delete_cache();
93
94 // Update the plugin version
95 update_option( 'ayg_version', AYG_VERSION );
96 }
97 }
98
99 /**
100 * Enqueue styles for the admin area.
101 *
102 * @since 1.0.0
103 */
104 public function enqueue_styles() {
105 wp_enqueue_style( 'wp-color-picker' );
106
107 wp_enqueue_style(
108 AYG_SLUG . '-admin',
109 AYG_URL . 'admin/assets/css/admin.min.css',
110 array(),
111 AYG_VERSION,
112 'all'
113 );
114 }
115
116 /**
117 * Enqueue scripts for the admin area.
118 *
119 * @since 1.0.0
120 */
121 public function enqueue_scripts() {
122 wp_enqueue_media();
123 wp_enqueue_script( 'wp-color-picker' );
124
125 wp_enqueue_script(
126 AYG_SLUG . '-admin',
127 AYG_URL . 'admin/assets/js/admin.min.js',
128 array( 'jquery' ),
129 AYG_VERSION,
130 false
131 );
132
133 wp_localize_script(
134 AYG_SLUG . '-admin',
135 'ayg_admin',
136 array(
137 'ajax_nonce' => wp_create_nonce( 'ayg_ajax_nonce' ),
138 'admin_url' => admin_url( 'admin.php' ),
139 'live_types' => array( 'search', 'livestream', 'video' ), // Rendered live — source stays editable (mirrors gallery-form.php).
140 'i18n' => array(
141 // API key
142 'invalid_api_key' => __( 'Please enter your YouTube API key.', 'automatic-youtube-gallery' ),
143
144 // Settings
145 'cache_cleared' => __( 'Cleared', 'automatic-youtube-gallery' ),
146
147 // Common
148 'save_failed' => __( 'Save failed. Please try again.', 'automatic-youtube-gallery' ),
149 'delete_failed' => __( 'Delete failed. Please try again.', 'automatic-youtube-gallery' ),
150
151 // Gallery: Shortcode
152 'shortcode_copied' => __( 'Shortcode copied! Paste it into any post, page, or widget to display your gallery.', 'automatic-youtube-gallery' ),
153
154 // Gallery: Delete confirmations
155 'confirm_delete' => __( 'Are you sure you want to delete this gallery? This action cannot be undone.', 'automatic-youtube-gallery' ),
156 'confirm_bulk_delete' => __( 'Are you sure you want to delete the selected galleries? This action cannot be undone.', 'automatic-youtube-gallery' ),
157
158 // Gallery Form
159 'unsaved_changes' => __( 'You have unsaved changes. Are you sure you want to leave?', 'automatic-youtube-gallery' ),
160 'update_gallery' => __( 'Update Gallery', 'automatic-youtube-gallery' ),
161
162 // Gallery Form: Source validation
163 'source_required' => array(
164 'channel' => __( 'A YouTube channel ID (or) a video URL from the channel is required.', 'automatic-youtube-gallery' ),
165 'playlist' => __( 'A YouTube playlist ID (or) URL is required.', 'automatic-youtube-gallery' ),
166 'username' => __( 'A YouTube account username is required.', 'automatic-youtube-gallery' ),
167 'search' => __( 'A search keyword is required.', 'automatic-youtube-gallery' ),
168 'livestream' => __( 'A YouTube channel ID (or) a video URL from the channel is required.', 'automatic-youtube-gallery' ),
169 'video' => __( 'A YouTube video ID (or) URL is required.', 'automatic-youtube-gallery' ),
170 'videos' => __( 'At least one YouTube video ID (or) URL is required.', 'automatic-youtube-gallery' )
171 ),
172 'channel_handle' => __( 'YouTube @handle URLs aren’t supported here. Please enter a channel ID, a /channel/ URL, or a video URL from the channel.', 'automatic-youtube-gallery' ),
173
174 // Source hint text, swapped live as the source-type dropdown changes. Must match
175 // the strings rendered in gallery-form.php's .ayg-source-hint (live / importable).
176 'source_live' => __( 'This is a live source — you can change it anytime. Updates take effect immediately the next time the gallery is viewed.', 'automatic-youtube-gallery' ),
177 'source_importable' => __( 'The video source can’t be changed once videos are imported. So please make sure you select the right source before saving.', 'automatic-youtube-gallery' ),
178
179 // Gallery Form: Import progress
180 'processing' => __( 'Processing', 'automatic-youtube-gallery' ),
181 'import_progress' => __( 'Imported: %imported%, Updated: %updated%. Please do not close this window until you see a success or error message', 'automatic-youtube-gallery' ),
182 'import_complete' => __( 'Done! Imported: %imported%, Updated: %updated%, Deleted: %deleted%. Refreshing the page', 'automatic-youtube-gallery' ),
183 'import_failed' => __( 'Import failed. Please try again.', 'automatic-youtube-gallery' ),
184 'quota_exceeded' => __( 'YouTube API quota exceeded. The import has been paused — click "Update Gallery" to resume after the quota resets.', 'automatic-youtube-gallery' )
185 )
186 )
187 );
188 }
189
190 /**
191 * Add dashboard page link on the plugins menu.
192 *
193 * @since 1.0.0
194 * @param array $links An array of plugin action links.
195 * @return string $links Array of filtered plugin action links.
196 */
197 public function plugin_action_links( $links ) {
198 $dashboard_link = sprintf(
199 '<a href="%s">%s</a>',
200 admin_url( 'admin.php?page=automatic-youtube-gallery' ),
201 __( 'Build Gallery', 'automatic-youtube-gallery' )
202 );
203
204 array_unshift( $links, $dashboard_link );
205
206 return $links;
207 }
208
209 /**
210 * Add "Dashboard" menu.
211 *
212 * @since 1.3.0
213 */
214 public function admin_menu() {
215 $hook = add_menu_page(
216 __( 'Automatic YouTube Gallery', 'automatic-youtube-gallery' ),
217 __( 'YouTube Gallery', 'automatic-youtube-gallery' ),
218 'manage_options',
219 'automatic-youtube-gallery',
220 array( $this, 'display_dashboard_content' ),
221 'dashicons-format-video',
222 10
223 );
224
225 // Process list table bulk actions before any HTML is output to allow wp_redirect().
226 add_action( 'load-' . $hook, array( $this, 'process_list_table_bulk_actions' ) );
227
228 add_submenu_page(
229 'automatic-youtube-gallery',
230 __( 'Dashboard', 'automatic-youtube-gallery' ),
231 __( 'Dashboard', 'automatic-youtube-gallery' ),
232 'manage_options',
233 'automatic-youtube-gallery',
234 array( $this, 'display_dashboard_content' )
235 );
236 }
237
238 /**
239 * Runs before the dashboard page HTML is output: hides the Screen Options tab
240 * and processes list-table bulk actions on the list view.
241 *
242 * @since 2.8.0
243 */
244 public function process_list_table_bulk_actions() {
245 $general_settings = ayg_get_option( 'ayg_general_settings' );
246
247 // Hide the Screen Options tab entirely. The list table would otherwise
248 // auto-populate a "Columns" section there, which we don't want on our
249 // custom dashboard header.
250 add_filter( 'screen_options_show_screen', '__return_false' );
251
252 // No list table is rendered on the setup screen or the gallery form view.
253 $action = isset( $_GET['action'] ) ? sanitize_text_field( $_GET['action'] ) : '';
254 if ( empty( $general_settings['api_key'] ) || 'new' === $action || 'edit' === $action ) {
255 return;
256 }
257
258 // Strip the GET search form's leftover bulk-action fields from the URL.
259 add_filter( 'removable_query_args', array( $this, 'add_removable_query_args' ) );
260
261 // Process bulk actions
262 require_once AYG_DIR . 'admin/galleries.php';
263
264 $this->gallery_list_table = new AYG_Admin_Gallery_List_Table();
265 $this->gallery_list_table->process_bulk_action();
266 }
267
268 /**
269 * Add the gallery list table's bulk-action fields to the list of query args
270 * WordPress strips from the URL via wp_admin_canonical_url().
271 *
272 * @since 2.8.0
273 * @param array $args Removable query argument names.
274 * @return array
275 */
276 public function add_removable_query_args( $args ) {
277 return array_merge( $args, array( 'action', 'action2', 'bulk_action', '_wpnonce', '_wp_http_referer' ) );
278 }
279
280 /**
281 * Display dashboard content.
282 *
283 * @since 1.3.0
284 */
285 public function display_dashboard_content() {
286 require_once AYG_DIR . 'admin/templates/dashboard.php';
287 }
288
289 /**
290 * Prints admin screen notices.
291 *
292 * @since 2.0.0
293 */
294 public function admin_notices() {
295 $screen = get_current_screen();
296 $on_page = $screen && 'toplevel_page_automatic-youtube-gallery' === $screen->id;
297
298 // Show on all admin pages except our own gallery form (action=new|edit).
299 if ( $on_page ) {
300 $action = isset( $_GET['action'] ) ? sanitize_text_field( $_GET['action'] ) : '';
301 if ( 'new' === $action || 'edit' === $action ) {
302 return;
303 }
304 }
305
306 $general_settings = ayg_get_option( 'ayg_general_settings' );
307
308 if ( ! empty( $general_settings['development_mode'] ) ) {
309 // WordPress relocates non-inline notices on our custom dashboard header, hiding them.
310 // Add the "inline" class only there so the notice stays put; leave it off elsewhere.
311 $classes = 'notice notice-info' . ( $on_page ? ' inline' : '' );
312 ?>
313 <div class="<?php echo esc_attr( $classes ); ?>">
314 <p>
315 <?php
316 printf(
317 __( '<strong>Automatic YouTube Gallery:</strong> You have <a href="%s">development mode</a> enabled. We do not cache API results in this mode. While this is ok when you are testing the plugin, we strongly recommend disabling this option when your site goes live.', 'automatic-youtube-gallery' ),
318 esc_url( admin_url( 'admin.php?page=automatic-youtube-gallery-settings' ) )
319 );
320 ?>
321 </p>
322 </div>
323 <?php
324 }
325 }
326
327 /**
328 * Save API Key.
329 *
330 * @since 1.3.0
331 */
332 public function ajax_callback_save_api_key() {
333 check_ajax_referer( 'ayg_ajax_nonce', 'security' );
334
335 if ( ! current_user_can( 'manage_options' ) ) {
336 wp_send_json_error( array( 'message' => __( 'Permission denied.', 'automatic-youtube-gallery' ) ) );
337 }
338
339 $general_settings = ayg_get_option( 'ayg_general_settings' );
340
341 $general_settings['api_key'] = sanitize_text_field( $_POST['api_key'] );
342 update_option( 'ayg_general_settings', $general_settings );
343
344 wp_send_json_success();
345 }
346
347 /**
348 * Save a gallery (insert or update).
349 *
350 * @since 2.8.0
351 */
352 public function ajax_callback_save_gallery() {
353 check_ajax_referer( 'ayg_ajax_nonce', 'security' );
354
355 if ( ! current_user_can( 'manage_options' ) ) {
356 wp_send_json_error( array( 'message' => __( 'Permission denied.', 'automatic-youtube-gallery' ) ) );
357 }
358
359 $gallery_id = absint( isset( $_POST['gallery_id'] ) ? $_POST['gallery_id'] : 0 );
360 $gallery_title = sanitize_text_field( isset( $_POST['title'] ) ? $_POST['title'] : '' );
361
362 $params = array();
363 $source_locked = false;
364 $source_type = '';
365 $source_value = '';
366
367 if ( $gallery_id > 0 ) {
368 $existing = ayg_get_gallery( $gallery_id );
369
370 if ( ! $existing ) {
371 wp_send_json_error( array( 'message' => __( 'Gallery not found.', 'automatic-youtube-gallery' ) ) );
372 }
373
374 // Seed params from the saved row so values from disabled inputs and runtime
375 // keys (page_token, last_video_published_at) survive the update.
376 $saved_params = json_decode( (string) $existing->params, true );
377 if ( is_array( $saved_params ) ) {
378 $params = $saved_params;
379 }
380
381 // The source locks only once videos have been imported. While the gallery
382 // still has no videos (e.g. the first import failed on a bad source), the
383 // source stays editable so it can be corrected in place. Live sources
384 // (search / livestream / single video) never import, so they never lock.
385 $source_locked = ( (int) $existing->video_count > 0 && ! in_array( $existing->source_type, array( 'search', 'livestream', 'video' ), true ) );
386
387 if ( $source_locked ) {
388 $source_type = $existing->source_type;
389 $source_value = $existing->source_value;
390 }
391 }
392
393 // Read the source from POST for a new gallery, or for an update before the first
394 // import. A re-edited source starts a clean import, so drop the runtime keys.
395 if ( ! $source_locked ) {
396 unset( $params['page_token'], $params['last_video_published_at'] );
397
398 $allowed_source_types = ayg_get_source_types();
399 $source_type = sanitize_key( isset( $_POST['type'] ) ? $_POST['type'] : 'playlist' );
400 if ( ! array_key_exists( $source_type, $allowed_source_types ) ) {
401 $source_type = 'playlist';
402 }
403
404 switch ( $source_type ) {
405 case 'channel':
406 case 'livestream':
407 $source_value = sanitize_text_field( isset( $_POST['channel'] ) ? $_POST['channel'] : '' );
408 break;
409
410 case 'playlist':
411 $source_value = sanitize_text_field( isset( $_POST['playlist'] ) ? $_POST['playlist'] : '' );
412 break;
413
414 case 'username':
415 $source_value = sanitize_text_field( isset( $_POST['username'] ) ? $_POST['username'] : '' );
416 break;
417
418 case 'search':
419 $source_value = sanitize_text_field( isset( $_POST['search'] ) ? $_POST['search'] : '' );
420 break;
421
422 case 'video':
423 $source_value = sanitize_text_field( isset( $_POST['video'] ) ? $_POST['video'] : '' );
424 break;
425
426 case 'videos':
427 $source_value = sanitize_textarea_field( isset( $_POST['videos'] ) ? $_POST['videos'] : '' );
428 break;
429
430 default:
431 $source_value = '';
432 }
433 }
434
435 $exclude = sanitize_textarea_field( isset( $_POST['exclude'] ) ? $_POST['exclude'] : '' );
436 if ( ! empty( $exclude ) ) $exclude = array_values( array_filter( array_map( 'trim', explode( "\n", $exclude ) ) ) );
437 $params['exclude'] = $exclude;
438
439 // 'paused' is a non-numeric sentinel (stop automatic imports); every other value is seconds.
440 $schedule = sanitize_text_field( isset( $_POST['schedule'] ) ? $_POST['schedule'] : 86400 );
441 $params['schedule'] = ( 'paused' === $schedule ) ? 'paused' : absint( $schedule );
442
443 // Display-time sort (gallery-form-only fields). Validated against whitelists; get_videos_from_db()
444 // maps them into a safe ORDER BY. Defaults reproduce the prior hardcoded "newest first".
445 $sort_by = sanitize_key( isset( $_POST['sort_by'] ) ? $_POST['sort_by'] : '' );
446 $params['sort_by'] = in_array( $sort_by, array( 'date', 'title', 'duration', 'random' ), true ) ? $sort_by : 'date';
447
448 $sort_order = sanitize_key( isset( $_POST['sort_order'] ) ? $_POST['sort_order'] : '' );
449 $params['sort_order'] = ( 'asc' === $sort_order ) ? 'asc' : 'desc';
450
451 // Display-time duration filter. get_videos_from_db() applies it only when a direction is set.
452 $duration_filter = sanitize_key( isset( $_POST['duration_filter'] ) ? $_POST['duration_filter'] : '' );
453 $params['duration_filter'] = in_array( $duration_filter, array( 'long', 'short' ), true ) ? $duration_filter : '';
454 $params['duration'] = absint( isset( $_POST['duration'] ) ? $_POST['duration'] : 0 );
455
456 // Fields stored in dedicated DB columns, not in params. 'cache' is kept (saved to params) so
457 // the live 'search' source type can control its API cache duration.
458 $fields = ayg_get_editor_fields();
459 $excluded = array( 'type', 'channel', 'playlist', 'username', 'search', 'video', 'videos' );
460
461 foreach ( $fields as $section ) {
462 foreach ( $section['fields'] as $field ) {
463 $name = $field['name'];
464
465 if ( in_array( $name, $excluded, true ) ) {
466 continue;
467 }
468
469 if ( 'checkbox' === $field['type'] ) {
470 $params[ $name ] = isset( $_POST[ $name ] ) ? 1 : 0;
471 } elseif ( ! isset( $_POST[ $name ] ) ) {
472 continue;
473 } elseif ( 'select' === $field['type'] && isset( $field['options'] ) ) {
474 $raw = sanitize_text_field( $_POST[ $name ] );
475 $params[ $name ] = array_key_exists( $raw, $field['options'] ) ? $raw : sanitize_text_field( $field['value'] );
476 } else {
477 $sanitize_callback = ! empty( $field['sanitize_callback'] ) ? $field['sanitize_callback'] : 'sanitize_text_field';
478 $params[ $name ] = call_user_func( $sanitize_callback, $_POST[ $name ] );
479 }
480 }
481 }
482
483 global $wpdb;
484 $table = $wpdb->prefix . 'ayg_galleries';
485
486 $now = current_time( 'mysql' );
487
488 $data = array(
489 'title' => $gallery_title,
490 'params' => wp_json_encode( $params ),
491 'updated_at' => $now
492 );
493
494 // Persist the source columns whenever they aren't locked (new gallery, or an
495 // update before the first import). All columns here are strings, so the format
496 // is left to default ('%s') instead of tracking a positional list.
497 if ( ! $source_locked ) {
498 $data['source_type'] = $source_type;
499 $data['source_value'] = $source_value;
500 }
501
502 // A "paused" schedule stops automatic imports: flag the gallery paused, clear any prior
503 // import error (so a user pause is distinguishable from a quota pause, which keeps its
504 // message), and drop it from the cron queue. The JS save flow skips the import for this case.
505 if ( 'paused' === $params['schedule'] ) {
506 $data['import_status'] = 'paused';
507 $data['import_error'] = '';
508 $data['next_import_at'] = null;
509 } elseif ( 0 === $params['schedule'] ) {
510 // "Only Once" is non-recurring: ensure no future cron run stays queued. Switching here from a
511 // recurring schedule may have left a stale next_import_at behind; clear it so a display-only
512 // save (import skipped) can't trigger one more unexpected background import. A save that does
513 // import will have import_batch() finalize the status / next run on completion anyway.
514 $data['next_import_at'] = null;
515 }
516
517 if ( $gallery_id > 0 ) {
518 if ( empty( $gallery_title ) ) {
519 $gallery_title = sprintf( __( 'Gallery %d', 'automatic-youtube-gallery' ), $gallery_id );
520 $data['title'] = $gallery_title;
521 }
522
523 // Drop any newly-excluded videos from this gallery's links (the videos table is left intact —
524 // rows may be shared by other galleries) and fold the refreshed count into this same update.
525 if ( ayg_delete_excluded_relationships( $gallery_id, $params['exclude'] ) > 0 ) {
526 $data['video_count'] = (int) $wpdb->get_var(
527 $wpdb->prepare( "SELECT COUNT(*) FROM {$wpdb->prefix}ayg_gallery_relationships WHERE gallery_id = %s", strval( $gallery_id ) )
528 );
529 }
530
531 $wpdb->update(
532 $table,
533 $data,
534 array( 'id' => $gallery_id ),
535 null,
536 array( '%d' )
537 );
538 } else {
539 $data['created_at'] = $now;
540
541 $wpdb->insert( $table, $data );
542
543 $gallery_id = (int) $wpdb->insert_id;
544
545 if ( empty( $gallery_title ) ) {
546 $gallery_title = sprintf( __( 'Gallery %d', 'automatic-youtube-gallery' ), $gallery_id );
547
548 $wpdb->update(
549 $table,
550 array( 'title' => $gallery_title ),
551 array( 'id' => $gallery_id ),
552 array( '%s' ),
553 array( '%d' )
554 );
555 }
556 }
557
558 // Live galleries (search / livestream / single video) render from the API and cache responses. Clear
559 // this gallery's cache on save so the front-end reflects the latest results right after "Update Gallery".
560 if ( in_array( $source_type, array( 'search', 'livestream', 'video' ), true ) ) {
561 ayg_delete_cache( strval( $gallery_id ) );
562 }
563
564 wp_send_json_success( array(
565 'gallery_id' => $gallery_id,
566 'shortcode' => sprintf( '[automatic_youtube_gallery id="%d"]', $gallery_id ),
567 ) );
568 }
569
570 /**
571 * Import one batch of videos into a gallery via AJAX.
572 *
573 * The client calls this in a loop, passing back the page_token returned by
574 * the previous call, until done = true.
575 *
576 * @since 2.8.0
577 */
578 public function ajax_callback_import_gallery() {
579 check_ajax_referer( 'ayg_ajax_nonce', 'security' );
580
581 if ( ! current_user_can( 'manage_options' ) ) {
582 wp_send_json_error( array( 'message' => __( 'Permission denied.', 'automatic-youtube-gallery' ) ) );
583 }
584
585 $gallery_id = absint( isset( $_POST['id'] ) ? $_POST['id'] : 0 );
586 $page_token = sanitize_text_field( isset( $_POST['page_token'] ) ? $_POST['page_token'] : '' );
587
588 // The browser-driven import is always a manual run: full re-scan, uncapped, prunes deletions.
589 $importer = new AYG_Import();
590 $result = $importer->import_batch( $gallery_id, $page_token, true );
591
592 if ( isset( $result['error'] ) ) {
593 wp_send_json_error( array(
594 'message' => $result['error'],
595 'quota_exceeded' => ! empty( $result['quota_exceeded'] )
596 ) );
597 }
598
599 wp_send_json_success( $result );
600 }
601
602 /**
603 * Delete a gallery via AJAX.
604 *
605 * @since 2.8.0
606 */
607 public function ajax_callback_delete_gallery() {
608 check_ajax_referer( 'ayg_ajax_nonce', 'security' );
609
610 if ( ! current_user_can( 'manage_options' ) ) {
611 wp_send_json_error( array( 'message' => __( 'Permission denied.', 'automatic-youtube-gallery' ) ) );
612 }
613
614 require_once AYG_DIR . 'admin/galleries.php';
615
616 $id = absint( isset( $_POST['id'] ) ? $_POST['id'] : 0 );
617 AYG_Admin_Gallery_List_Table::delete_gallery( $id );
618
619 wp_send_json_success();
620 }
621
622 }
623