-
Notifications
You must be signed in to change notification settings - Fork 179
/
Media_Source_Taxonomy.php
403 lines (357 loc) · 10.3 KB
/
Media_Source_Taxonomy.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
<?php
/**
* Class Media_Source_Taxonomy
*
* @link https://github.com/googleforcreators/web-stories-wp
*
* @copyright 2021 Google LLC
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
*/
/**
* Copyright 2021 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
declare(strict_types = 1);
namespace Google\Web_Stories\Media;
use Google\Web_Stories\Context;
use Google\Web_Stories\REST_API\Stories_Terms_Controller;
use Google\Web_Stories\Taxonomy\Taxonomy_Base;
use ReflectionClass;
use WP_Post;
use WP_Query;
use WP_Site;
/**
* Class Media_Source_Taxonomy
*
* @phpstan-import-type TaxonomyArgs from \Google\Web_Stories\Taxonomy\Taxonomy_Base
*/
class Media_Source_Taxonomy extends Taxonomy_Base {
public const TERM_EDITOR = 'editor';
public const TERM_POSTER_GENERATION = 'poster-generation';
public const TERM_SOURCE_VIDEO = 'source-video';
public const TERM_SOURCE_IMAGE = 'source-image';
public const TERM_VIDEO_OPTIMIZATION = 'video-optimization';
public const TERM_PAGE_TEMPLATE = 'page-template';
public const TERM_GIF_CONVERSION = 'gif-conversion';
public const TERM_RECORDING = 'recording';
/**
* Media Source key.
*/
public const MEDIA_SOURCE_KEY = 'web_stories_media_source';
/**
* Context instance.
*/
private Context $context;
/**
* Single constructor.
*
* @param Context $context Context instance.
*/
public function __construct( Context $context ) {
$this->context = $context;
$this->taxonomy_slug = 'web_story_media_source';
$this->taxonomy_post_type = 'attachment';
}
/**
* Init.
*
* @since 1.10.0
*/
public function register(): void {
$this->register_taxonomy();
add_action( 'rest_api_init', [ $this, 'rest_api_init' ] );
add_filter( 'wp_prepare_attachment_for_js', [ $this, 'wp_prepare_attachment_for_js' ] );
// Hide video posters from Media grid view.
add_filter( 'ajax_query_attachments_args', [ $this, 'filter_ajax_query_attachments_args' ], PHP_INT_MAX );
// Hide video posters from Media list view.
add_action( 'pre_get_posts', [ $this, 'filter_generated_media_attachments' ], PHP_INT_MAX );
// Hide video posters from web-stories/v1/media REST API requests.
add_filter( 'web_stories_rest_attachment_query', [ $this, 'filter_rest_generated_media_attachments' ], PHP_INT_MAX );
}
/**
* Act on site initialization.
*
* @since 1.29.0
*
* @param WP_Site $site The site being initialized.
*/
public function on_site_initialization( WP_Site $site ): void {
parent::on_site_initialization( $site );
$this->add_missing_terms();
}
/**
* Act on plugin activation.
*
* @since 1.29.0
*
* @param bool $network_wide Whether the activation was done network-wide.
*/
public function on_plugin_activation( bool $network_wide ): void {
parent::on_plugin_activation( $network_wide );
$this->add_missing_terms();
}
/**
* Returns all defined media source term names.
*
* @since 1.29.0
*
* @return string[] Media sources
*/
public function get_all_terms(): array {
$consts = ( new ReflectionClass( $this ) )->getConstants();
/**
* List of terms.
*
* @var string[] $terms
*/
$terms = array_values(
array_filter(
$consts,
static fn( $key ) => str_starts_with( $key, 'TERM_' ),
ARRAY_FILTER_USE_KEY
)
);
return $terms;
}
/**
* Registers additional REST API fields upon API initialization.
*
* @since 1.10.0
*/
public function rest_api_init(): void {
// Custom field, as built in term update require term id and not slug.
register_rest_field(
$this->taxonomy_post_type,
self::MEDIA_SOURCE_KEY,
[
'get_callback' => [ $this, 'get_callback_media_source' ],
'schema' => [
'description' => __( 'Media source.', 'web-stories' ),
'type' => 'string',
'enum' => $this->get_all_terms(),
'context' => [ 'view', 'edit', 'embed' ],
],
'update_callback' => [ $this, 'update_callback_media_source' ],
]
);
}
/**
* Filters the attachment data prepared for JavaScript.
*
* @since 1.0.0
*
* @param array|mixed $response Array of prepared attachment data.
* @return array|mixed $response Filtered attachment data.
*
* @template T
*
* @phpstan-return ($response is array<T> ? array<T> : mixed)
*/
public function wp_prepare_attachment_for_js( $response ) {
if ( ! \is_array( $response ) ) {
return $response;
}
$response[ self::MEDIA_SOURCE_KEY ] = $this->get_callback_media_source( $response );
return $response;
}
/**
* Force media attachment as string instead of the default array.
*
* @since 1.0.0
*
* @param array<string, mixed> $prepared Prepared data before response.
*/
public function get_callback_media_source( array $prepared ): string {
/**
* Taxonomy ID.
*
* @var int $id
*/
$id = $prepared['id'];
$terms = get_the_terms( $id, $this->taxonomy_slug );
if ( \is_array( $terms ) && ! empty( $terms ) ) {
return array_shift( $terms )->slug;
}
return '';
}
/**
* Update rest field callback.
*
* @since 1.0.0
*
* @param string $value Value to update.
* @param WP_Post $object Object to update on.
* @return true|\WP_Error
*/
public function update_callback_media_source( string $value, WP_Post $object ) {
$check = wp_set_object_terms( $object->ID, $value, $this->taxonomy_slug );
if ( is_wp_error( $check ) ) {
return $check;
}
return true;
}
/**
* Filters the attachment query args to hide generated video poster images.
*
* Reduces unnecessary noise in the Media grid view.
*
* @since 1.10.0
*
* @param array<string, mixed>|mixed $args Query args.
* @return array<string, mixed>|mixed Filtered query args.
*
* @template T
*
* @phpstan-return ($args is array<T> ? array<T> : mixed)
*/
public function filter_ajax_query_attachments_args( $args ) {
if ( ! \is_array( $args ) ) {
return $args;
}
$args['tax_query'] = $this->get_exclude_tax_query( $args ); // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query
return $args;
}
/**
* Filters the current query to hide generated video poster images and source video.
*
* Reduces unnecessary noise in the Media list view.
*
* @since 1.10.0
*
* @param WP_Query $query WP_Query instance, passed by reference.
*/
public function filter_generated_media_attachments( WP_Query $query ): void {
if ( is_admin() && $query->is_main_query() && $this->context->is_upload_screen() ) {
$tax_query = $query->get( 'tax_query' );
$query->set( 'tax_query', $this->get_exclude_tax_query( [ 'tax_query' => $tax_query ] ) ); // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query
}
}
/**
* Filters the current query to hide generated video poster images.
*
* Reduces unnecessary noise in media REST API requests.
*
* @since 1.10.0
*
* @param array<string, mixed>|mixed $args Query args.
* @return array<string, mixed>|mixed Filtered query args.
*
* @template T
*
* @phpstan-return ($args is array<T> ? array<T> : mixed)
*/
public function filter_rest_generated_media_attachments( $args ) {
if ( ! \is_array( $args ) ) {
return $args;
}
$args['tax_query'] = $this->get_exclude_tax_query( $args ); // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query
return $args;
}
/**
* Adds missing terms to the taxonomy.
*
* @since 1.29.0
*/
private function add_missing_terms(): void {
$existing_terms = get_terms(
[
'taxonomy' => $this->get_taxonomy_slug(),
'hide_empty' => false,
'fields' => 'slugs',
]
);
if ( is_wp_error( $existing_terms ) ) {
return;
}
$missing_terms = array_diff( $this->get_all_terms(), $existing_terms );
foreach ( $missing_terms as $term ) {
wp_insert_term( $term, $this->get_taxonomy_slug() );
}
}
/**
* Taxonomy args.
*
* @since 1.12.0
*
* @return array<string,mixed> Taxonomy args.
*
* @phpstan-return TaxonomyArgs
*/
protected function taxonomy_args(): array {
return [
'label' => __( 'Source', 'web-stories' ),
'public' => false,
'rewrite' => false,
'hierarchical' => false,
'show_in_rest' => true,
'rest_namespace' => self::REST_NAMESPACE,
'rest_controller_class' => Stories_Terms_Controller::class,
];
}
/**
* Returns the tax query needed to exclude generated video poster images and source videos.
*
* @param array<string, mixed> $args Existing WP_Query args.
* @return array<int|string, mixed> Tax query arg.
*/
private function get_exclude_tax_query( array $args ): array {
/**
* Tax query.
*
* @var array<int|string, mixed> $tax_query
*/
$tax_query = ! empty( $args['tax_query'] ) ? $args['tax_query'] : [];
/**
* Filter whether generated attachments should be hidden in the media library.
*
* @since 1.16.0
*
* @param bool $enabled Whether the taxonomy check should be applied.
* @param array $args Existing WP_Query args.
*/
$enabled = apply_filters( 'web_stories_hide_auto_generated_attachments', true, $args );
if ( true !== $enabled ) {
return $tax_query;
}
/**
* Merge with existing tax query if needed,
* in a nested way so WordPress will run them
* with an 'AND' relation. Example:
*
* [
* 'relation' => 'AND', // implicit.
* [ this query ],
* [ [ any ], [ existing ], [ tax queries] ]
* ]
*/
$new_tax_query = [
'relation' => 'AND',
[
'taxonomy' => $this->taxonomy_slug,
'field' => 'slug',
'terms' => [
self::TERM_POSTER_GENERATION,
self::TERM_SOURCE_VIDEO,
self::TERM_SOURCE_IMAGE,
self::TERM_PAGE_TEMPLATE,
],
'operator' => 'NOT IN',
],
];
if ( ! empty( $tax_query ) ) {
$new_tax_query[] = [ $tax_query ];
}
return $new_tax_query;
}
}