PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3-beta
Jetpack – WP Security, Backup, Speed, & Growth v16.3-beta
16.3-beta 16.3-a.5 16.3-a.7 16.3-a.3 16.3-a.1 16.2 16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 All 507 releases
jetpack / jetpack_vendor / automattic / jetpack-videopress / src / class-caption-tracks.php

class-caption-tracks.php in Jetpack – WP Security, Backup, Speed, & Growth 16.3-beta, at jetpack_vendor/automattic/jetpack-videopress/src/class-caption-tracks.php

161 lines 5.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * VideoPress caption track post type and helpers.
4 *
5 * @package automattic/jetpack-videopress
6 */
7
8 namespace Automattic\Jetpack\VideoPress;
9
10 /**
11 * Stores editable caption track documents as private WordPress posts.
12 *
13 * Track content is stored as serialized `videopress/caption-cue` block markup,
14 * matching the block-based editor that produces it. This is a deliberate
15 * trade-off: it keeps the draft round-trippable through the editor without a
16 * bespoke format, at the cost of coupling stored drafts to the block markup
17 * (sanitization re-parses blocks, and replacing the editor later means
18 * migrating stored content).
19 *
20 * The REST routes serving this store live in
21 * {@see WPCOM_REST_API_V2_Endpoint_VideoPress_Caption_Tracks}.
22 */
23 class Caption_Tracks {
24
25 const POST_TYPE = 'vp_caption_track';
26
27 const CUE_BLOCK_NAME = 'videopress/caption-cue';
28
29 const META_GUID = '_videopress_guid';
30 const META_KIND = '_videopress_caption_kind';
31 const META_SRC_LANG = '_videopress_caption_src_lang';
32 const META_LABEL = '_videopress_caption_label';
33 const META_SOURCE_TRACK_KIND = '_videopress_source_track_kind';
34 const META_SOURCE_TRACK_SRC_LANG = '_videopress_source_track_src_lang';
35 const META_SOURCE_TRACK_SRC = '_videopress_source_track_src';
36
37 /**
38 * Initialize the caption track post type.
39 *
40 * @return void
41 */
42 public static function init() {
43 add_action( 'init', array( __CLASS__, 'register_post_type' ) );
44 add_action( 'init', array( __CLASS__, 'register_meta' ) );
45 }
46
47 /**
48 * Register the private caption track post type.
49 *
50 * @return void
51 */
52 public static function register_post_type() {
53 register_post_type(
54 self::POST_TYPE,
55 array(
56 'labels' => array(
57 'name' => __( 'VideoPress caption tracks', 'jetpack-videopress-pkg' ),
58 'singular_name' => __( 'VideoPress caption track', 'jetpack-videopress-pkg' ),
59 ),
60 'public' => false,
61 'exclude_from_search' => true,
62 'publicly_queryable' => false,
63 'show_ui' => false,
64 'show_in_menu' => false,
65 'show_in_rest' => false,
66 // No revisions: there is no restore UI, and each one would copy up to 1MB of content.
67 'supports' => array( 'title', 'editor' ),
68
69 /*
70 * Caption tracks are an internal store reached only through the
71 * /wpcom/v2/videopress/caption-tracks routes, which authorize
72 * each request against the video it targets. The post type itself
73 * is not exposed (no REST, no admin UI), so standard post
74 * capabilities are sufficient.
75 */
76 'capability_type' => 'post',
77 'map_meta_cap' => true,
78 )
79 );
80 }
81
82 /**
83 * Register caption track metadata so values are sanitized when saved.
84 *
85 * @return void
86 */
87 public static function register_meta() {
88 $string_meta = array(
89 self::META_GUID => array( __CLASS__, 'sanitize_guid' ),
90 self::META_KIND => array( __CLASS__, 'sanitize_kind' ),
91 self::META_SRC_LANG => array( __CLASS__, 'sanitize_manual_language' ),
92 self::META_LABEL => 'sanitize_text_field',
93 self::META_SOURCE_TRACK_KIND => array( __CLASS__, 'sanitize_kind' ),
94 self::META_SOURCE_TRACK_SRC_LANG => 'sanitize_text_field',
95 self::META_SOURCE_TRACK_SRC => 'esc_url_raw',
96 );
97
98 foreach ( $string_meta as $key => $sanitize_callback ) {
99 register_post_meta(
100 self::POST_TYPE,
101 $key,
102 array(
103 'type' => 'string',
104 'single' => true,
105 'sanitize_callback' => $sanitize_callback,
106 )
107 );
108 }
109 }
110
111 /**
112 * Sanitize a VideoPress GUID.
113 *
114 * @param string $value Raw value.
115 * @return string
116 */
117 public static function sanitize_guid( $value ) {
118 $value = sanitize_text_field( $value );
119 return preg_match( '/^[a-z0-9]{8}$/i', $value ) ? $value : '';
120 }
121
122 /**
123 * Sanitize a text track kind.
124 *
125 * @param string $value Raw value.
126 * @return string
127 */
128 public static function sanitize_kind( $value ) {
129 $value = sanitize_key( $value );
130 $valid = array( 'subtitles', 'captions', 'descriptions', 'chapters', 'metadata' );
131 return in_array( $value, $valid, true ) ? $value : '';
132 }
133
134 /**
135 * Validate a caption track's BCP-47 language tag.
136 *
137 * The client canonicalizes tags with `Intl.getCanonicalLocales` before
138 * saving, and the "one track per language" lookup matches stored tags by
139 * exact string, so this is a validation gate only: it rejects generated
140 * source keys such as `auto_en` and malformed tags, then keeps the client's
141 * canonical value verbatim. Re-casing here would risk diverging from the
142 * client's canonical form and silently create duplicate tracks.
143 *
144 * @param string $value Raw value.
145 * @return string Validated tag, or empty string when invalid.
146 */
147 public static function sanitize_manual_language( $value ) {
148 $value = trim( sanitize_text_field( $value ) );
149
150 if ( preg_match( '/^auto[_-]/i', $value ) ) {
151 return '';
152 }
153
154 if ( ! preg_match( '/^[a-z]{2,3}(?:-[a-z0-9]{2,8})*$/i', $value ) ) {
155 return '';
156 }
157
158 return $value;
159 }
160 }
161