| @@ -21,78 +21,22 @@ | ||
| 21 | 21 | * @param array $args An array of gallery options. |
| 22 | 22 | * @return mixed |
| 23 | 23 | */ |
| 24 | 24 | function ayg_build_gallery( $args ) { |
| 25 | - $general_settings = ayg_get_option( 'ayg_general_settings' ); | |
| 26 | - $strings_settings = ayg_get_option( 'ayg_strings_settings' ); | |
| 27 | - | |
| 28 | 25 | global $post; |
| 29 | 26 | |
| 30 | 27 | // Vars |
| 31 | 28 | $fields = ayg_get_editor_fields(); |
| 32 | - $excluded = array( 'popup' ); // Fields not part of attributes | |
| 29 | + $defaults = array(); | |
| 33 | 30 | |
| 34 | - // Defaults not backed by an editor field (kept through shortcode_atts): internal "db" source + sort. | |
| 35 | - $defaults = array( | |
| 36 | - 'db' => '', | |
| 37 | - 'sort_by' => 'date', | |
| 38 | - 'sort_order' => 'desc', | |
| 39 | - 'sort_seed' => '', | |
| 40 | - 'duration_filter' => '', | |
| 41 | - 'duration' => 0 | |
| 42 | - ); | |
| 43 | - | |
| 44 | 31 | foreach ( $fields as $key => $value ) { |
| 45 | 32 | foreach ( $value['fields'] as $field ) { |
| 46 | - if ( in_array( $field['name'], $excluded ) ) { | |
| 47 | - continue; | |
| 48 | - } | |
| 49 | - | |
| 50 | 33 | $defaults[ $field['name'] ] = $field['value']; |
| 51 | 34 | } |
| 52 | 35 | } |
| 53 | 36 | |
| 54 | - $defaults = array_merge( $defaults, (array) $strings_settings ); | |
| 37 | + $attributes = shortcode_atts( $defaults, $args ); | |
| 55 | 38 | |
| 56 | - // Gallery Builder mode: a numeric "id" loads the saved gallery config, layers its saved | |
| 57 | - // settings under $args (shortcode attributes still win, except the source), and switches | |
| 58 | - // the source to the internal "db" type so the existing build pipeline serves it from the DB. | |
| 59 | - if ( isset( $args['id'] ) && is_numeric( $args['id'] ) && (int) $args['id'] > 0 ) { | |
| 60 | - $config = ayg_get_gallery( (int) $args['id'] ); | |
| 61 | - | |
| 62 | - if ( ! $config ) { | |
| 63 | - return sprintf( '<div class="ayg ayg-error">%s</div>', esc_html__( 'Gallery not found.', 'automatic-youtube-gallery' ) ); | |
| 64 | - } | |
| 65 | - | |
| 66 | - $config_params = json_decode( (string) $config->params, true ); | |
| 67 | - if ( ! is_array( $config_params ) ) { | |
| 68 | - $config_params = array(); | |
| 69 | - } | |
| 70 | - | |
| 71 | - // Saved settings act as defaults; explicit shortcode attributes override them. | |
| 72 | - $args = array_merge( $config_params, $args ); | |
| 73 | - | |
| 74 | - // The source always comes from the config and can't be overridden by the shortcode. | |
| 75 | - // Livestream, search and single video query the live API at display time (nothing is | |
| 76 | - // imported/stored); everything else renders from the DB. ayg_get_gallery_source() owns that | |
| 77 | - // mapping so the public AJAX endpoint resolves the same source from the same saved row. | |
| 78 | - $source = ayg_get_gallery_source( $config ); | |
| 79 | - | |
| 80 | - $args['type'] = $source['type']; | |
| 81 | - $args[ $source['field'] ] = $source['src']; | |
| 82 | - | |
| 83 | - // Single video + popup mode → force the popup theme, mirroring the block and widget (which | |
| 84 | - // apply this before calling ayg_build_gallery()). | |
| 85 | - if ( 'video' === $config->source_type && ! empty( $args['popup'] ) ) { | |
| 86 | - $args['theme'] = 'popup'; | |
| 87 | - } | |
| 88 | - | |
| 89 | - // Gallery Builder relationships are keyed by the config ID as a string. | |
| 90 | - $args['uid'] = strval( $config->id ); | |
| 91 | - } | |
| 92 | - | |
| 93 | - $attributes = shortcode_atts( $defaults, $args, 'automatic_youtube_gallery' ); | |
| 94 | - | |
| 95 | 39 | $attributes['post_id'] = 0; |
| 96 | 40 | if ( isset( $post->ID ) ) { |
| 97 | 41 | $attributes['post_id'] = (int) $post->ID; |
| 98 | 42 | } |
| @@ -106,90 +50,31 @@ | ||
| 106 | 50 | if ( empty( $attributes['limit'] ) ) { |
| 107 | 51 | $attributes['limit'] = 500; |
| 108 | 52 | } |
| 109 | 53 | |
| 110 | - $source_type = sanitize_text_field( $attributes['type'] ); | |
| 111 | - | |
| 112 | - // Videos per page. Database (Gallery Builder) galleries read from our own table, so 0 means | |
| 113 | - // "show all videos on a single page" (no pagination). The live-API sources (search, livestream) | |
| 114 | - // stay capped at 50 because that is YouTube's maximum number of results per request. | |
| 115 | - if ( 'db' === $source_type ) { | |
| 116 | - $attributes['per_page'] = max( 0, (int) $attributes['per_page'] ); | |
| 117 | - } else { | |
| 118 | - $attributes['per_page'] = min( 50, (int) $attributes['per_page'] ); | |
| 119 | - if ( empty( $attributes['per_page'] ) ) { | |
| 120 | - $attributes['per_page'] = 50; | |
| 121 | - } | |
| 54 | + $attributes['per_page'] = min( 50, (int) $attributes['per_page'] ); | |
| 55 | + if ( empty( $attributes['per_page'] ) ) { | |
| 56 | + $attributes['per_page'] = 50; | |
| 122 | 57 | } |
| 123 | 58 | |
| 59 | + $source_type = sanitize_text_field( $attributes['type'] ); | |
| 124 | 60 | if ( 'livestream' == $source_type ) { |
| 125 | 61 | $attributes['livestream'] = $attributes['channel']; |
| 126 | 62 | } |
| 127 | 63 | |
| 128 | - $attributes['lazyload'] = 0; | |
| 129 | - if ( ! empty( $general_settings['lazyload'] ) ) { | |
| 130 | - $attributes['lazyload'] = 1; | |
| 131 | - } | |
| 132 | - | |
| 133 | - $source_url = sanitize_text_field( $attributes[ $source_type ] ); | |
| 134 | - | |
| 135 | - if ( isset( $args['id'] ) && ! empty( $args['id'] ) ) { | |
| 136 | - $attributes['id'] = absint( $args['id'] ); | |
| 137 | - } | |
| 138 | - | |
| 139 | 64 | if ( isset( $args['uid'] ) && ! empty( $args['uid'] ) ) { |
| 140 | - $attributes['uid'] = sanitize_text_field( $args['uid'] ); | |
| 65 | + $attributes['uid'] = $args['uid']; | |
| 141 | 66 | } else { |
| 142 | - $attributes['uid'] = md5( $source_type . $source_url ); | |
| 67 | + $attributes['uid'] = md5( $source_type . sanitize_text_field( $attributes[ $source_type ] ) . sanitize_text_field( $attributes['theme'] ) ); | |
| 143 | 68 | } |
| 144 | 69 | |
| 145 | - $attributes['uid'] = apply_filters( 'ayg_gallery_id', $attributes['uid'], $args ); | |
| 146 | - | |
| 147 | - // Deprecated since v2.5.8. Retained for backward compatibility. | |
| 148 | - $deprecated_uid = md5( $source_type . $source_url . sanitize_text_field( $attributes['theme'] ) ); // Deprecated | |
| 149 | - if ( isset( $args['deprecated_uid'] ) && ! empty( $args['deprecated_uid'] ) ) { | |
| 150 | - $deprecated_uid = sanitize_text_field( $args['deprecated_uid'] ); | |
| 151 | - } | |
| 152 | - | |
| 153 | - // Deeplinked video (premium): resolve before the query so DB-served galleries can pin it as | |
| 154 | - // the first item at the SQL level, keeping the per-page count exact. API-served (legacy) | |
| 155 | - // galleries keep the old prepend behavior below. | |
| 156 | - $gallery_id_from_url = get_query_var( 'ayg_gallery_id' ); | |
| 157 | - $video_id_from_url = get_query_var( 'ayg_video_id' ); | |
| 158 | - $deeplinked_video = false; | |
| 159 | - | |
| 160 | - if ( ! empty( $video_id_from_url ) && ( $attributes['uid'] == $gallery_id_from_url || $deprecated_uid == $gallery_id_from_url ) ) { | |
| 161 | - $deeplinked_video = ayg_db_get_video( $video_id_from_url ); | |
| 162 | - } | |
| 163 | - | |
| 164 | - if ( ! $deeplinked_video ) { | |
| 165 | - $video_id_from_url = ''; | |
| 166 | - } | |
| 167 | - | |
| 168 | - $featured_video_id = ( 'db' === $source_type && ! empty( $video_id_from_url ) ) ? sanitize_text_field( $video_id_from_url ) : ''; | |
| 169 | - | |
| 170 | - // Random ordering: generate a per-render seed (once) so RAND(seed) stays stable across this | |
| 171 | - // gallery's own pagination/search AJAX calls. A seed hardcoded in the shortcode is respected. | |
| 172 | - if ( 'random' === $attributes['sort_by'] && empty( $attributes['sort_seed'] ) ) { | |
| 173 | - $attributes['sort_seed'] = wp_rand( 1, 2147483647 ); | |
| 174 | - } | |
| 175 | - | |
| 176 | 70 | // Get Videos |
| 177 | 71 | $api_params = array( |
| 178 | - 'uid' => $attributes['uid'], | |
| 179 | - 'type' => $source_type, | |
| 180 | - 'src' => $source_url, | |
| 181 | - 'store' => true, // Trusted context: the source comes from the rendered shortcode, not from a visitor's request. | |
| 182 | - 'featured_video_id' => $featured_video_id, // Works only when type = "db". | |
| 183 | - 'order' => sanitize_text_field( $attributes['order'] ), // Works only when type = "search". | |
| 184 | - 'sort_by' => sanitize_key( $attributes['sort_by'] ), // Works only when type = "db". | |
| 185 | - 'sort_order' => sanitize_key( $attributes['sort_order'] ), // Works only when type = "db". | |
| 186 | - 'sort_seed' => absint( $attributes['sort_seed'] ), // Works only when type = "db" + sort_by = "random". | |
| 187 | - 'duration_filter' => sanitize_key( $attributes['duration_filter'] ), // Works only when type = "db". | |
| 188 | - 'duration' => absint( $attributes['duration'] ), // Works only when type = "db". | |
| 189 | - 'limit' => $attributes['limit'], // Works only when type = "search". | |
| 190 | - 'maxResults' => $attributes['per_page'], | |
| 191 | - 'cache' => (int) $attributes['cache'] | |
| 72 | + 'type' => $source_type, | |
| 73 | + 'src' => sanitize_text_field( $attributes[ $source_type ] ), | |
| 74 | + 'order' => sanitize_text_field( $attributes['order'] ), // applicable only when type=search | |
| 75 | + 'maxResults' => $attributes['per_page'], | |
| 76 | + 'cache' => (int) $attributes['cache'] | |
| 192 | 77 | ); |
| 193 | 78 | |
| 194 | 79 | $api_params = apply_filters( 'ayg_youtube_api_request_params', $api_params, $args ); |
| 195 | 80 | |
| @@ -199,9 +84,9 @@ | ||
| 199 | 84 | // Process output |
| 200 | 85 | if ( ! isset( $response->error ) ) { |
| 201 | 86 | // Store Gallery ID |
| 202 | 87 | if ( $attributes['post_id'] > 0 && isset( $attributes['deeplinking'] ) && 1 == $attributes['deeplinking'] ) { |
| 203 | - $pages = ayg_get_option( 'ayg_gallery_page_ids' ); | |
| 88 | + $pages = get_option( 'ayg_gallery_page_ids', array() ); | |
| 204 | 89 | $page_id = $attributes['post_id']; |
| 205 | 90 | |
| 206 | 91 | if ( ! in_array( $page_id, $pages ) ) { |
| 207 | 92 | $pages[] = $page_id; |
| @@ -207,26 +92,31 @@ | ||
| 207 | 92 | $pages[] = $page_id; |
| 208 | 93 | update_option( 'ayg_gallery_page_ids', $pages ); |
| 209 | 94 | } |
| 210 | 95 | } |
| 96 | + | |
| 97 | + // Enqueue dependencies | |
| 98 | + wp_enqueue_style( AYG_SLUG . '-public' ); | |
| 99 | + wp_enqueue_script( AYG_SLUG . '-public' ); | |
| 211 | 100 | |
| 212 | 101 | // Gallery |
| 213 | - $videos = array(); | |
| 102 | + $videos = array(); | |
| 214 | 103 | |
| 215 | - if ( ! empty( $video_id_from_url ) ) { | |
| 216 | - // DB-served galleries pin the deeplinked video via the SQL query itself, so the result | |
| 217 | - // set already starts with it. Fall back to prepending it only when the pin couldn't | |
| 218 | - // apply: API-served (legacy) sources, or the video is no longer part of this gallery's | |
| 219 | - // result set (removed from the gallery, or excluded by a duration filter). | |
| 220 | - $is_pinned_by_sql = ! empty( $api_params['featured_video_id'] ) && ! empty( $response->videos ) && $response->videos[0]->id == $video_id_from_url; | |
| 104 | + $gallery_id_from_url = get_query_var( 'ayg_gallery_id' ); | |
| 105 | + $video_id_from_url = get_query_var( 'ayg_video_id' ); | |
| 221 | 106 | |
| 222 | - if ( ! $is_pinned_by_sql ) { | |
| 223 | - $videos[] = $deeplinked_video; | |
| 107 | + if ( $attributes['uid'] == $gallery_id_from_url ) { | |
| 108 | + $video = ayg_db_get_video( $video_id_from_url ); | |
| 109 | + | |
| 110 | + if ( $video ) { | |
| 111 | + $videos[] = $video; | |
| 112 | + } else { | |
| 113 | + $video_id_from_url = ''; | |
| 224 | 114 | } |
| 225 | 115 | } |
| 226 | - | |
| 116 | + | |
| 227 | 117 | if ( isset( $response->videos ) ) { |
| 228 | - if ( ! empty( $videos ) ) { | |
| 118 | + if ( ! empty( $video_id_from_url ) ) { | |
| 229 | 119 | foreach ( $response->videos as $video ) { |
| 230 | 120 | if ( $video->id != $video_id_from_url ) { |
| 231 | 121 | $videos[] = $video; |
| 232 | 122 | } |
| @@ -237,17 +127,9 @@ | ||
| 237 | 127 | } |
| 238 | 128 | |
| 239 | 129 | // Pagination |
| 240 | 130 | if ( isset( $response->page_info ) ) { |
| 241 | - $page_info = $response->page_info; | |
| 242 | - | |
| 243 | - // Sign the page tokens on their way out to the browser so the public AJAX endpoint can | |
| 244 | - // tell the tokens this site issued from ones a visitor made up. | |
| 245 | - if ( ayg_page_token_is_signed( $source_type ) ) { | |
| 246 | - $page_info = ayg_sign_page_tokens( $page_info, $attributes['uid'] ); | |
| 247 | - } | |
| 248 | - | |
| 249 | - $attributes = array_merge( $attributes, $api_params, $page_info ); | |
| 131 | + $attributes = array_merge( $attributes, $api_params, $response->page_info ); | |
| 250 | 132 | } |
| 251 | 133 | |
| 252 | 134 | // Theme |
| 253 | 135 | $theme = 'classic'; |
| @@ -261,328 +143,92 @@ | ||
| 261 | 143 | $theme = 'single'; |
| 262 | 144 | } |
| 263 | 145 | } |
| 264 | 146 | |
| 265 | - // Enqueue dependencies | |
| 266 | - wp_enqueue_style( AYG_SLUG . '-public' ); | |
| 267 | - | |
| 268 | - wp_enqueue_script( AYG_SLUG . '-public' ); | |
| 269 | - if ( $attributes['theme'] == $theme && 'classic' == $attributes['theme'] ) { | |
| 270 | - wp_enqueue_script( AYG_SLUG . '-theme-classic' ); | |
| 271 | - } | |
| 272 | - | |
| 273 | 147 | // Output |
| 274 | 148 | ob_start(); |
| 275 | 149 | include ayg_get_template( AYG_DIR . "public/templates/theme-{$theme}.php", $attributes['theme'] ); |
| 276 | 150 | return ob_get_clean(); |
| 277 | 151 | } else { |
| 278 | - return sprintf( '<div class="ayg ayg-error">%s</div>', wp_kses_post( $response->error_message ) ); | |
| 152 | + return '<p class="ayg-error">' . $response->error_message . '</p>'; | |
| 279 | 153 | } |
| 280 | 154 | } |
| 281 | 155 | |
| 282 | 156 | /** |
| 283 | - * Combine video attributes as a string. | |
| 284 | - * | |
| 285 | - * @since 2.5.0 | |
| 286 | - * @param array $atts Array of video attributes. | |
| 287 | - * @param string Combined attributes string. | |
| 288 | - */ | |
| 289 | -function ayg_combine_video_attributes( $atts ) { | |
| 290 | - $attributes = array(); | |
| 291 | - | |
| 292 | - foreach ( $atts as $key => $value ) { | |
| 293 | - if ( '' === $value ) { | |
| 294 | - $attributes[] = $key; | |
| 295 | - } else { | |
| 296 | - $attributes[] = sprintf( '%s="%s"', $key, $value ); | |
| 297 | - } | |
| 298 | - } | |
| 299 | - | |
| 300 | - return implode( ' ', $attributes ); | |
| 301 | -} | |
| 302 | - | |
| 303 | -/** | |
| 304 | - * Create custom database tables | |
| 157 | + * Create a custom database table "{$wpdb->prefix}ayg_videos" | |
| 305 | 158 | * |
| 306 | 159 | * @since 2.1.0 |
| 307 | 160 | */ |
| 308 | -function ayg_db_create_custom_tables() { | |
| 161 | +function ayg_db_create_videos_table() { | |
| 309 | 162 | global $wpdb; |
| 310 | 163 | |
| 311 | - require_once( ABSPATH . 'wp-admin/includes/upgrade.php' ); | |
| 312 | - $charset_collate = $wpdb->get_charset_collate(); | |
| 164 | + $charset_collate = $wpdb->get_charset_collate(); | |
| 313 | 165 | |
| 314 | - $videos_table = $wpdb->prefix . 'ayg_videos'; | |
| 315 | - $rel_table = $wpdb->prefix . 'ayg_gallery_relationships'; | |
| 316 | - $old_table = $wpdb->prefix . 'ayg_galleries'; | |
| 317 | - | |
| 318 | - // Rename wp_ayg_galleries (old pivot) → wp_ayg_gallery_relationships. | |
| 319 | - // Guard: only when the old table exists AND the new name does not yet exist AND | |
| 320 | - // the old table is the pivot (identified by having a video_id column). | |
| 321 | - $old_exists = $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $wpdb->esc_like( $old_table ) ) ) === $old_table; | |
| 322 | - $new_not_exist = $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $wpdb->esc_like( $rel_table ) ) ) !== $rel_table; | |
| 323 | - | |
| 324 | - if ( $old_exists && $new_not_exist ) { | |
| 325 | - $is_old_pivot = ! empty( $wpdb->get_results( "SHOW COLUMNS FROM `$old_table` LIKE 'video_id'" ) ); | |
| 326 | - | |
| 327 | - if ( $is_old_pivot ) { | |
| 328 | - $renamed = $wpdb->query( "RENAME TABLE `$old_table` TO `$rel_table`" ); | |
| 329 | - | |
| 330 | - if ( false === $renamed ) { | |
| 331 | - // Rename failed (insufficient DB privilege on some hosts). | |
| 332 | - // This table holds auto-generated cache data, not user content — safe to drop. | |
| 333 | - $wpdb->query( "DROP TABLE IF EXISTS `$old_table`" ); | |
| 334 | - } | |
| 335 | - } | |
| 336 | - } | |
| 337 | - | |
| 338 | - // Rename columns in wp_ayg_videos (NUM → id, id → video_id). | |
| 339 | - // Each ALTER is guarded so the block is idempotent on re-runs. | |
| 340 | - $videos_table_exists = $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $wpdb->esc_like( $videos_table ) ) ) === $videos_table; | |
| 341 | - | |
| 342 | - if ( $videos_table_exists ) { | |
| 343 | - // Rename 'id' → 'video_id' first. Must happen before NUM → id to avoid a duplicate | |
| 344 | - // column name error: if NUM were renamed to 'id' while the varchar 'id' still exists, | |
| 345 | - // MySQL would reject it with "Duplicate column name 'id'". | |
| 346 | - $old_id_col = $wpdb->get_row( "SHOW COLUMNS FROM `$videos_table` LIKE 'id'" ); | |
| 347 | - | |
| 348 | - if ( $old_id_col && false !== strpos( strtolower( $old_id_col->Type ), 'varchar' ) ) { | |
| 349 | - $wpdb->query( "ALTER TABLE `$videos_table` CHANGE `id` `video_id` varchar(100) NOT NULL" ); | |
| 350 | - } | |
| 351 | - | |
| 352 | - // Now safe to rename NUM → id (no column named 'id' exists at this point). | |
| 353 | - $has_num = ! empty( $wpdb->get_results( "SHOW COLUMNS FROM `$videos_table` LIKE 'NUM'" ) ); | |
| 354 | - | |
| 355 | - if ( $has_num ) { | |
| 356 | - $wpdb->query( "ALTER TABLE `$videos_table` CHANGE `NUM` `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT" ); | |
| 357 | - } | |
| 358 | - | |
| 359 | - // Remove duplicate rows using the new column names (idempotent). | |
| 360 | - $wpdb->query( | |
| 361 | - "DELETE v1 FROM $videos_table v1 | |
| 362 | - INNER JOIN $videos_table v2 | |
| 363 | - ON v1.video_id = v2.video_id AND v1.id > v2.id" | |
| 364 | - ); | |
| 365 | - } | |
| 366 | - | |
| 367 | - // Create/update wp_ayg_videos — dbDelta adds the 2 new columns on existing installs. | |
| 368 | - $sql = "CREATE TABLE $videos_table ( | |
| 369 | - id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT, | |
| 370 | - video_id varchar(100) NOT NULL, | |
| 166 | + $sql = "CREATE TABLE `{$wpdb->prefix}ayg_videos` ( | |
| 167 | + NUM bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT, | |
| 168 | + id varchar(100) NOT NULL, | |
| 371 | 169 | title text NOT NULL, |
| 372 | 170 | description text NOT NULL, |
| 373 | - thumbnails text NOT NULL, | |
| 171 | + thumbnails text NOT NULL, | |
| 374 | 172 | duration varchar(25) NOT NULL, |
| 375 | - duration_seconds int(11) UNSIGNED NOT NULL DEFAULT 0, | |
| 376 | - video_type varchar(20) NOT NULL DEFAULT 'none', | |
| 377 | 173 | status varchar(100) NOT NULL, |
| 378 | 174 | published_at varchar(100) NOT NULL, |
| 379 | - published_at_datetime datetime NULL, | |
| 380 | - PRIMARY KEY (id), | |
| 381 | - UNIQUE KEY ayg_unique_video_id (video_id), | |
| 382 | - INDEX ayg_idx_published_at_datetime (published_at_datetime) | |
| 175 | + PRIMARY KEY (NUM) | |
| 383 | 176 | ) $charset_collate;"; |
| 384 | - | |
| 385 | - dbDelta( $sql ); | |
| 386 | - | |
| 387 | - // Fallback: add UNIQUE KEY if dbDelta did not create it (edge case on existing installs). | |
| 388 | - $existing_keys = $wpdb->get_results( "SHOW KEYS FROM $videos_table WHERE Key_name = 'ayg_unique_video_id'" ); | |
| 389 | 177 | |
| 390 | - if ( empty( $existing_keys ) ) { | |
| 391 | - $wpdb->query( "ALTER TABLE $videos_table ADD UNIQUE KEY ayg_unique_video_id (video_id)" ); | |
| 392 | - } | |
| 393 | - | |
| 394 | - // Fallback: add INDEX on published_at_datetime if missing (existing installs). | |
| 395 | - $existing_dt_index = $wpdb->get_results( "SHOW KEYS FROM $videos_table WHERE Key_name = 'ayg_idx_published_at_datetime'" ); | |
| 396 | - | |
| 397 | - if ( empty( $existing_dt_index ) ) { | |
| 398 | - $wpdb->query( "ALTER TABLE $videos_table ADD INDEX ayg_idx_published_at_datetime (published_at_datetime)" ); | |
| 399 | - } | |
| 400 | - | |
| 401 | - // Rename NUM → id in wp_ayg_gallery_relationships (only if rename succeeded above). | |
| 402 | - // If the table was recreated fresh by dbDelta below, it already has 'id' — no ALTER needed. | |
| 403 | - $rel_table_exists = $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $wpdb->esc_like( $rel_table ) ) ) === $rel_table; | |
| 404 | - | |
| 405 | - if ( $rel_table_exists ) { | |
| 406 | - $has_num_in_rel = ! empty( $wpdb->get_results( "SHOW COLUMNS FROM `$rel_table` LIKE 'NUM'" ) ); | |
| 407 | - | |
| 408 | - if ( $has_num_in_rel ) { | |
| 409 | - $wpdb->query( "ALTER TABLE `$rel_table` CHANGE `NUM` `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT" ); | |
| 410 | - } | |
| 411 | - } | |
| 412 | - | |
| 413 | - // Create/update wp_ayg_gallery_relationships (fresh creation if the rename fallback dropped the old table). | |
| 414 | - $sql = "CREATE TABLE $rel_table ( | |
| 415 | - id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT, | |
| 416 | - video_id varchar(100) NOT NULL, | |
| 417 | - gallery_id varchar(100) NOT NULL, | |
| 418 | - synced_at datetime NULL, | |
| 419 | - PRIMARY KEY (id), | |
| 420 | - UNIQUE KEY ayg_unique_video_gallery (video_id, gallery_id), | |
| 421 | - INDEX ayg_idx_gallery_id (gallery_id) | |
| 422 | - ) $charset_collate;"; | |
| 423 | - | |
| 178 | + require_once( ABSPATH . 'wp-admin/includes/upgrade.php' ); | |
| 424 | 179 | dbDelta( $sql ); |
| 425 | - | |
| 426 | - // Fallback: add the synced_at column if dbDelta did not (existing installs). | |
| 427 | - $has_synced_at = ! empty( $wpdb->get_results( "SHOW COLUMNS FROM `$rel_table` LIKE 'synced_at'" ) ); | |
| 428 | - | |
| 429 | - if ( ! $has_synced_at ) { | |
| 430 | - $wpdb->query( "ALTER TABLE `$rel_table` ADD `synced_at` datetime NULL" ); | |
| 431 | - } | |
| 432 | - | |
| 433 | - // Create new wp_ayg_galleries entity table (one row per saved gallery config). | |
| 434 | - $galleries_table = $wpdb->prefix . 'ayg_galleries'; | |
| 435 | - | |
| 436 | - $sql = "CREATE TABLE $galleries_table ( | |
| 437 | - id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT, | |
| 438 | - title text NOT NULL, | |
| 439 | - source_type varchar(50) NOT NULL DEFAULT 'channel', | |
| 440 | - source_value text NOT NULL, | |
| 441 | - params longtext NOT NULL, | |
| 442 | - import_status varchar(20) NOT NULL DEFAULT 'idle', | |
| 443 | - import_error text NULL, | |
| 444 | - import_log longtext NULL, | |
| 445 | - last_imported_at datetime NULL, | |
| 446 | - next_import_at datetime NULL, | |
| 447 | - video_count int(11) UNSIGNED NOT NULL DEFAULT 0, | |
| 448 | - created_at datetime NOT NULL, | |
| 449 | - updated_at datetime NOT NULL, | |
| 450 | - PRIMARY KEY (id), | |
| 451 | - INDEX idx_import (next_import_at, import_status) | |
| 452 | - ) $charset_collate;"; | |
| 453 | - | |
| 454 | - dbDelta( $sql ); | |
| 455 | 180 | } |
| 456 | 181 | |
| 457 | 182 | /** |
| 458 | - * Check whether any videos are linked to the given gallery UID. | |
| 459 | - * | |
| 460 | - * Lets the public AJAX endpoint tell a gallery this site actually displays from a UID a visitor | |
| 461 | - * simply made up: a gallery that has been rendered even once always has its videos in the | |
| 462 | - * relationship table, an invented UID never does. | |
| 463 | - * | |
| 464 | - * @since 2.9.0 | |
| 465 | - * @param string $gallery_id Gallery UID. | |
| 466 | - * @return bool True when at least one video is linked to the UID. | |
| 467 | - */ | |
| 468 | -function ayg_db_gallery_has_videos( $gallery_id ) { | |
| 469 | - global $wpdb; | |
| 470 | - | |
| 471 | - $gallery_id = (string) $gallery_id; | |
| 472 | - | |
| 473 | - if ( '' === $gallery_id ) { | |
| 474 | - return false; | |
| 475 | - } | |
| 476 | - | |
| 477 | - $found = $wpdb->get_var( | |
| 478 | - $wpdb->prepare( | |
| 479 | - "SELECT 1 FROM {$wpdb->prefix}ayg_gallery_relationships WHERE gallery_id = %s LIMIT 1", | |
| 480 | - $gallery_id | |
| 481 | - ) | |
| 482 | - ); | |
| 483 | - | |
| 484 | - return ! empty( $found ); | |
| 485 | -} | |
| 486 | - | |
| 487 | -/** | |
| 488 | - * Get a single video record from our custom database table "{$wpdb->prefix}ayg_videos" | |
| 489 | - * | |
| 490 | - * @since 2.1.0 | |
| 491 | - * @param string $video_id YouTube Video ID. | |
| 492 | - * @return mixed | |
| 493 | - */ | |
| 494 | -function ayg_db_get_video( $video_id ) { | |
| 495 | - global $wpdb; | |
| 496 | - | |
| 497 | - $cache_key = 'ayg_'. $video_id; | |
| 498 | - $row = wp_cache_get( $cache_key ); | |
| 499 | - | |
| 500 | - if ( false === $row ) { | |
| 501 | - $query = $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}ayg_videos WHERE video_id = %s", $video_id ); | |
| 502 | - $row = $wpdb->get_row( $query ); | |
| 503 | - | |
| 504 | - if ( $row ) { | |
| 505 | - if ( ! empty( $row->thumbnails ) ) { | |
| 506 | - $row->thumbnails = ayg_maybe_unserialize( $row->thumbnails ); | |
| 507 | - } | |
| 508 | - | |
| 509 | - // Backward compat: templates reference $video->id as the YouTube video ID. | |
| 510 | - $row->id = $row->video_id; | |
| 511 | - | |
| 512 | - wp_cache_set( $cache_key, $row ); | |
| 513 | - } | |
| 514 | - } | |
| 515 | - | |
| 516 | - return $row; | |
| 517 | -} | |
| 518 | - | |
| 519 | -/** | |
| 520 | 183 | * Store videos in our custom database table "{$wpdb->prefix}ayg_videos" |
| 521 | 184 | * |
| 522 | 185 | * @since 2.1.0 |
| 523 | - * @param object $data YouTube API response object. | |
| 524 | - * @param array $attributes Array of user attributes. | |
| 186 | + * @param object $data YouTube API response object. | |
| 525 | 187 | */ |
| 526 | -function ayg_db_store_videos( $data, $attributes = array() ) { | |
| 188 | +function ayg_db_store_videos( $data ) { | |
| 527 | 189 | if ( AYG_VERSION !== get_option( 'ayg_version' ) ) { |
| 528 | 190 | return false; |
| 529 | 191 | } |
| 530 | 192 | |
| 193 | + global $wpdb, $post; | |
| 194 | + | |
| 195 | + $table_name = $wpdb->prefix . 'ayg_videos'; | |
| 196 | + | |
| 531 | 197 | if ( isset( $data->kind ) && 'youtube#channelListResponse' == $data->kind ) { |
| 532 | 198 | return false; |
| 533 | 199 | } |
| 534 | 200 | |
| 535 | - if ( empty( $data->items ) || ! is_array( $data->items ) ) { | |
| 201 | + if ( ! isset( $data->items ) ) { | |
| 536 | 202 | return false; |
| 537 | 203 | } |
| 538 | 204 | |
| 539 | - global $wpdb; | |
| 205 | + $items = $data->items; | |
| 206 | + if ( ! is_array( $items ) || 0 == count( $items ) ) { | |
| 207 | + return false; | |
| 208 | + } | |
| 540 | 209 | |
| 541 | - $items = $data->items; | |
| 542 | - $gallery_id = isset( $attributes['uid'] ) ? $attributes['uid'] : ''; | |
| 543 | - $source_type = isset( $attributes['type'] ) ? $attributes['type'] : 'videos'; | |
| 544 | - $store_gallery = ! empty( $gallery_id ) && 'livestream' !== $source_type; | |
| 545 | - $exclude = ( isset( $attributes['exclude'] ) && is_array( $attributes['exclude'] ) ) ? $attributes['exclude'] : array(); | |
| 546 | - | |
| 547 | - // Only videos.list responses carry contentDetails.duration and snippet.liveBroadcastContent. | |
| 548 | - // For other responses (playlistItems.list, search.list) the duration_seconds / video_type | |
| 549 | - // columns are left out entirely so existing values are never clobbered with defaults. | |
| 550 | - $has_video_details = isset( $data->kind ) && 'youtube#videoListResponse' === $data->kind; | |
| 551 | - | |
| 552 | - $videos_table = $wpdb->prefix . 'ayg_videos'; | |
| 553 | - $galleries_table = $wpdb->prefix . 'ayg_gallery_relationships'; | |
| 554 | - | |
| 555 | - $video_placeholders = array(); | |
| 556 | - $video_values = array(); | |
| 557 | - $gallery_placeholders = array(); | |
| 558 | - $gallery_values = array(); | |
| 559 | - | |
| 560 | - foreach ( $items as $item ) { | |
| 210 | + // Store Videos | |
| 211 | + foreach ( $items as $item ) { | |
| 561 | 212 | $row = array(); |
| 562 | 213 | |
| 563 | 214 | // Video ID |
| 564 | - $row['video_id'] = ''; | |
| 215 | + $row['id'] = ''; | |
| 565 | 216 | |
| 566 | 217 | if ( isset( $item->snippet->resourceId ) && isset( $item->snippet->resourceId->videoId ) ) { |
| 567 | - $row['video_id'] = $item->snippet->resourceId->videoId; | |
| 218 | + $row['id'] = $item->snippet->resourceId->videoId; | |
| 568 | 219 | } elseif ( isset( $item->contentDetails ) && isset( $item->contentDetails->videoId ) ) { |
| 569 | - $row['video_id'] = $item->contentDetails->videoId; | |
| 220 | + $row['id'] = $item->contentDetails->videoId; | |
| 570 | 221 | } elseif ( isset( $item->id ) && isset( $item->id->videoId ) ) { |
| 571 | - $row['video_id'] = $item->id->videoId; | |
| 222 | + $row['id'] = $item->id->videoId; | |
| 572 | 223 | } elseif ( isset( $item->id ) ) { |
| 573 | - $row['video_id'] = $item->id; | |
| 574 | - } | |
| 224 | + $row['id'] = $item->id; | |
| 225 | + } | |
| 575 | 226 | |
| 576 | - if ( empty( $row['video_id'] ) ) { | |
| 227 | + if ( empty( $row['id'] ) ) { | |
| 577 | 228 | continue; |
| 578 | 229 | } |
| 579 | 230 | |
| 580 | - // Skip videos on the gallery's exclude list (kept out of both tables) | |
| 581 | - if ( ayg_is_video_excluded( $row['video_id'], $exclude ) ) { | |
| 582 | - continue; | |
| 583 | - } | |
| 584 | - | |
| 585 | 231 | // Video title |
| 586 | 232 | $row['title'] = $item->snippet->title; |
| 587 | 233 | |
| 588 | 234 | // Video description |
| @@ -591,206 +237,103 @@ | ||
| 591 | 237 | // Video thumbnails |
| 592 | 238 | $row['thumbnails'] = ''; |
| 593 | 239 | if ( isset( $item->snippet->thumbnails ) ) { |
| 594 | 240 | $row['thumbnails'] = serialize( $item->snippet->thumbnails ); |
| 595 | - } | |
| 241 | + } | |
| 596 | 242 | |
| 597 | 243 | // Video duration |
| 598 | 244 | $row['duration'] = ''; |
| 599 | 245 | if ( isset( $item->contentDetails ) && isset( $item->contentDetails->duration ) ) { |
| 600 | 246 | $row['duration'] = $item->contentDetails->duration; |
| 601 | - } | |
| 247 | + } | |
| 602 | 248 | |
| 603 | - if ( $has_video_details ) { | |
| 604 | - $row['duration_seconds'] = ayg_parse_duration_seconds( $row['duration'] ); | |
| 605 | - | |
| 606 | - // Raw liveBroadcastContent value ('live', 'upcoming', 'none'); shorts are not | |
| 607 | - // classified at import time — filter by duration_seconds <= 60 at query time. | |
| 608 | - $row['video_type'] = 'none'; | |
| 609 | - if ( ! empty( $item->snippet->liveBroadcastContent ) ) { | |
| 610 | - $row['video_type'] = $item->snippet->liveBroadcastContent; | |
| 611 | - } | |
| 612 | - } | |
| 613 | - | |
| 614 | 249 | // Video status |
| 615 | 250 | $row['status'] = 'private'; |
| 616 | - | |
| 251 | + | |
| 617 | 252 | if ( isset( $item->status ) && ( 'public' == $item->status->privacyStatus || 'unlisted' == $item->status->privacyStatus ) ) { |
| 618 | - $row['status'] = 'public'; | |
| 253 | + $row['status'] = 'public'; | |
| 619 | 254 | } |
| 620 | 255 | |
| 621 | 256 | if ( isset( $item->snippet->status ) && ( 'public' == $item->snippet->status->privacyStatus || 'unlisted' == $item->snippet->status->privacyStatus ) ) { |
| 622 | - $row['status'] = 'public'; | |
| 257 | + $row['status'] = 'public'; | |
| 623 | 258 | } |
| 624 | 259 | |
| 625 | 260 | if ( 'youtube#searchResult' == $item->kind ) { |
| 626 | - $row['status'] = 'public'; | |
| 261 | + $row['status'] = 'public'; | |
| 627 | 262 | } |
| 628 | 263 | |
| 629 | 264 | // Video publish date |
| 630 | 265 | $row['published_at'] = $item->snippet->publishedAt; |
| 631 | 266 | |
| 632 | - $datetime = new DateTime( $item->snippet->publishedAt ); | |
| 633 | - $row['published_at_datetime'] = date_format( $datetime, 'Y-m-d H:i:s' ); | |
| 267 | + // Store | |
| 268 | + $query = $wpdb->prepare( "SELECT NUM FROM $table_name WHERE id = %s", $row['id'] ); | |
| 269 | + $insert_id = $wpdb->get_var( $query ); | |
| 634 | 270 | |
| 635 | - // Collect for bulk insert | |
| 636 | - if ( $has_video_details ) { | |
| 637 | - $video_placeholders[] = '(%s, %s, %s, %s, %s, %d, %s, %s, %s, %s)'; | |
| 638 | - | |
| 639 | - array_push( | |
| 640 | - $video_values, | |
| 641 | - $row['video_id'], | |
| 642 | - $row['title'], | |
| 643 | - $row['description'], | |
| 644 | - $row['thumbnails'], | |
| 645 | - $row['duration'], | |
| 646 | - $row['duration_seconds'], | |
| 647 | - $row['video_type'], | |
| 648 | - $row['status'], | |
| 649 | - $row['published_at'], | |
| 650 | - $row['published_at_datetime'] | |
| 271 | + if ( empty( $insert_id ) ) { | |
| 272 | + $wpdb->insert( | |
| 273 | + $table_name, | |
| 274 | + $row, | |
| 275 | + array( '%s', '%s', '%s', '%s', '%s', '%s', '%s' ) | |
| 651 | 276 | ); |
| 652 | 277 | } else { |
| 653 | - $video_placeholders[] = '(%s, %s, %s, %s, %s, %s, %s, %s)'; | |
| 654 | - | |
| 655 | - array_push( | |
| 656 | - $video_values, | |
| 657 | - $row['video_id'], | |
| 658 | - $row['title'], | |
| 659 | - $row['description'], | |
| 660 | - $row['thumbnails'], | |
| 661 | - $row['duration'], | |
| 662 | - $row['status'], | |
| 663 | - $row['published_at'], | |
| 664 | - $row['published_at_datetime'] | |
| 278 | + $wpdb->update( | |
| 279 | + $table_name, | |
| 280 | + $row, | |
| 281 | + array( 'NUM' => $insert_id ), | |
| 282 | + array( '%s', '%s', '%s', '%s', '%s', '%s', '%s' ), | |
| 283 | + array( '%d' ) | |
| 665 | 284 | ); |
| 666 | 285 | } |
| 667 | - | |
| 668 | - if ( $store_gallery ) { | |
| 669 | - $gallery_placeholders[] = '(%s, %s)'; | |
| 670 | - array_push( $gallery_values, $row['video_id'], $gallery_id ); | |
| 671 | - } | |
| 672 | 286 | } |
| 673 | - | |
| 674 | - // Bulk insert videos (2 queries total instead of N×2) | |
| 675 | - if ( ! empty( $video_placeholders ) ) { | |
| 676 | - if ( $has_video_details ) { | |
| 677 | - $sql = "INSERT INTO $videos_table (video_id, title, description, thumbnails, duration, duration_seconds, video_type, status, published_at, published_at_datetime) | |
| 678 | - VALUES " . implode( ', ', $video_placeholders ) . " | |
| 679 | - ON DUPLICATE KEY UPDATE | |
| 680 | - title = VALUES(title), | |
| 681 | - description = VALUES(description), | |
| 682 | - thumbnails = VALUES(thumbnails), | |
| 683 | - duration = VALUES(duration), | |
| 684 | - duration_seconds = VALUES(duration_seconds), | |
| 685 | - video_type = VALUES(video_type), | |
| 686 | - status = VALUES(status), | |
| 687 | - published_at = VALUES(published_at), | |
| 688 | - published_at_datetime = VALUES(published_at_datetime)"; | |
| 689 | - } else { | |
| 690 | - $sql = "INSERT INTO $videos_table (video_id, title, description, thumbnails, duration, status, published_at, published_at_datetime) | |
| 691 | - VALUES " . implode( ', ', $video_placeholders ) . " | |
| 692 | - ON DUPLICATE KEY UPDATE | |
| 693 | - title = VALUES(title), | |
| 694 | - description = VALUES(description), | |
| 695 | - thumbnails = VALUES(thumbnails), | |
| 696 | - duration = VALUES(duration), | |
| 697 | - status = VALUES(status), | |
| 698 | - published_at = VALUES(published_at), | |
| 699 | - published_at_datetime = VALUES(published_at_datetime)"; | |
| 700 | - } | |
| 701 | - | |
| 702 | - $wpdb->query( $wpdb->prepare( $sql, $video_values ) ); | |
| 703 | - } | |
| 704 | - | |
| 705 | - // Bulk insert gallery relationships (2 queries total instead of N×2) | |
| 706 | - if ( ! empty( $gallery_placeholders ) ) { | |
| 707 | - $sql = "INSERT IGNORE INTO $galleries_table (video_id, gallery_id) VALUES " . implode( ', ', $gallery_placeholders ); | |
| 708 | - $wpdb->query( $wpdb->prepare( $sql, $gallery_values ) ); | |
| 709 | - } | |
| 710 | 287 | } |
| 711 | 288 | |
| 712 | 289 | /** |
| 713 | - * Dump our plugin transients. | |
| 290 | + * Get a single video record from our custom database table "{$wpdb->prefix}ayg_videos" | |
| 714 | 291 | * |
| 715 | - * @since 2.1.0 | |
| 716 | - * @param string $uid Gallery uid to target. Empty string flushes all plugin transients. | |
| 292 | + * @since 2.1.0 | |
| 293 | + * @param string $video_id YouTube Video ID. | |
| 294 | + * @return mixed | |
| 717 | 295 | */ |
| 718 | -function ayg_delete_cache( $uid = '' ) { | |
| 719 | - $uid = (string) $uid; | |
| 720 | - $keys = (array) ayg_get_option( 'ayg_transient_keys' ); | |
| 296 | +function ayg_db_get_video( $video_id ) { | |
| 297 | + global $wpdb; | |
| 721 | 298 | |
| 722 | - // Targeted: delete only the keys prefixed with this gallery's uid, keep the rest in the registry. | |
| 723 | - if ( '' !== $uid ) { | |
| 724 | - $prefix = 'ayg_' . $uid . '_'; | |
| 725 | - $remaining = array(); | |
| 299 | + $cache_key = 'ayg_'. $video_id; | |
| 300 | + $row = wp_cache_get( $cache_key ); | |
| 726 | 301 | |
| 727 | - foreach ( $keys as $key ) { | |
| 728 | - if ( 0 === strpos( $key, $prefix ) ) { | |
| 729 | - delete_transient( $key ); | |
| 730 | - } else { | |
| 731 | - $remaining[] = $key; | |
| 302 | + if ( false === $row ) { | |
| 303 | + $query = $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}ayg_videos WHERE id = %s", $video_id ); | |
| 304 | + $row = $wpdb->get_row( $query ); | |
| 305 | + | |
| 306 | + if ( $row ) { | |
| 307 | + if ( ! empty( $row->thumbnails ) ) { | |
| 308 | + $row->thumbnails = unserialize( $row->thumbnails ); | |
| 732 | 309 | } |
| 733 | - } | |
| 734 | 310 | |
| 735 | - update_option( 'ayg_transient_keys', $remaining, false ); | |
| 736 | - return; | |
| 311 | + wp_cache_set( $cache_key, $row ); | |
| 312 | + } | |
| 737 | 313 | } |
| 738 | 314 | |
| 739 | - // Full flush: every transient, plus the cached gallery page-id lookup. | |
| 740 | - delete_option( 'ayg_gallery_page_ids' ); | |
| 741 | - | |
| 742 | - foreach ( $keys as $key ) { | |
| 743 | - delete_transient( $key ); | |
| 744 | - } | |
| 745 | - | |
| 746 | - // Reset our DB value (autoload=no: not needed on every page load) | |
| 747 | - update_option( 'ayg_transient_keys', array(), false ); | |
| 315 | + return $row; | |
| 748 | 316 | } |
| 749 | 317 | |
| 750 | 318 | /** |
| 751 | - * Remove a gallery's excluded videos from the relationship table. | |
| 319 | + * Dump our plugin transients. | |
| 752 | 320 | * |
| 753 | - * @since 2.8.0 | |
| 754 | - * @param int $gallery_id Gallery ID. | |
| 755 | - * @param array $exclude Exclude entries (video IDs or URLs). | |
| 756 | - * @return int Number of relationships removed. | |
| 321 | + * @since 2.1.0 | |
| 757 | 322 | */ |
| 758 | -function ayg_delete_excluded_relationships( $gallery_id, $exclude ) { | |
| 759 | - global $wpdb; | |
| 323 | +function ayg_delete_cache() { | |
| 324 | + delete_option( 'ayg_gallery_page_ids' ); | |
| 325 | + | |
| 326 | + // Get the current list of transients | |
| 327 | + $transient_keys = get_option( 'ayg_transient_keys', array() ); | |
| 760 | 328 | |
| 761 | - $gallery_id = absint( $gallery_id ); | |
| 762 | - | |
| 763 | - if ( $gallery_id <= 0 || empty( $exclude ) || ! is_array( $exclude ) ) { | |
| 764 | - return 0; | |
| 329 | + // For each key, delete that transient | |
| 330 | + foreach ( $transient_keys as $key ) { | |
| 331 | + delete_transient( $key ); | |
| 765 | 332 | } |
| 766 | - | |
| 767 | - // Resolve the exclude entries (IDs or URLs) to video IDs. | |
| 768 | - $video_ids = array(); | |
| 769 | - | |
| 770 | - foreach ( $exclude as $entry ) { | |
| 771 | - $video_id = ayg_get_youtube_video_id( $entry ); | |
| 772 | - | |
| 773 | - if ( '' !== $video_id ) { | |
| 774 | - $video_ids[] = $video_id; | |
| 775 | - } | |
| 776 | - } | |
| 777 | - | |
| 778 | - $video_ids = array_values( array_unique( $video_ids ) ); | |
| 779 | - | |
| 780 | - if ( empty( $video_ids ) ) { | |
| 781 | - return 0; | |
| 782 | - } | |
| 783 | - | |
| 784 | - $rel_table = $wpdb->prefix . 'ayg_gallery_relationships'; | |
| 785 | - $placeholders = implode( ',', array_fill( 0, count( $video_ids ), '%s' ) ); | |
| 786 | - | |
| 787 | - return (int) $wpdb->query( | |
| 788 | - $wpdb->prepare( | |
| 789 | - "DELETE FROM $rel_table WHERE gallery_id = %s AND video_id IN ( $placeholders )", | |
| 790 | - array_merge( array( strval( $gallery_id ) ), $video_ids ) | |
| 791 | - ) | |
| 792 | - ); | |
| 333 | + | |
| 334 | + // Reset our DB value | |
| 335 | + update_option( 'ayg_transient_keys', array() ); | |
| 793 | 336 | } |
| 794 | 337 | |
| 795 | 338 | /** |
| 796 | 339 | * Get current address bar URL. |
| @@ -813,22 +356,11 @@ | ||
| 813 | 356 | */ |
| 814 | 357 | function ayg_get_default_settings() { |
| 815 | 358 | $defaults = array( |
| 816 | 359 | 'ayg_general_settings' => array( |
| 817 | - 'force_load_assets' => array( | |
| 818 | - 'css' => 'css' | |
| 819 | - ), | |
| 820 | - 'api_key' => '', | |
| 821 | - 'lazyload' => 0, | |
| 822 | - 'development_mode' => 0 | |
| 360 | + 'api_key' => '', | |
| 361 | + 'development_mode' => 1 | |
| 823 | 362 | ), |
| 824 | - 'ayg_strings_settings' => array( | |
| 825 | - 'more_button_label' => __( 'Load More', 'automatic-youtube-gallery' ), | |
| 826 | - 'previous_button_label' => __( 'Previous', 'automatic-youtube-gallery' ), | |
| 827 | - 'next_button_label' => __( 'Next', 'automatic-youtube-gallery' ), | |
| 828 | - 'show_more_label' => __( 'Show More', 'automatic-youtube-gallery' ), | |
| 829 | - 'show_less_label' => __( 'Show Less', 'automatic-youtube-gallery' ) | |
| 830 | - ), | |
| 831 | 363 | 'ayg_gallery_settings' => array( |
| 832 | 364 | 'theme' => 'classic', |
| 833 | 365 | 'columns' => 3, |
| 834 | 366 | 'per_page' => 12, |
| @@ -838,14 +370,13 @@ | ||
| 838 | 370 | 'thumb_excerpt' => 1, |
| 839 | 371 | 'thumb_excerpt_length' => 75, |
| 840 | 372 | 'pagination' => 1, |
| 841 | 373 | 'pagination_type' => 'more', |
| 842 | - 'scroll_top_offset' => 10 | |
| 374 | + 'more_button_label' => __( 'Load More', 'automatic-youtube-gallery' ), | |
| 375 | + 'previous_button_label' => __( 'Previous', 'automatic-youtube-gallery' ), | |
| 376 | + 'next_button_label' => __( 'Next', 'automatic-youtube-gallery' ) | |
| 843 | 377 | ), |
| 844 | 378 | 'ayg_player_settings' => array( |
| 845 | - 'player_type' => 'youtube', | |
| 846 | - 'player_color' => '#00b3ff', | |
| 847 | - 'player_width' => '', | |
| 848 | 379 | 'player_ratio' => 56.25, |
| 849 | 380 | 'player_title' => 1, |
| 850 | 381 | 'player_description' => 1, |
| 851 | 382 | 'autoplay' => 0, |
| @@ -854,8 +385,9 @@ | ||
| 854 | 385 | 'muted' => 0, |
| 855 | 386 | 'controls' => 1, |
| 856 | 387 | 'modestbranding' => 1, |
| 857 | 388 | 'cc_load_policy' => 0, |
| 389 | + 'iv_load_policy' => 0, | |
| 858 | 390 | 'hl' => '', |
| 859 | 391 | 'cc_lang_pref' => '', |
| 860 | 392 | 'privacy_enhanced_mode' => 0, |
| 861 | 393 | 'origin' => 0 |
| @@ -878,12 +410,12 @@ | ||
| 878 | 410 | * |
| 879 | 411 | * @since 1.0.0 |
| 880 | 412 | * @return array Array of fields. |
| 881 | 413 | */ |
| 882 | -function ayg_get_editor_fields() { | |
| 414 | +function ayg_get_editor_fields() { | |
| 883 | 415 | $fields = array( |
| 884 | 416 | 'source' => array( |
| 885 | - 'label' => __( 'Source Configuration', 'automatic-youtube-gallery' ), | |
| 417 | + 'label' => __( 'General', 'automatic-youtube-gallery' ), | |
| 886 | 418 | 'fields' => array( |
| 887 | 419 | array( |
| 888 | 420 | 'name' => 'type', |
| 889 | 421 | 'label' => __( 'Source Type', 'automatic-youtube-gallery' ), |
| @@ -888,9 +420,17 @@ | ||
| 888 | 420 | 'name' => 'type', |
| 889 | 421 | 'label' => __( 'Source Type', 'automatic-youtube-gallery' ), |
| 890 | 422 | 'description' => '', |
| 891 | 423 | 'type' => 'select', |
| 892 | - 'options' => ayg_get_source_types(), | |
| 424 | + 'options' => array( | |
| 425 | + 'playlist' => __( 'Playlist', 'automatic-youtube-gallery' ), | |
| 426 | + 'channel' => __( 'Channel', 'automatic-youtube-gallery' ), | |
| 427 | + 'username' => __( 'Username', 'automatic-youtube-gallery' ), | |
| 428 | + 'search' => __( 'Search Keywords', 'automatic-youtube-gallery' ), | |
| 429 | + 'video' => __( 'Single Video', 'automatic-youtube-gallery' ), | |
| 430 | + 'livestream' => __( 'Livestream', 'automatic-youtube-gallery' ), | |
| 431 | + 'videos' => __( 'Custom Videos List', 'automatic-youtube-gallery' ) | |
| 432 | + ), | |
| 893 | 433 | 'value' => 'playlist', |
| 894 | 434 | 'sanitize_callback' => 'sanitize_key' |
| 895 | 435 | ), |
| 896 | 436 | array( |
| @@ -897,18 +437,16 @@ | ||
| 897 | 437 | 'name' => 'playlist', |
| 898 | 438 | 'label' => __( 'YouTube Playlist ID (or) URL', 'automatic-youtube-gallery' ), |
| 899 | 439 | 'description' => sprintf( '%s: https://www.youtube.com/playlist?list=XXXXXXXXXX', __( 'Example', 'automatic-youtube-gallery' ) ), |
| 900 | 440 | 'type' => 'url', |
| 901 | - 'required' => 1, | |
| 902 | 441 | 'value' => '', |
| 903 | 442 | 'sanitize_callback' => 'sanitize_text_field' |
| 904 | 443 | ), |
| 905 | 444 | array( |
| 906 | 445 | 'name' => 'channel', |
| 907 | - 'label' => __( 'YouTube Channel ID (or) a Video URL from the Channel', 'automatic-youtube-gallery' ), | |
| 446 | + 'label' => __( 'YouTube Channel ID (or) a YouTube Video URL from the Channel', 'automatic-youtube-gallery' ), | |
| 908 | 447 | 'description' => sprintf( '%s: https://www.youtube.com/channel/XXXXXXXXXX', __( 'Example', 'automatic-youtube-gallery' ) ), |
| 909 | 448 | 'type' => 'url', |
| 910 | - 'required' => 1, | |
| 911 | 449 | 'value' => '', |
| 912 | 450 | 'sanitize_callback' => 'sanitize_text_field' |
| 913 | 451 | ), |
| 914 | 452 | array( |
| @@ -915,9 +453,8 @@ | ||
| 915 | 453 | 'name' => 'username', |
| 916 | 454 | 'label' => __( 'YouTube Account Username', 'automatic-youtube-gallery' ), |
| 917 | 455 | 'description' => sprintf( '%s: SanRosh', __( 'Example', 'automatic-youtube-gallery' ) ), |
| 918 | 456 | 'type' => 'text', |
| 919 | - 'required' => 1, | |
| 920 | 457 | 'value' => '', |
| 921 | 458 | 'sanitize_callback' => 'sanitize_text_field' |
| 922 | 459 | ), |
| 923 | 460 | array( |
| @@ -924,9 +461,8 @@ | ||
| 924 | 461 | 'name' => 'search', |
| 925 | 462 | 'label' => __( 'Search Keywords', 'automatic-youtube-gallery' ), |
| 926 | 463 | 'description' => sprintf( '%s: Cartoon (space:AND , -:NOT , |:OR)', __( 'Example', 'automatic-youtube-gallery' ) ), |
| 927 | 464 | 'type' => 'text', |
| 928 | - 'required' => 1, | |
| 929 | 465 | 'value' => '', |
| 930 | 466 | 'sanitize_callback' => 'sanitize_text_field' |
| 931 | 467 | ), |
| 932 | 468 | array( |
| @@ -933,9 +469,8 @@ | ||
| 933 | 469 | 'name' => 'video', |
| 934 | 470 | 'label' => __( 'YouTube Video ID (or) URL', 'automatic-youtube-gallery' ), |
| 935 | 471 | 'description' => sprintf( '%s: https://www.youtube.com/watch?v=XXXXXXXXXX', __( 'Example', 'automatic-youtube-gallery' ) ), |
| 936 | 472 | 'type' => 'url', |
| 937 | - 'required' => 1, | |
| 938 | 473 | 'value' => '', |
| 939 | 474 | 'sanitize_callback' => 'sanitize_text_field' |
| 940 | 475 | ), |
| 941 | 476 | array( |
| @@ -942,16 +477,15 @@ | ||
| 942 | 477 | 'name' => 'videos', |
| 943 | 478 | 'label' => __( 'YouTube Video IDs (or) URLs', 'automatic-youtube-gallery' ), |
| 944 | 479 | 'description' => sprintf( '%s: https://www.youtube.com/watch?v=XXXXXXXXXX', __( 'Example', 'automatic-youtube-gallery' ) ), |
| 945 | 480 | 'type' => 'textarea', |
| 946 | - 'required' => 1, | |
| 947 | 481 | 'placeholder' => __( 'Enter one video per line', 'automatic-youtube-gallery' ), |
| 948 | 482 | 'value' => '', |
| 949 | - 'sanitize_callback' => 'sanitize_textarea_field' | |
| 483 | + 'sanitize_callback' => 'sanitize_text_field' | |
| 950 | 484 | ), |
| 951 | 485 | array( |
| 952 | 486 | 'name' => 'order', |
| 953 | - 'label' => __( 'Search Order', 'automatic-youtube-gallery' ), | |
| 487 | + 'label' => __( 'Order Videos by', 'automatic-youtube-gallery' ), | |
| 954 | 488 | 'description' => '', |
| 955 | 489 | 'type' => 'select', |
| 956 | 490 | 'options' => array( |
| 957 | 491 | 'date' => __( 'Date', 'automatic-youtube-gallery' ), |
| @@ -960,14 +494,14 @@ | ||
| 960 | 494 | 'title' => __( 'Title', 'automatic-youtube-gallery' ), |
| 961 | 495 | 'viewCount' => __( 'View Count', 'automatic-youtube-gallery' ) |
| 962 | 496 | ), |
| 963 | 497 | 'value' => 'relevance', |
| 964 | - 'sanitize_callback' => 'sanitize_text_field' | |
| 498 | + 'sanitize_callback' => 'sanitize_key' | |
| 965 | 499 | ), |
| 966 | 500 | array( |
| 967 | 501 | 'name' => 'limit', |
| 968 | - 'label' => __( 'Search Limit', 'automatic-youtube-gallery' ), | |
| 969 | - 'description' => __( 'Enter the number of videos to display in this gallery. Set to 0 for the maximum amount (500).', 'automatic-youtube-gallery' ), | |
| 502 | + 'label' => __( 'Number of Videos', 'automatic-youtube-gallery' ), | |
| 503 | + 'description' => __( 'Specifies the maximum number of videos that will appear in this gallery. Set to 0 for the maximum amount (500).', 'automatic-youtube-gallery' ), | |
| 970 | 504 | 'type' => 'number', |
| 971 | 505 | 'min' => 0, |
| 972 | 506 | 'max' => 500, |
| 973 | 507 | 'value' => 0, |
| @@ -978,9 +512,8 @@ | ||
| 978 | 512 | 'label' => __( 'Cache Duration', 'automatic-youtube-gallery' ), |
| 979 | 513 | 'description' => __( 'Specifies how frequently we should check your YouTube source for new videos/updates.', 'automatic-youtube-gallery' ), |
| 980 | 514 | 'type' => 'select', |
| 981 | 515 | 'options' => array( |
| 982 | - '0' => '— '. __( 'No Caching', 'automatic-youtube-gallery' ) . ' —', | |
| 983 | 516 | '900' => __( '15 Minutes', 'automatic-youtube-gallery' ), |
| 984 | 517 | '1800' => __( '30 Minutes', 'automatic-youtube-gallery' ), |
| 985 | 518 | '3600' => __( '1 Hour', 'automatic-youtube-gallery' ), |
| 986 | 519 | '86400' => __( '1 Day', 'automatic-youtube-gallery' ), |
| @@ -992,30 +525,14 @@ | ||
| 992 | 525 | ) |
| 993 | 526 | ) |
| 994 | 527 | ), |
| 995 | 528 | 'gallery' => array( |
| 996 | - 'label' => __( 'Gallery Options', 'automatic-youtube-gallery' ), | |
| 529 | + 'label' => __( 'Gallery (optional)', 'automatic-youtube-gallery' ), | |
| 997 | 530 | 'fields' => ayg_get_gallery_settings_fields() |
| 998 | 531 | ), |
| 999 | 532 | 'player' => array( |
| 1000 | - 'label' => __( 'Player Options', 'automatic-youtube-gallery' ), | |
| 533 | + 'label' => __( 'Player (optional)', 'automatic-youtube-gallery' ), | |
| 1001 | 534 | 'fields' => ayg_get_player_settings_fields() |
| 1002 | - ), | |
| 1003 | - 'search' => array( | |
| 1004 | - 'label' => __( 'Search Form', 'automatic-youtube-gallery' ), | |
| 1005 | - 'fields' => array( | |
| 1006 | - array( | |
| 1007 | - 'name' => 'search_form', | |
| 1008 | - 'label' => __( 'Search Form', 'automatic-youtube-gallery' ), | |
| 1009 | - 'description' => sprintf( | |
| 1010 | - __( 'Check this option to enable the search form. Having issues? <a href="%s" target="_blank" rel="noopener noreferrer">Check here</a>.', 'automatic-youtube-gallery' ), | |
| 1011 | - 'https://plugins360.com/automatic-youtube-gallery/searchform/' | |
| 1012 | - ), | |
| 1013 | - 'type' => 'checkbox', | |
| 1014 | - 'value' => 0, | |
| 1015 | - 'sanitize_callback' => 'intval' | |
| 1016 | - ) | |
| 1017 | - ) | |
| 1018 | 535 | ) |
| 1019 | 536 | ); |
| 1020 | 537 | |
| 1021 | 538 | return apply_filters( 'ayg_editor_fields', $fields ); |
| @@ -1021,23 +538,8 @@ | ||
| 1021 | 538 | return apply_filters( 'ayg_editor_fields', $fields ); |
| 1022 | 539 | } |
| 1023 | 540 | |
| 1024 | 541 | /** |
| 1025 | - * Fetch a single gallery row by ID. | |
| 1026 | - * | |
| 1027 | - * @since 2.8.0 | |
| 1028 | - * @param int $gallery_id Gallery ID. | |
| 1029 | - * @return object|null | |
| 1030 | - */ | |
| 1031 | -function ayg_get_gallery( $gallery_id ) { | |
| 1032 | - global $wpdb; | |
| 1033 | - | |
| 1034 | - return $wpdb->get_row( | |
| 1035 | - $wpdb->prepare( "SELECT * FROM `{$wpdb->prefix}ayg_galleries` WHERE id = %d", absint( $gallery_id ) ) | |
| 1036 | - ); | |
| 1037 | -} | |
| 1038 | - | |
| 1039 | -/** | |
| 1040 | 542 | * Get gallery settings fields. |
| 1041 | 543 | * |
| 1042 | 544 | * @since 1.0.0 |
| 1043 | 545 | * @return array $fields Array of fields. |
| @@ -1042,9 +544,9 @@ | ||
| 1042 | 544 | * @since 1.0.0 |
| 1043 | 545 | * @return array $fields Array of fields. |
| 1044 | 546 | */ |
| 1045 | 547 | function ayg_get_gallery_settings_fields() { |
| 1046 | - $gallery_settings = ayg_get_option( 'ayg_gallery_settings' ); | |
| 548 | + $gallery_settings = get_option( 'ayg_gallery_settings' ); | |
| 1047 | 549 | |
| 1048 | 550 | $fields = array( |
| 1049 | 551 | array( |
| 1050 | 552 | 'name' => 'theme', |
| @@ -1069,9 +571,9 @@ | ||
| 1069 | 571 | ), |
| 1070 | 572 | array( |
| 1071 | 573 | 'name' => 'per_page', |
| 1072 | 574 | 'label' => __( 'Videos per Page', 'automatic-youtube-gallery' ), |
| 1073 | - 'description' => __( 'Enter the number of videos to show per page. Maximum of 50.', 'automatic-youtube-gallery' ), | |
| 575 | + 'description' => __( 'Enter the number of videos to show per page. Maximum of 50.', 'automatic-youtube-gallery' ), | |
| 1074 | 576 | 'type' => 'number', |
| 1075 | 577 | 'min' => 0, |
| 1076 | 578 | 'max' => 50, |
| 1077 | 579 | 'value' => $gallery_settings['per_page'], |
| @@ -1080,13 +582,12 @@ | ||
| 1080 | 582 | array( |
| 1081 | 583 | 'name' => 'thumb_ratio', |
| 1082 | 584 | 'label' => __( 'Image Height (Ratio)', 'automatic-youtube-gallery' ), |
| 1083 | 585 | 'description' => __( 'Select the ratio value used to calculate the image height in the gallery thumbnails.', 'automatic-youtube-gallery' ), |
| 1084 | - 'type' => 'select', | |
| 586 | + 'type' => 'radio', | |
| 1085 | 587 | 'options' => array( |
| 1086 | - '56.25' => __( 'Standard (16:9) — Default', 'automatic-youtube-gallery' ), | |
| 1087 | - '177.78' => __( 'Shorts / Vertical (9:16)', 'automatic-youtube-gallery' ), | |
| 1088 | - '75' => __( 'Classic (4:3)', 'automatic-youtube-gallery' ) | |
| 588 | + '56.25' => '16:9', | |
| 589 | + '75' => '4:3' | |
| 1089 | 590 | ), |
| 1090 | 591 | 'value' => $gallery_settings['thumb_ratio'], |
| 1091 | 592 | 'sanitize_callback' => 'floatval' |
| 1092 | 593 | ), |
| @@ -1144,8 +645,32 @@ | ||
| 1144 | 645 | |
| 1145 | 646 | ), |
| 1146 | 647 | 'value' => $gallery_settings['pagination_type'], |
| 1147 | 648 | 'sanitize_callback' => 'sanitize_key' |
| 649 | + ), | |
| 650 | + array( | |
| 651 | + 'name' => 'more_button_label', | |
| 652 | + 'label' => __( 'More Button Label', 'automatic-youtube-gallery' ), | |
| 653 | + 'description' => __( 'Enter the more button label text.', 'automatic-youtube-gallery' ), | |
| 654 | + 'type' => 'text', | |
| 655 | + 'value' => isset( $gallery_settings['more_button_label'] ) ? $gallery_settings['more_button_label'] : __( 'Load More', 'automatic-youtube-gallery' ), | |
| 656 | + 'sanitize_callback' => 'sanitize_text_field' | |
| 657 | + ), | |
| 658 | + array( | |
| 659 | + 'name' => 'previous_button_label', | |
| 660 | + 'label' => __( 'Previous Button Label', 'automatic-youtube-gallery' ), | |
| 661 | + 'description' => __( 'Enter the previous button label text.', 'automatic-youtube-gallery' ), | |
| 662 | + 'type' => 'text', | |
| 663 | + 'value' => isset( $gallery_settings['previous_button_label'] ) ? $gallery_settings['previous_button_label'] : __( 'Previous', 'automatic-youtube-gallery' ), | |
| 664 | + 'sanitize_callback' => 'sanitize_text_field' | |
| 665 | + ), | |
| 666 | + array( | |
| 667 | + 'name' => 'next_button_label', | |
| 668 | + 'label' => __( 'Next Button Label', 'automatic-youtube-gallery' ), | |
| 669 | + 'description' => __( 'Enter the next button label text.', 'automatic-youtube-gallery' ), | |
| 670 | + 'type' => 'text', | |
| 671 | + 'value' => isset( $gallery_settings['next_button_label'] ) ? $gallery_settings['next_button_label'] : __( 'Next', 'automatic-youtube-gallery' ), | |
| 672 | + 'sanitize_callback' => 'sanitize_text_field' | |
| 1148 | 673 | ) |
| 1149 | 674 | ); |
| 1150 | 675 | |
| 1151 | 676 | return apply_filters( 'ayg_gallery_settings_fields', $fields ); |
| @@ -1151,222 +676,30 @@ | ||
| 1151 | 676 | return apply_filters( 'ayg_gallery_settings_fields', $fields ); |
| 1152 | 677 | } |
| 1153 | 678 | |
| 1154 | 679 | /** |
| 1155 | - * Build the signature that ties a gallery UID to the source and cache duration it was rendered | |
| 1156 | - * with. | |
| 1157 | - * | |
| 1158 | - * A gallery UID is printed into the page, so it is not a secret and cannot be trusted on its own. | |
| 1159 | - * What this protects is the *pairing*: a request may only ask for the source that was rendered | |
| 1160 | - * alongside that UID, at the cache duration that gallery was configured with. | |
| 1161 | - * | |
| 1162 | - * It exists because a UID cannot always be recomputed. ayg_build_gallery() derives it as | |
| 1163 | - * md5( type + source ), but a gallery can override that through the "uid" shortcode attribute or | |
| 1164 | - * the ayg_gallery_id filter, and neither is knowable from the request alone. Signing the whole set | |
| 1165 | - * at render time lets the public AJAX endpoint accept such galleries without also having to accept | |
| 1166 | - * whatever source — or cache duration — the caller claims goes with them. | |
| 1167 | - * | |
| 1168 | - * Including the cache duration is what lets the "Cache Duration" gallery option keep working | |
| 1169 | - * exactly as the site owner set it, right down to "No Caching", while still making it impossible | |
| 1170 | - * for a visitor to bypass the cache and force live API requests. | |
| 1171 | - * | |
| 1172 | - * Each part is length prefixed so that no two different sets of values can produce the same | |
| 1173 | - * string to sign. | |
| 1174 | - * | |
| 1175 | - * @since 2.9.0 | |
| 1176 | - * @param string $uid Gallery UID. | |
| 1177 | - * @param string $type Gallery source type. | |
| 1178 | - * @param string $src Gallery source value. | |
| 1179 | - * @param string|int $cache Gallery cache duration in seconds. | |
| 1180 | - * @return string Signature. | |
| 1181 | - */ | |
| 1182 | -function ayg_get_gallery_signature( $uid, $type, $src, $cache ) { | |
| 1183 | - $parts = array( (string) $uid, (string) $type, (string) $src, (string) (int) $cache ); | |
| 1184 | - $data = 'ayg_gallery'; | |
| 1185 | - | |
| 1186 | - foreach ( $parts as $part ) { | |
| 1187 | - $data .= '|' . strlen( $part ) . ':' . $part; | |
| 1188 | - } | |
| 1189 | - | |
| 1190 | - return substr( wp_hash( $data ), 0, 16 ); | |
| 1191 | -} | |
| 1192 | - | |
| 1193 | -/** | |
| 1194 | - * Resolve a saved gallery's source type and value. | |
| 1195 | - * | |
| 1196 | - * Livestream, search and single video galleries query the YouTube API at display time — nothing | |
| 1197 | - * is imported for them — so they keep their own source type. Everything else is served from the | |
| 1198 | - * custom tables through the internal "db" source type, keyed by the gallery ID. | |
| 1199 | - * | |
| 1200 | - * Both the renderer (ayg_build_gallery) and the public AJAX endpoint resolve a saved gallery's | |
| 1201 | - * source through this function, so an untrusted request can never point a gallery at a source | |
| 1202 | - * other than the one stored in its own row. | |
| 1203 | - * | |
| 1204 | - * @since 2.9.0 | |
| 1205 | - * @param object $gallery Gallery row from "{$wpdb->prefix}ayg_galleries". | |
| 1206 | - * @return array Source "type", the matching attribute name in "field", and the | |
| 1207 | - * source value in "src". | |
| 1208 | - */ | |
| 1209 | -function ayg_get_gallery_source( $gallery ) { | |
| 1210 | - $source_type = isset( $gallery->source_type ) ? $gallery->source_type : ''; | |
| 1211 | - $source_value = isset( $gallery->source_value ) ? $gallery->source_value : ''; | |
| 1212 | - | |
| 1213 | - if ( 'livestream' === $source_type ) { | |
| 1214 | - return array( | |
| 1215 | - 'type' => 'livestream', | |
| 1216 | - 'field' => 'channel', | |
| 1217 | - 'src' => $source_value | |
| 1218 | - ); | |
| 1219 | - } | |
| 1220 | - | |
| 1221 | - if ( 'search' === $source_type ) { | |
| 1222 | - return array( | |
| 1223 | - 'type' => 'search', | |
| 1224 | - 'field' => 'search', | |
| 1225 | - 'src' => $source_value | |
| 1226 | - ); | |
| 1227 | - } | |
| 1228 | - | |
| 1229 | - if ( 'video' === $source_type ) { | |
| 1230 | - return array( | |
| 1231 | - 'type' => 'video', | |
| 1232 | - 'field' => 'video', | |
| 1233 | - 'src' => $source_value | |
| 1234 | - ); | |
| 1235 | - } | |
| 1236 | - | |
| 1237 | - return array( | |
| 1238 | - 'type' => 'db', | |
| 1239 | - 'field' => 'db', | |
| 1240 | - 'src' => strval( $gallery->id ) | |
| 1241 | - ); | |
| 1242 | -} | |
| 1243 | - | |
| 1244 | -/** | |
| 1245 | - * Return the schedule interval options for import scheduling. | |
| 1246 | - * | |
| 1247 | - * @since 2.8.0 | |
| 1248 | - * @return array Value (seconds as string) => label pairs. | |
| 1249 | - */ | |
| 1250 | -function ayg_get_import_schedule_options() { | |
| 1251 | - return array( | |
| 1252 | - 'paused' => __( '— Pause Automatic Imports —', 'automatic-youtube-gallery' ), | |
| 1253 | - '0' => __( 'Only Once', 'automatic-youtube-gallery' ), | |
| 1254 | - '3600' => __( 'Every Hour', 'automatic-youtube-gallery' ), | |
| 1255 | - '86400' => __( 'Every Day', 'automatic-youtube-gallery' ), | |
| 1256 | - '604800' => __( 'Every Week', 'automatic-youtube-gallery' ), | |
| 1257 | - '2592000' => __( 'Every Month', 'automatic-youtube-gallery' ) | |
| 1258 | - ); | |
| 1259 | -} | |
| 1260 | - | |
| 1261 | -/** | |
| 1262 | - * Return the human-readable label for an import status key. | |
| 1263 | - * | |
| 1264 | - * @since 2.8.0 | |
| 1265 | - * @param string $status Import status key (idle, running, paused, error, completed). | |
| 1266 | - * @param int|null $video_count Optional. Imported video count, used to resolve the | |
| 1267 | - * ambiguous "idle" status into "Pending Import" (nothing | |
| 1268 | - * imported yet) or "Up to Date" (waiting for the next sync). | |
| 1269 | - * @param bool $resuming Optional. True when an import was capped mid-run and parked | |
| 1270 | - * as 'idle' for cron to resume — shown as "Importing", not idle. | |
| 1271 | - * @param string $context Optional. 'log' for a per-run history entry, where a successful | |
| 1272 | - * run (stored as 'idle' for recurring galleries) reads "Completed" | |
| 1273 | - * like non-recurring runs, instead of the live-state "Idle". | |
| 1274 | - * @return string | |
| 1275 | - */ | |
| 1276 | -function ayg_get_import_status_label( $status, $video_count = null, $resuming = false, $context = '' ) { | |
| 1277 | - if ( 'idle' === $status ) { | |
| 1278 | - // History rows record a finished run: a recurring gallery's success is stored as 'idle', | |
| 1279 | - // so surface it as "Completed" to match non-recurring runs rather than the live "Idle". | |
| 1280 | - if ( 'log' === $context ) { | |
| 1281 | - return __( 'Completed', 'automatic-youtube-gallery' ); | |
| 1282 | - } | |
| 1283 | - | |
| 1284 | - // If the import was capped mid-run and parked as 'idle' for cron to resume, show "Importing". | |
| 1285 | - if ( $resuming ) { | |
| 1286 | - return __( 'Importing', 'automatic-youtube-gallery' ); | |
| 1287 | - } | |
| 1288 | - | |
| 1289 | - // If the import has never run, show "Pending Import" instead of "Idle". | |
| 1290 | - if ( null !== $video_count ) { | |
| 1291 | - return ( (int) $video_count > 0 ) ? __( 'Up to Date', 'automatic-youtube-gallery' ) : __( 'Pending Import', 'automatic-youtube-gallery' ); | |
| 1292 | - } | |
| 1293 | - } | |
| 1294 | - | |
| 1295 | - $statuses = array( | |
| 1296 | - 'idle' => __( 'Idle', 'automatic-youtube-gallery' ), | |
| 1297 | - 'running' => __( 'Importing', 'automatic-youtube-gallery' ), | |
| 1298 | - 'paused' => __( 'Paused', 'automatic-youtube-gallery' ), | |
| 1299 | - 'error' => __( 'Error', 'automatic-youtube-gallery' ), | |
| 1300 | - 'completed' => __( 'Completed', 'automatic-youtube-gallery' ) | |
| 1301 | - ); | |
| 1302 | - | |
| 1303 | - return isset( $statuses[ $status ] ) ? $statuses[ $status ] : ucfirst( $status ); | |
| 1304 | -} | |
| 1305 | - | |
| 1306 | -/** | |
| 1307 | - * Retrieve a plugin option with fallback to default settings. | |
| 1308 | - * | |
| 1309 | - * @since 2.7.0 | |
| 1310 | - * @param string $option The option name to retrieve. | |
| 1311 | - * @return mixed | |
| 1312 | - */ | |
| 1313 | -function ayg_get_option( $option ) { | |
| 1314 | - $defaults = ayg_get_default_settings(); | |
| 1315 | - $default = isset( $defaults[ $option ] ) ? $defaults[ $option ] : array(); | |
| 1316 | - | |
| 1317 | - $saved = get_option( $option, null ); | |
| 1318 | - | |
| 1319 | - // Option does not exist OR corrupted | |
| 1320 | - if ( null === $saved || ! is_array( $saved ) ) { | |
| 1321 | - return $default; | |
| 1322 | - } | |
| 1323 | - | |
| 1324 | - // Merge saved values with defaults | |
| 1325 | - return wp_parse_args( $saved, $default ); | |
| 1326 | -} | |
| 1327 | - | |
| 1328 | -/** | |
| 1329 | - * Build the signature for a pagination token. | |
| 1330 | - * | |
| 1331 | - * Keyed on the site's own salts through wp_hash(), and bound to the gallery so a token issued for | |
| 1332 | - * one gallery cannot be replayed against another. Unlike a nonce this never expires, so tokens | |
| 1333 | - * printed into a page that is held by a full page cache keep working. | |
| 1334 | - * | |
| 1335 | - * @since 2.9.0 | |
| 1336 | - * @param string $token Raw page token. | |
| 1337 | - * @param string $uid Gallery UID the token belongs to. | |
| 1338 | - * @return string Signature. | |
| 1339 | - */ | |
| 1340 | -function ayg_get_page_token_signature( $token, $uid ) { | |
| 1341 | - return substr( wp_hash( 'ayg_page_token|' . (string) $uid . '|' . (string) $token ), 0, 16 ); | |
| 1342 | -} | |
| 1343 | - | |
| 1344 | -/** | |
| 1345 | 680 | * Get video description to show on top of the player. |
| 1346 | 681 | * |
| 1347 | 682 | * @since 1.0.0 |
| 1348 | 683 | * @param stdClass $video YouTube video object. |
| 1349 | - * @param array $attributes Array of user attributes. | |
| 1350 | 684 | * @param int $words_count Number of words to show by default. |
| 1351 | 685 | * @return string Video description. |
| 1352 | 686 | */ |
| 1353 | -function ayg_get_player_description( $video, $attributes = array(), $words_count = 30 ) { | |
| 687 | +function ayg_get_player_description( $video, $words_count = 30 ) { | |
| 1354 | 688 | $description = $video->description; |
| 1355 | 689 | |
| 1356 | 690 | $words_array = explode( ' ', strip_tags( $description ) ); |
| 1357 | 691 | if ( count( $words_array ) > $words_count ) { |
| 1358 | - $show_more_label = ! empty( $attributes['show_more_label'] ) ? $attributes['show_more_label'] : __( 'Show More', 'automatic-youtube-gallery' ); | |
| 1359 | 692 | $words_array[ $words_count ] = '<span class="ayg-player-description-dots">...</span></span><span class="ayg-player-description-more">' . $words_array[ $words_count ]; |
| 1360 | 693 | |
| 1361 | 694 | $description = '<span class="ayg-player-description-less">' . implode( ' ', $words_array ) . '</span>'; |
| 1362 | - $description .= '<a href="#" class="ayg-player-description-toggle-btn">' . esc_html( $show_more_label ) . '</a>'; | |
| 695 | + $description .= '<a href="#" class="ayg-player-description-toggle-btn">[+] ' . __( 'Show More', 'automatic-youtube-gallery' ) . '</a>'; | |
| 1363 | 696 | } |
| 1364 | 697 | |
| 1365 | 698 | $description = nl2br( $description ); |
| 1366 | 699 | $description = make_clickable( $description ); |
| 1367 | 700 | |
| 1368 | - return apply_filters( 'ayg_player_description', $description, $video, $attributes, $words_count ); | |
| 701 | + return apply_filters( 'ayg_player_description', $description, $video, $words_count ); | |
| 1369 | 702 | } |
| 1370 | 703 | |
| 1371 | 704 | /** |
| 1372 | 705 | * Get player settings fields. |
| @@ -1374,28 +707,19 @@ | ||
| 1374 | 707 | * @since 1.0.0 |
| 1375 | 708 | * @return array $fields Array of fields. |
| 1376 | 709 | */ |
| 1377 | 710 | function ayg_get_player_settings_fields() { |
| 1378 | - $player_settings = ayg_get_option( 'ayg_player_settings' ); | |
| 711 | + $player_settings = get_option( 'ayg_player_settings' ); | |
| 1379 | 712 | |
| 1380 | 713 | $fields = array( |
| 1381 | 714 | array( |
| 1382 | - 'name' => 'player_width', | |
| 1383 | - 'label' => __( 'Player Width', 'automatic-youtube-gallery' ), | |
| 1384 | - 'description' => __( 'In pixels. Maximum width of the player. Leave this field empty to scale 100% of its enclosing container/html element.', 'automatic-youtube-gallery' ), | |
| 1385 | - 'type' => 'text', | |
| 1386 | - 'value' => isset( $player_settings['player_width'] ) ? $player_settings['player_width'] : '', | |
| 1387 | - 'sanitize_callback' => 'ayg_sanitize_int' | |
| 1388 | - ), | |
| 1389 | - array( | |
| 1390 | 715 | 'name' => 'player_ratio', |
| 1391 | 716 | 'label' => __( 'Player Height (Ratio)', 'automatic-youtube-gallery' ), |
| 1392 | 717 | 'description' => __( 'Select the ratio value used to calculate the player height.', 'automatic-youtube-gallery' ), |
| 1393 | - 'type' => 'select', | |
| 718 | + 'type' => 'radio', | |
| 1394 | 719 | 'options' => array( |
| 1395 | - '56.25' => __( 'Standard (16:9) — Default', 'automatic-youtube-gallery' ), | |
| 1396 | - '177.78' => __( 'Shorts / Vertical (9:16)', 'automatic-youtube-gallery' ), | |
| 1397 | - '75' => __( 'Classic (4:3)', 'automatic-youtube-gallery' ) | |
| 720 | + '56.25' => '16:9', | |
| 721 | + '75' => '4:3' | |
| 1398 | 722 | ), |
| 1399 | 723 | 'value' => $player_settings['player_ratio'], |
| 1400 | 724 | 'sanitize_callback' => 'floatval' |
| 1401 | 725 | ), |
| @@ -1417,9 +741,9 @@ | ||
| 1417 | 741 | ), |
| 1418 | 742 | array( |
| 1419 | 743 | 'name' => 'autoplay', |
| 1420 | 744 | 'label' => __( 'Autoplay', 'automatic-youtube-gallery' ), |
| 1421 | - 'description' => __( 'Check this option to automatically start playing the initial video when the player loads.', 'automatic-youtube-gallery' ), | |
| 745 | + 'description' => __( 'Specifies whether the initial video will automatically start to play when the player loads.', 'automatic-youtube-gallery' ), | |
| 1422 | 746 | 'type' => 'checkbox', |
| 1423 | 747 | 'value' => $player_settings['autoplay'], |
| 1424 | 748 | 'sanitize_callback' => 'intval' |
| 1425 | 749 | ), |
| @@ -1425,9 +749,9 @@ | ||
| 1425 | 749 | ), |
| 1426 | 750 | array( |
| 1427 | 751 | 'name' => 'autoadvance', |
| 1428 | 752 | 'label' => __( 'Autoplay Next Video', 'automatic-youtube-gallery' ), |
| 1429 | - 'description' => __( 'Check this option to automatically play the next video in the list after the previous one ends.', 'automatic-youtube-gallery' ), | |
| 753 | + 'description' => __( 'Specifies whether to play the next video in the list automatically after previous one end.', 'automatic-youtube-gallery' ), | |
| 1430 | 754 | 'type' => 'checkbox', |
| 1431 | 755 | 'value' => $player_settings['autoadvance'], |
| 1432 | 756 | 'sanitize_callback' => 'intval' |
| 1433 | 757 | ), |
| @@ -1433,9 +757,9 @@ | ||
| 1433 | 757 | ), |
| 1434 | 758 | array( |
| 1435 | 759 | 'name' => 'loop', |
| 1436 | 760 | 'label' => __( 'Loop', 'automatic-youtube-gallery' ), |
| 1437 | - 'description' => __( 'Check this option to loop playback. In the case of a single video player, plays the initial video again and again. In the case of a gallery, plays the entire list and then starts again at the first video.', 'automatic-youtube-gallery' ), | |
| 761 | + 'description' => __( 'In the case of a single video player, plays the initial video again and again. In the case of a gallery, plays the entire list in the gallery and then starts again at the first video.', 'automatic-youtube-gallery' ), | |
| 1438 | 762 | 'type' => 'checkbox', |
| 1439 | 763 | 'value' => $player_settings['loop'], |
| 1440 | 764 | 'sanitize_callback' => 'intval' |
| 1441 | 765 | ), |
| @@ -1457,9 +781,9 @@ | ||
| 1457 | 781 | ), |
| 1458 | 782 | array( |
| 1459 | 783 | 'name' => 'modestbranding', |
| 1460 | 784 | 'label' => __( 'Hide YouTube Logo', 'automatic-youtube-gallery' ), |
| 1461 | - 'description' => __( "Check this option to prevent the YouTube logo from displaying in the control bar. Note that a small YouTube text label will still display in the upper-right corner of a paused video when the user's mouse pointer hovers over the player.", 'automatic-youtube-gallery' ), | |
| 785 | + 'description' => __( "Lets you prevent the YouTube logo from displaying in the control bar. Note that a small YouTube text label will still display in the upper-right corner of a paused video when the user's mouse pointer hovers over the player.", 'automatic-youtube-gallery' ), | |
| 1462 | 786 | 'type' => 'checkbox', |
| 1463 | 787 | 'value' => $player_settings['modestbranding'], |
| 1464 | 788 | 'sanitize_callback' => 'intval' |
| 1465 | 789 | ), |
| @@ -1465,12 +789,20 @@ | ||
| 1465 | 789 | ), |
| 1466 | 790 | array( |
| 1467 | 791 | 'name' => 'cc_load_policy', |
| 1468 | 792 | 'label' => __( 'Force Closed Captions', 'automatic-youtube-gallery' ), |
| 1469 | - 'description' => __( 'Check this option to show captions by default, even if the user has turned captions off. The default behavior is based on user preference.', 'automatic-youtube-gallery' ), | |
| 793 | + 'description' => __( 'Show captions by default, even if the user has turned captions off. The default behavior is based on user preference.', 'automatic-youtube-gallery' ), | |
| 1470 | 794 | 'type' => 'checkbox', |
| 1471 | 795 | 'value' => $player_settings['cc_load_policy'], |
| 1472 | 796 | 'sanitize_callback' => 'intval' |
| 797 | + ), | |
| 798 | + array( | |
| 799 | + 'name' => 'iv_load_policy', | |
| 800 | + 'label' => __( 'Show Annotations', 'automatic-youtube-gallery' ), | |
| 801 | + 'description' => __( 'Choose whether to show annotations or not.', 'automatic-youtube-gallery' ), | |
| 802 | + 'type' => 'checkbox', | |
| 803 | + 'value' => $player_settings['iv_load_policy'], | |
| 804 | + 'sanitize_callback' => 'intval' | |
| 1473 | 805 | ), |
| 1474 | 806 | array( |
| 1475 | 807 | 'name' => 'hl', |
| 1476 | 808 | 'label' => __( 'Player Language', 'automatic-youtube-gallery' ), |
| @@ -1491,9 +823,25 @@ | ||
| 1491 | 823 | ), |
| 1492 | 824 | 'type' => 'text', |
| 1493 | 825 | 'value' => $player_settings['cc_lang_pref'], |
| 1494 | 826 | 'sanitize_callback' => 'sanitize_text_field' |
| 1495 | - ) | |
| 827 | + ), | |
| 828 | + array( | |
| 829 | + 'name' => 'privacy_enhanced_mode', | |
| 830 | + 'label' => __( 'Privacy Enhanced Mode', 'automatic-youtube-gallery' ), | |
| 831 | + 'description' => __( "Prevent YouTube from leaving tracking cookies on your visitor's browsers unless they actually play the videos. Please uncheck this option if you see errors while testing your playlist embeds or watching your videos on mobile.", 'automatic-youtube-gallery' ), | |
| 832 | + 'type' => 'checkbox', | |
| 833 | + 'value' => isset( $player_settings['privacy_enhanced_mode'] ) ? $player_settings['privacy_enhanced_mode'] : 0, | |
| 834 | + 'sanitize_callback' => 'intval' | |
| 835 | + ), | |
| 836 | + array( | |
| 837 | + 'name' => 'origin', | |
| 838 | + 'label' => __( 'Extra Player Security', 'automatic-youtube-gallery' ), | |
| 839 | + 'description' => __( 'Add site origin information with each embed code as an extra security measure. In YouTube\'s own words, checking this option "protects against malicious third-party JavaScript being injected into your page and hijacking control of your YouTube player."', 'automatic-youtube-gallery' ), | |
| 840 | + 'type' => 'checkbox', | |
| 841 | + 'value' => isset( $player_settings['origin'] ) ? $player_settings['origin'] : 0, | |
| 842 | + 'sanitize_callback' => 'intval' | |
| 843 | + ), | |
| 1496 | 844 | ); |
| 1497 | 845 | |
| 1498 | 846 | return $fields; |
| 1499 | 847 | } |
| @@ -1511,38 +859,8 @@ | ||
| 1511 | 859 | return apply_filters( 'ayg_single_video_url', '', $video, $attributes ); |
| 1512 | 860 | } |
| 1513 | 861 | |
| 1514 | 862 | /** |
| 1515 | - * Return the human-readable label for a source type key. | |
| 1516 | - * | |
| 1517 | - * @since 2.8.0 | |
| 1518 | - * @param string $type Source type key (channel, playlist, username, search, livestream, video, videos). | |
| 1519 | - * @return string | |
| 1520 | - */ | |
| 1521 | -function ayg_get_source_type_label( $type ) { | |
| 1522 | - $types = ayg_get_source_types(); | |
| 1523 | - return isset( $types[ $type ] ) ? $types[ $type ] : ucfirst( $type ); | |
| 1524 | -} | |
| 1525 | - | |
| 1526 | -/** | |
| 1527 | - * Get source types. | |
| 1528 | - * | |
| 1529 | - * @since 2.8.0 | |
| 1530 | - * @return array | |
| 1531 | - */ | |
| 1532 | -function ayg_get_source_types() { | |
| 1533 | - return array( | |
| 1534 | - 'channel' => __( 'Channel', 'automatic-youtube-gallery' ), | |
| 1535 | - 'playlist' => __( 'Playlist', 'automatic-youtube-gallery' ), | |
| 1536 | - 'username' => __( 'Username', 'automatic-youtube-gallery' ), | |
| 1537 | - 'search' => __( 'Search Keywords', 'automatic-youtube-gallery' ), | |
| 1538 | - 'livestream' => __( 'Livestream', 'automatic-youtube-gallery' ), | |
| 1539 | - 'video' => __( 'Single Video', 'automatic-youtube-gallery' ), | |
| 1540 | - 'videos' => __( 'Custom Videos List', 'automatic-youtube-gallery' ) | |
| 1541 | - ); | |
| 1542 | -} | |
| 1543 | - | |
| 1544 | -/** | |
| 1545 | 863 | * Get filtered php template file path. |
| 1546 | 864 | * |
| 1547 | 865 | * @since 1.0.0 |
| 1548 | 866 | * @param array $template PHP file path. |
| @@ -1575,9 +893,9 @@ | ||
| 1575 | 893 | * @since 2.3.0 |
| 1576 | 894 | * @return string YouTube embed domain. |
| 1577 | 895 | */ |
| 1578 | 896 | function ayg_get_youtube_domain() { |
| 1579 | - $player_settings = ayg_get_option( 'ayg_player_settings' ); | |
| 897 | + $player_settings = get_option( 'ayg_player_settings' ); | |
| 1580 | 898 | |
| 1581 | 899 | $domain = 'https://www.youtube.com'; |
| 1582 | 900 | if ( isset( $player_settings['privacy_enhanced_mode'] ) && ! empty( $player_settings['privacy_enhanced_mode'] ) ) { |
| 1583 | 901 | $domain = 'https://www.youtube-nocookie.com'; |
| @@ -1586,110 +904,8 @@ | ||
| 1586 | 904 | return $domain; |
| 1587 | 905 | } |
| 1588 | 906 | |
| 1589 | 907 | /** |
| 1590 | - * Get the YouTube embed URL. | |
| 1591 | - * | |
| 1592 | - * @since 2.5.0 | |
| 1593 | - * @param string $video_id YouTube video ID. | |
| 1594 | - * @param array $attributes Array of user attributes. | |
| 1595 | - * @return string Player embed URL. | |
| 1596 | - */ | |
| 1597 | -function ayg_get_youtube_embed_url( $video_id, $attributes = array() ) { | |
| 1598 | - $player_settings = ayg_get_option( 'ayg_player_settings' ); | |
| 1599 | - | |
| 1600 | - $player_website = 'https://www.youtube.com'; | |
| 1601 | - if ( isset( $player_settings['privacy_enhanced_mode'] ) && ! empty( $player_settings['privacy_enhanced_mode'] ) ) { | |
| 1602 | - $player_website = 'https://www.youtube-nocookie.com'; | |
| 1603 | - } | |
| 1604 | - | |
| 1605 | - if ( empty( $video_id ) ) { | |
| 1606 | - return ''; | |
| 1607 | - } | |
| 1608 | - | |
| 1609 | - $url = $player_website . '/embed/' . $video_id . '?enablejsapi=1&playsinline=1&rel=0'; | |
| 1610 | - | |
| 1611 | - if ( isset( $player_settings['origin'] ) && ! empty( $player_settings['origin'] ) ) { | |
| 1612 | - $site_url_parts = parse_url( site_url() ); | |
| 1613 | - $origin = $site_url_parts['scheme'] . '://' . $site_url_parts['host']; | |
| 1614 | - | |
| 1615 | - $url = add_query_arg( 'origin', $origin, $url ); | |
| 1616 | - } | |
| 1617 | - | |
| 1618 | - if ( ! is_array( $attributes ) ) { | |
| 1619 | - $attributes = (array) $attributes; | |
| 1620 | - } | |
| 1621 | - | |
| 1622 | - $autoplay = isset( $attributes['autoplay'] ) ? (int) $attributes['autoplay'] : 0; | |
| 1623 | - if ( 1 == $autoplay ) { | |
| 1624 | - $url = add_query_arg( 'autoplay', 1, $url ); | |
| 1625 | - } | |
| 1626 | - | |
| 1627 | - $loop = isset( $attributes['loop'] ) ? (int) $attributes['loop'] : 0; | |
| 1628 | - if ( 1 == $loop ) { | |
| 1629 | - $url = add_query_arg( 'playlist', $video_id, $url ); | |
| 1630 | - $url = add_query_arg( 'loop', 1, $url ); | |
| 1631 | - } | |
| 1632 | - | |
| 1633 | - $muted = isset( $attributes['muted'] ) ? (int) $attributes['muted'] : 0; | |
| 1634 | - if ( 1 == $muted ) { | |
| 1635 | - $url = add_query_arg( 'mute', 1, $url ); | |
| 1636 | - } | |
| 1637 | - | |
| 1638 | - $controls = isset( $attributes['controls'] ) ? (int) $attributes['controls'] : 1; | |
| 1639 | - if ( 0 == $controls ) { | |
| 1640 | - $url = add_query_arg( 'controls', 0, $url ); | |
| 1641 | - } | |
| 1642 | - | |
| 1643 | - $modestbranding = isset( $attributes['modestbranding'] ) ? (int) $attributes['modestbranding'] : 0; | |
| 1644 | - if ( 1 == $modestbranding ) { | |
| 1645 | - $url = add_query_arg( 'modestbranding', 1, $url ); | |
| 1646 | - } | |
| 1647 | - | |
| 1648 | - $cc_load_policy = isset( $attributes['cc_load_policy'] ) ? (int) $attributes['cc_load_policy'] : 0; | |
| 1649 | - if ( 1 == $cc_load_policy ) { | |
| 1650 | - $url = add_query_arg( 'cc_load_policy', 1, $url ); | |
| 1651 | - } | |
| 1652 | - | |
| 1653 | - if ( isset( $attributes['hl'] ) && ! empty( $attributes['hl'] ) ) { | |
| 1654 | - $url = add_query_arg( 'hl', sanitize_text_field( $attributes['hl'] ), $url ); | |
| 1655 | - } | |
| 1656 | - | |
| 1657 | - if ( isset( $attributes['cc_lang_pref'] ) && ! empty( $attributes['cc_lang_pref'] ) ) { | |
| 1658 | - $url = add_query_arg( 'cc_lang_pref', sanitize_text_field( $attributes['cc_lang_pref'] ), $url ); | |
| 1659 | - } | |
| 1660 | - | |
| 1661 | - return apply_filters( 'ayg_youtube_embed_url', $url, $video_id, $attributes ); | |
| 1662 | -} | |
| 1663 | - | |
| 1664 | -/** | |
| 1665 | - * Resolve a YouTube video ID from a bare ID or a YouTube URL. | |
| 1666 | - * | |
| 1667 | - * @since 2.8.0 | |
| 1668 | - * @param string $string A video ID or YouTube URL. | |
| 1669 | - * @return string The 11-char video ID, or '' if none found. | |
| 1670 | - */ | |
| 1671 | -function ayg_get_youtube_video_id( $string ) { | |
| 1672 | - $string = trim( (string) $string ); | |
| 1673 | - | |
| 1674 | - if ( '' === $string ) { | |
| 1675 | - return ''; | |
| 1676 | - } | |
| 1677 | - | |
| 1678 | - // Already a bare video ID. | |
| 1679 | - if ( preg_match( '~^[A-Za-z0-9_-]{11}$~', $string ) ) { | |
| 1680 | - return $string; | |
| 1681 | - } | |
| 1682 | - | |
| 1683 | - // Common YouTube URL forms. | |
| 1684 | - if ( preg_match( '~(?:v=|/(?:embed|v|shorts|live)/|youtu\.be/)([A-Za-z0-9_-]{11})~', $string, $matches ) ) { | |
| 1685 | - return $matches[1]; | |
| 1686 | - } | |
| 1687 | - | |
| 1688 | - return ''; | |
| 1689 | -} | |
| 1690 | - | |
| 1691 | -/** | |
| 1692 | 908 | * Inserts a new associative array after another associative array key. |
| 1693 | 909 | * |
| 1694 | 910 | * @since 2.1.0 |
| 1695 | 911 | * @param string $key The associative array key to insert after. |
| @@ -1717,141 +933,8 @@ | ||
| 1717 | 933 | return $array; |
| 1718 | 934 | } |
| 1719 | 935 | |
| 1720 | 936 | /** |
| 1721 | - * Detect if the client is using an iOS device (iPhone, iPad, or iPod). | |
| 1722 | - * | |
| 1723 | - * @return bool True if the user agent string suggests an iOS device, false otherwise. | |
| 1724 | - */ | |
| 1725 | -function ayg_is_ios() { | |
| 1726 | - if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) { | |
| 1727 | - return false; | |
| 1728 | - } | |
| 1729 | - | |
| 1730 | - $ua = strtolower( $_SERVER['HTTP_USER_AGENT'] ); | |
| 1731 | - | |
| 1732 | - if ( | |
| 1733 | - strpos( $ua, 'iphone' ) !== false || | |
| 1734 | - strpos( $ua, 'ipad' ) !== false || | |
| 1735 | - strpos( $ua, 'ipod' ) !== false | |
| 1736 | - ) { | |
| 1737 | - return true; | |
| 1738 | - } | |
| 1739 | - | |
| 1740 | - return false; | |
| 1741 | -} | |
| 1742 | - | |
| 1743 | -/** | |
| 1744 | - * Check whether a video ID matches a gallery's exclude list. | |
| 1745 | - * | |
| 1746 | - * @since 2.8.0 | |
| 1747 | - * @param string $video_id YouTube video ID. | |
| 1748 | - * @param array $exclude Exclude entries (video IDs or URLs). | |
| 1749 | - * @return bool True if the video is excluded. | |
| 1750 | - */ | |
| 1751 | -function ayg_is_video_excluded( $video_id, $exclude ) { | |
| 1752 | - if ( empty( $video_id ) || empty( $exclude ) || ! is_array( $exclude ) ) { | |
| 1753 | - return false; | |
| 1754 | - } | |
| 1755 | - | |
| 1756 | - foreach ( $exclude as $entry ) { | |
| 1757 | - if ( false !== strpos( $entry, $video_id ) ) { | |
| 1758 | - return true; | |
| 1759 | - } | |
| 1760 | - } | |
| 1761 | - | |
| 1762 | - return false; | |
| 1763 | -} | |
| 1764 | - | |
| 1765 | -/** | |
| 1766 | - * Unserialize a stored value without letting it instantiate arbitrary classes. | |
| 1767 | - * | |
| 1768 | - * Our serialized columns only ever hold the thumbnail set from an API response — plain data plus | |
| 1769 | - * stdClass objects — so nothing else is allowed through. This keeps a row that was tampered with | |
| 1770 | - * or carried over from another install from building an object of some other class. | |
| 1771 | - * | |
| 1772 | - * @since 2.9.0 | |
| 1773 | - * @param string $value Serialized value. | |
| 1774 | - * @return mixed Unserialized value, or the value unchanged when it isn't serialized. | |
| 1775 | - */ | |
| 1776 | -function ayg_maybe_unserialize( $value ) { | |
| 1777 | - if ( ! is_serialized( $value ) ) { | |
| 1778 | - return $value; | |
| 1779 | - } | |
| 1780 | - | |
| 1781 | - // is_serialized() trims the value before testing it, so trim here as well — otherwise a padded | |
| 1782 | - // value passes the test and then fails to unserialize. Mirrors core's maybe_unserialize(). | |
| 1783 | - $value = trim( $value ); | |
| 1784 | - | |
| 1785 | - // PHP 7.0+ can restrict this natively. | |
| 1786 | - if ( version_compare( PHP_VERSION, '7.0', '>=' ) ) { | |
| 1787 | - return @unserialize( $value, array( 'allowed_classes' => array( 'stdClass' ) ) ); | |
| 1788 | - } | |
| 1789 | - | |
| 1790 | - // PHP 5.6 has no "allowed_classes" option, so screen the payload instead: "O:" introduces a | |
| 1791 | - // plain object and "C:" a Serializable one, so refuse the value outright if it names any class | |
| 1792 | - // other than stdClass. Same protection as the call above, without raising the minimum PHP | |
| 1793 | - // version and cutting those sites off from plugin updates. | |
| 1794 | - if ( preg_match_all( '/(?:^|[;{])([OC]):\d+:"([^"]*)"/', $value, $matches, PREG_SET_ORDER ) ) { | |
| 1795 | - foreach ( $matches as $match ) { | |
| 1796 | - if ( 'O' !== $match[1] || 'stdClass' !== $match[2] ) { | |
| 1797 | - return false; | |
| 1798 | - } | |
| 1799 | - } | |
| 1800 | - } | |
| 1801 | - | |
| 1802 | - return @unserialize( $value ); | |
| 1803 | -} | |
| 1804 | - | |
| 1805 | -/** | |
| 1806 | - * Does this source type hand out signed pagination tokens? | |
| 1807 | - * | |
| 1808 | - * Only the sources that page through the live YouTube API do, because for those every distinct | |
| 1809 | - * page token means another API call. The rest either never reach the API at all (the Gallery | |
| 1810 | - * Builder "db" source) or page through a list already known to the site ("videos"), where the | |
| 1811 | - * page number is simply clamped to the real range and so cannot force extra calls. | |
| 1812 | - * | |
| 1813 | - * @since 2.9.0 | |
| 1814 | - * @param string $source_type Gallery source type. | |
| 1815 | - * @return bool True when tokens of this source type are signed. | |
| 1816 | - */ | |
| 1817 | -function ayg_page_token_is_signed( $source_type ) { | |
| 1818 | - return in_array( $source_type, array( 'playlist', 'channel', 'username', 'search' ), true ); | |
| 1819 | -} | |
| 1820 | - | |
| 1821 | -/** | |
| 1822 | - * Parse an ISO 8601 duration (e.g. PT4M13S) into total seconds. | |
| 1823 | - * | |
| 1824 | - * @since 2.8.0 | |
| 1825 | - * @param string $iso8601 ISO 8601 duration string from the YouTube API. | |
| 1826 | - * @return int Duration in seconds. | |
| 1827 | - */ | |
| 1828 | -function ayg_parse_duration_seconds( $iso8601 ) { | |
| 1829 | - if ( empty( $iso8601 ) ) { | |
| 1830 | - return 0; | |
| 1831 | - } | |
| 1832 | - | |
| 1833 | - try { | |
| 1834 | - $interval = new DateInterval( $iso8601 ); | |
| 1835 | - } catch ( Exception $e ) { | |
| 1836 | - return 0; | |
| 1837 | - } | |
| 1838 | - | |
| 1839 | - return ( $interval->d * DAY_IN_SECONDS ) + ( $interval->h * HOUR_IN_SECONDS ) + ( $interval->i * MINUTE_IN_SECONDS ) + $interval->s; | |
| 1840 | -} | |
| 1841 | - | |
| 1842 | -/** | |
| 1843 | - * Sanitize the array inputs. | |
| 1844 | - * | |
| 1845 | - * @since 2.7.0 | |
| 1846 | - * @param array $value Input array. | |
| 1847 | - * @return array Sanitized array. | |
| 1848 | - */ | |
| 1849 | -function ayg_sanitize_array( $value ) { | |
| 1850 | - return ! empty( $value ) ? array_map( 'sanitize_text_field', $value ) : array(); | |
| 1851 | -} | |
| 1852 | - | |
| 1853 | -/** | |
| 1854 | 937 | * Sanitize the integer inputs, accepts empty values. |
| 1855 | 938 | * |
| 1856 | 939 | * @since 1.0.0 |
| 1857 | 940 | * @param string|int $value Input value. |
| @@ -1862,49 +945,8 @@ | ||
| 1862 | 945 | return ( 0 == $value ) ? '' : $value; |
| 1863 | 946 | } |
| 1864 | 947 | |
| 1865 | 948 | /** |
| 1866 | - * Sign a pagination token before it is handed to the browser. | |
| 1867 | - * | |
| 1868 | - * Page tokens travel out to the visitor and are posted back to the public AJAX endpoint, where | |
| 1869 | - * each distinct token means another live call to the YouTube Data API. Signing them means only | |
| 1870 | - * the tokens this site actually issued are ever acted on, so a visitor cannot invent an endless | |
| 1871 | - * stream of new ones and burn through the site's daily API quota. | |
| 1872 | - * | |
| 1873 | - * @since 2.9.0 | |
| 1874 | - * @param string $token Raw page token from the API response. | |
| 1875 | - * @param string $uid Gallery UID the token belongs to. | |
| 1876 | - * @return string Signed token, or an empty string when there is no token. | |
| 1877 | - */ | |
| 1878 | -function ayg_sign_page_token( $token, $uid ) { | |
| 1879 | - $token = (string) $token; | |
| 1880 | - | |
| 1881 | - if ( '' === $token ) { | |
| 1882 | - return ''; | |
| 1883 | - } | |
| 1884 | - | |
| 1885 | - return $token . '.' . ayg_get_page_token_signature( $token, $uid ); | |
| 1886 | -} | |
| 1887 | - | |
| 1888 | -/** | |
| 1889 | - * Sign the pagination tokens in a page info array. | |
| 1890 | - * | |
| 1891 | - * @since 2.9.0 | |
| 1892 | - * @param array $page_info Page info array from the API response. | |
| 1893 | - * @param string $uid Gallery UID the tokens belong to. | |
| 1894 | - * @return array The same array with its tokens signed. | |
| 1895 | - */ | |
| 1896 | -function ayg_sign_page_tokens( $page_info, $uid ) { | |
| 1897 | - foreach ( array( 'next_page_token', 'prev_page_token' ) as $key ) { | |
| 1898 | - if ( ! empty( $page_info[ $key ] ) ) { | |
| 1899 | - $page_info[ $key ] = ayg_sign_page_token( $page_info[ $key ], $uid ); | |
| 1900 | - } | |
| 1901 | - } | |
| 1902 | - | |
| 1903 | - return $page_info; | |
| 1904 | -} | |
| 1905 | - | |
| 1906 | -/** | |
| 1907 | 949 | * Trims text to a certain number of characters. |
| 1908 | 950 | * |
| 1909 | 951 | * @since 2.0.0 |
| 1910 | 952 | * @param string $text Text to trim. |
| @@ -1931,51 +973,17 @@ | ||
| 1931 | 973 | |
| 1932 | 974 | $text .= $append; |
| 1933 | 975 | } |
| 1934 | 976 | |
| 1935 | - return apply_filters( 'ayg_trim_words', $text, $original_text, $num_characters, $append ); | |
| 977 | + return apply_filters( 'ayg_trim_words', $text, $original_text, $num_characters, $append ); | |
| 1936 | 978 | } |
| 1937 | 979 | |
| 1938 | 980 | /** |
| 1939 | - * Verify a pagination token that came back from the browser. | |
| 1940 | - * | |
| 1941 | - * @since 2.9.0 | |
| 1942 | - * @param string $token Signed token as posted by the browser. | |
| 1943 | - * @param string $uid Gallery UID the token is claimed to belong to. | |
| 1944 | - * @return string|false The raw token when the signature matches, false when it does not. An | |
| 1945 | - * empty token is the first page and needs no signature. | |
| 1946 | - */ | |
| 1947 | -function ayg_verify_page_token( $token, $uid ) { | |
| 1948 | - $token = (string) $token; | |
| 1949 | - | |
| 1950 | - if ( '' === $token ) { | |
| 1951 | - return ''; | |
| 1952 | - } | |
| 1953 | - | |
| 1954 | - // The signature is appended last, and a YouTube page token can itself contain almost anything, | |
| 1955 | - // so split on the final separator rather than the first. | |
| 1956 | - $position = strrpos( $token, '.' ); | |
| 1957 | - | |
| 1958 | - if ( false === $position ) { | |
| 1959 | - return false; | |
| 1960 | - } | |
| 1961 | - | |
| 1962 | - $raw = substr( $token, 0, $position ); | |
| 1963 | - $signature = substr( $token, $position + 1 ); | |
| 1964 | - | |
| 1965 | - if ( '' === $raw || ! hash_equals( ayg_get_page_token_signature( $raw, $uid ), $signature ) ) { | |
| 1966 | - return false; | |
| 1967 | - } | |
| 1968 | - | |
| 1969 | - return $raw; | |
| 1970 | -} | |
| 1971 | - | |
| 1972 | -/** | |
| 1973 | 981 | * Gallery HTML output. |
| 1974 | 982 | * |
| 1975 | - * @since 1.0.0 | |
| 1976 | - * @param array $video YouTube video object. | |
| 1977 | - * @param array $attributes Array of user attributes. | |
| 983 | + * @since 1.0.0 | |
| 984 | + * @param array $video YouTube video object. | |
| 985 | + * @param array $attributes Array of user attributes. | |
| 1978 | 986 | */ |
| 1979 | 987 | function the_ayg_gallery_thumbnail( $video, $attributes ) { |
| 1980 | 988 | include ayg_get_template( AYG_DIR . 'public/templates/thumbnail.php' ); |
| 1981 | 989 | } |
| @@ -1982,35 +990,12 @@ | ||
| 1982 | 990 | |
| 1983 | 991 | /** |
| 1984 | 992 | * Pagination HTML output. |
| 1985 | 993 | * |
| 1986 | - * @since 1.0.0 | |
| 1987 | - * @param array $attributes Array of user attributes. | |
| 994 | + * @since 1.0.0 | |
| 995 | + * @param array $attributes Array of user attributes. | |
| 1988 | 996 | */ |
| 1989 | 997 | function the_ayg_pagination( $attributes ) { |
| 1990 | 998 | if ( ! empty( $attributes['pagination'] ) ) { |
| 1991 | 999 | include ayg_get_template( AYG_DIR . 'public/templates/pagination.php' ); |
| 1992 | 1000 | } |
| 1993 | -} | |
| 1994 | - | |
| 1995 | -/** | |
| 1996 | - * Search Form HTML output. | |
| 1997 | - * | |
| 1998 | - * @since 2.5.7 | |
| 1999 | - * @param array $attributes Array of user attributes. | |
| 2000 | - */ | |
| 2001 | -function the_ayg_search_form( $attributes ) { | |
| 2002 | - if ( ! empty( $attributes['search_form'] ) ) { | |
| 2003 | - include ayg_get_template( AYG_DIR . 'public/templates/search-form.php' ); | |
| 2004 | - } | |
| 2005 | -} | |
| 2006 | - | |
| 2007 | -/** | |
| 2008 | - * Gallery HTML output. | |
| 2009 | - * | |
| 2010 | - * @since 2.5.0 | |
| 2011 | - * @param array $video YouTube video object. | |
| 2012 | - * @param array $attributes Array of user attributes. | |
| 2013 | - */ | |
| 2014 | -function the_ayg_player( $video, $attributes ) { | |
| 2015 | - include ayg_get_template( AYG_DIR . 'public/templates/player.php' ); | |
| 2016 | 1001 | } |