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 / galleries.php

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

407 lines 10.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * Gallery List Table.
5 *
6 * @link https://plugins360.com
7 * @since 2.8.0
8 *
9 * @package Automatic_YouTube_Gallery
10 */
11
12 // Exit if accessed directly
13 if ( ! defined( 'WPINC' ) ) {
14 die;
15 }
16
17 if ( ! class_exists( 'WP_List_Table' ) ) {
18 require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php';
19 }
20
21 /**
22 * Renders the saved-gallery list table on the Dashboard page.
23 *
24 * @since 2.8.0
25 */
26 class AYG_Admin_Gallery_List_Table extends WP_List_Table {
27
28 /**
29 * Set up the list table arguments.
30 *
31 * @since 2.8.0
32 */
33 public function __construct() {
34 parent::__construct( array(
35 'singular' => 'ayg-gallery',
36 'plural' => 'ayg-galleries',
37 'ajax' => false
38 ) );
39 }
40
41 /**
42 * Define the table columns.
43 *
44 * @since 2.8.0
45 * @return array Column ID => label pairs.
46 */
47 public function get_columns() {
48 return array(
49 'cb' => '<input type="checkbox">',
50 'title' => __( 'Title', 'automatic-youtube-gallery' ),
51 'source' => __( 'Source', 'automatic-youtube-gallery' ),
52 'videos' => __( 'Videos', 'automatic-youtube-gallery' ),
53 'status' => __( 'Status', 'automatic-youtube-gallery' ),
54 'schedule' => __( 'Next Import', 'automatic-youtube-gallery' ),
55 'shortcode' => __( 'Shortcode', 'automatic-youtube-gallery' )
56 );
57 }
58
59 /**
60 * Define which columns are sortable and which database column they map to.
61 *
62 * @since 2.8.0
63 * @return array Column ID => [ db_column, is_default_sort ] pairs.
64 */
65 protected function get_sortable_columns() {
66 return array(
67 'title' => array( 'title', false ),
68 'source' => array( 'source_type', false ),
69 'videos' => array( 'video_count', false )
70 );
71 }
72
73 /**
74 * Register available bulk actions.
75 *
76 * @since 2.8.0
77 * @return array Action key => label pairs.
78 */
79 protected function get_bulk_actions() {
80 return array(
81 'delete' => __( 'Delete', 'automatic-youtube-gallery' )
82 );
83 }
84
85 /**
86 * Suppress the top and bottom navigation bars when there are no items.
87 *
88 * @since 2.8.0
89 * @param string $which 'top' or 'bottom'.
90 */
91 protected function display_tablenav( $which ) {
92 if ( empty( $this->items ) ) {
93 return;
94 }
95
96 parent::display_tablenav( $which );
97 }
98
99 /**
100 * Output a message when the table is empty.
101 *
102 * @since 2.8.0
103 */
104 public function no_items() {
105 esc_html_e( 'No galleries found.', 'automatic-youtube-gallery' );
106 }
107
108 /**
109 * Render the checkbox column used for bulk selection.
110 *
111 * @since 2.8.0
112 * @param object $item Gallery row object.
113 * @return string
114 */
115 protected function column_cb( $item ) {
116 return sprintf( '<input type="checkbox" name="gallery_ids[]" value="%d">', absint( $item->id ) );
117 }
118
119 /**
120 * Render the title column with an edit link and row actions (Edit, Delete).
121 *
122 * @since 2.8.0
123 * @param object $item Gallery row object.
124 * @return string
125 */
126 protected function column_title( $item ) {
127 $base_url = admin_url( 'admin.php?page=automatic-youtube-gallery' );
128 $edit_url = add_query_arg( array( 'action' => 'edit', 'id' => $item->id ), $base_url );
129
130 $title = sprintf(
131 '<a href="%s"><strong>%s</strong></a>',
132 esc_url( $edit_url ),
133 esc_html( $item->title )
134 );
135
136 $actions = array(
137 'edit' => sprintf(
138 '<a href="%s">%s</a>',
139 esc_url( $edit_url ),
140 __( 'Edit', 'automatic-youtube-gallery' )
141 ),
142 );
143
144 $actions['delete'] = sprintf(
145 '<a href="#" class="ayg-button-delete-gallery" data-id="%d">%s</a>',
146 absint( $item->id ),
147 __( 'Delete', 'automatic-youtube-gallery' )
148 );
149
150 return $title . $this->row_actions( $actions );
151 }
152
153 /**
154 * Render the Source column showing the source type label.
155 *
156 * @since 2.8.0
157 * @param object $item Gallery row object.
158 * @return string
159 */
160 protected function column_source( $item ) {
161 $type = ! empty( $item->source_type ) ? $item->source_type : 'playlist';
162 $label = ayg_get_source_type_label( $type );
163
164 return esc_html( $label );
165 }
166
167 /**
168 * Render the Videos column showing the denormalised video count.
169 *
170 * @since 2.8.0
171 * @param object $item Gallery row object.
172 * @return string
173 */
174 protected function column_videos( $item ) {
175 // Live sources (search / livestream / single video) store nothing — they render on the fly —
176 // so a "0" count would be misleading. Show a dash instead.
177 if ( in_array( $item->source_type, array( 'search', 'livestream', 'video' ), true ) ) {
178 return '&mdash;';
179 }
180
181 return number_format_i18n( absint( $item->video_count ) );
182 }
183
184 /**
185 * Render the Status column as a badge.
186 *
187 * @since 2.8.0
188 * @param object $item Gallery row object.
189 * @return string
190 */
191 protected function column_status( $item ) {
192 // Live sources (search / livestream / single video) never import — they query the API at
193 // display time — so the import-status labels ("Pending Import", etc.) don't apply. Show "Live".
194 if ( in_array( $item->source_type, array( 'search', 'livestream', 'video' ), true ) ) {
195 return '<span class="ayg-badge ayg-badge-live">' . esc_html__( 'Live Source', 'automatic-youtube-gallery' ) . '</span>';
196 }
197
198 $status = ! empty( $item->import_status ) ? $item->import_status : 'idle';
199 $params = json_decode( (string) $item->params, true );
200 $label = ayg_get_import_status_label( $status, $item->video_count, ! empty( $params['page_token'] ) );
201
202 return sprintf(
203 '<span class="ayg-badge ayg-badge-%s">%s</span>',
204 esc_attr( $status ),
205 esc_html( $label )
206 );
207 }
208
209 /**
210 * Render the Schedule column: The next scheduled import date.
211 *
212 * @since 2.8.0
213 * @param object $item Gallery row object.
214 * @return string
215 */
216 protected function column_schedule( $item ) {
217 if ( empty( $item->next_import_at ) ) {
218 return '&mdash;';
219 }
220
221 static $date_format = null;
222 if ( null === $date_format ) {
223 $date_format = get_option( 'date_format' ) . ' ' . get_option( 'time_format' );
224 }
225
226 return esc_html( wp_date( $date_format, strtotime( $item->next_import_at ) ) );
227 }
228
229 /**
230 * Render the Shortcode column as a one-click copy chip.
231 *
232 * @since 2.8.0
233 * @param object $item Gallery row object.
234 * @return string
235 */
236 protected function column_shortcode( $item ) {
237 $shortcode = sprintf( '[automatic_youtube_gallery id="%d"]', absint( $item->id ) );
238
239 return sprintf(
240 '<button type="button" class="ayg-button ayg-button-copy-shortcode button button-small" title="%1$s" data-clipboard-text="%1$s">%2$s</button>',
241 esc_attr( $shortcode ),
242 esc_html__( 'Copy Shortcode', 'automatic-youtube-gallery' )
243 );
244 }
245
246 /**
247 * Fallback renderer for any column that does not have a dedicated method.
248 *
249 * @since 2.8.0
250 * @param object $item Gallery row object.
251 * @param string $column_name Column identifier.
252 * @return string
253 */
254 public function column_default( $item, $column_name ) {
255 return '';
256 }
257
258 /**
259 * Query galleries from the database and populate $this->items with the current page.
260 *
261 * @since 2.8.0
262 */
263 public function prepare_items() {
264 $this->_column_headers = array(
265 $this->get_columns(),
266 array(),
267 $this->get_sortable_columns(),
268 );
269
270 global $wpdb;
271 $table = $wpdb->prefix . 'ayg_galleries';
272
273 /**
274 * Filters the number of galleries listed per page in the admin list table.
275 *
276 * @since 2.8.0
277 * @param int $per_page Number of galleries per page. Default 20.
278 */
279 $per_page = (int) apply_filters( 'ayg_galleries_per_page', 20 );
280 if ( $per_page < 1 ) {
281 $per_page = 20;
282 }
283 $curr_page = $this->get_pagenum();
284 $offset = ( $curr_page - 1 ) * $per_page;
285
286 $search = isset( $_REQUEST['s'] ) ? sanitize_text_field( $_REQUEST['s'] ) : '';
287
288 $allowed_orderby = array( 'id', 'title', 'source_type', 'video_count', 'last_imported_at' );
289 $orderby = ( isset( $_REQUEST['orderby'] ) && in_array( $_REQUEST['orderby'], $allowed_orderby, true ) ) ? $_REQUEST['orderby'] : 'id';
290 $order = ( isset( $_REQUEST['order'] ) && 'asc' === strtolower( $_REQUEST['order'] ) ) ? 'ASC' : 'DESC';
291
292 if ( ! empty( $search ) ) {
293 $like = '%' . $wpdb->esc_like( $search ) . '%';
294
295 $total_items = (int) $wpdb->get_var(
296 $wpdb->prepare( "SELECT COUNT(*) FROM `{$table}` WHERE `title` LIKE %s", $like )
297 );
298
299 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
300 $this->items = $wpdb->get_results(
301 $wpdb->prepare(
302 "SELECT * FROM `{$table}` WHERE `title` LIKE %s ORDER BY `{$orderby}` {$order} LIMIT %d OFFSET %d",
303 $like,
304 $per_page,
305 $offset
306 )
307 );
308 } else {
309 $total_items = (int) $wpdb->get_var( "SELECT COUNT(*) FROM `{$table}`" );
310
311 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
312 $this->items = $wpdb->get_results(
313 $wpdb->prepare(
314 "SELECT * FROM `{$table}` ORDER BY `{$orderby}` {$order} LIMIT %d OFFSET %d",
315 $per_page,
316 $offset
317 )
318 );
319 }
320
321 $this->set_pagination_args( array(
322 'total_items' => $total_items,
323 'per_page' => $per_page,
324 'total_pages' => (int) ceil( $total_items / $per_page )
325 ) );
326 }
327
328 /**
329 * Process the bulk Delete action, then redirect back to the list with a count notice.
330 *
331 * @since 2.8.0
332 */
333 public function process_bulk_action() {
334 if ( 'delete' !== $this->current_action() ) {
335 return;
336 }
337
338 check_admin_referer( 'bulk-ayg-galleries' );
339
340 if ( ! current_user_can( 'manage_options' ) ) {
341 wp_die( -1 );
342 }
343
344 $ids = isset( $_REQUEST['gallery_ids'] ) ? array_map( 'absint', (array) $_REQUEST['gallery_ids'] ) : array();
345 if ( empty( $ids ) ) {
346 return;
347 }
348
349 foreach ( $ids as $id ) {
350 self::delete_gallery( $id );
351 }
352
353 $redirect_to = remove_query_arg(
354 array( 'action', 'action2', 'bulk_action', '_wpnonce', '_wp_http_referer', 'gallery_ids', 'deleted' )
355 );
356
357 wp_redirect( add_query_arg( 'deleted', count( $ids ), $redirect_to ) );
358 exit;
359 }
360
361 /**
362 * Delete a gallery, its pivot rows, and any orphaned videos.
363 *
364 * Orphaned videos are removed in 200-row chunks via LEFT JOIN to keep
365 * table locks short on large datasets.
366 *
367 * Called by process_bulk_action() and by AYG_Admin::ajax_callback_delete_gallery().
368 *
369 * @since 2.8.0
370 * @param int $id Gallery ID.
371 */
372 public static function delete_gallery( $id ) {
373 global $wpdb;
374
375 $id = absint( $id );
376 if ( ! $id ) {
377 return;
378 }
379
380 $videos_table = $wpdb->prefix . 'ayg_videos';
381 $rel_table = $wpdb->prefix . 'ayg_gallery_relationships';
382 $gal_table = $wpdb->prefix . 'ayg_galleries';
383
384 $wpdb->delete( $rel_table, array( 'gallery_id' => strval( $id ) ), array( '%s' ) );
385 $wpdb->delete( $gal_table, array( 'id' => $id ), array( '%d' ) );
386
387 do {
388 // Multi-table DELETE with LIMIT is not valid in MySQL/MariaDB.
389 // Use a derived subquery so the outer DELETE targets a single table by id.
390 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
391 $deleted = $wpdb->query(
392 "DELETE FROM `{$videos_table}`
393 WHERE `id` IN (
394 SELECT `id` FROM (
395 SELECT v.`id`
396 FROM `{$videos_table}` v
397 LEFT JOIN `{$rel_table}` r ON v.video_id = r.video_id
398 WHERE r.video_id IS NULL
399 LIMIT 200
400 ) AS orphans
401 )"
402 );
403 } while ( $deleted > 0 );
404 }
405
406 }
407