PluginProbe ʕ •ᴥ•ʔ
Presto Player / 4.3.1
Presto Player v4.3.1
4.3.1 4.3.0 4.2.4 4.2.3 4.2.2 4.2.0 4.2.1 trunk 1.10.0 1.10.1 1.10.2 1.11.0 1.12.0 1.13.0 1.14.0 1.14.1 1.5.10 1.5.11 1.5.12 1.5.13 1.5.14 1.5.15 1.5.5 1.5.6 1.5.7 1.5.8 1.5.9 1.6.0 1.6.1 1.6.10 1.6.11 1.6.12 1.6.13 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 1.6.8 1.6.9 1.7.0 1.7.1 1.7.2 1.8.0 1.8.1 1.8.2 1.8.3 1.8.4 1.8.5 1.8.6 1.9.0 1.9.1 1.9.10 1.9.11 1.9.12 1.9.13 1.9.14 1.9.2 1.9.3 1.9.4 1.9.5 1.9.6 1.9.7 1.9.8 1.9.9 2.0.0 2.0.1 2.0.10 2.0.11 2.0.12 2.0.13 2.0.14 2.0.15 2.0.16 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8 2.0.9 2.1.0 2.2.0 2.2.1 2.2.2 2.2.3 2.2.3-beta1 2.3.0 2.3.1 2.3.2 2.3.3 3.0.0 3.0.0-beta1 3.0.1 3.0.2 3.0.3 3.0.4 3.0.5 3.0.6 3.0.7 3.0.8 3.1.0 3.1.1 3.1.2 3.1.3 4.0.0 4.0.1 4.0.2 4.0.3 4.0.4 4.0.5 4.0.6 4.0.7 4.0.8 4.1.0 4.1.1 4.1.2 4.1.3 4.1.4
presto-player / inc / Services / Settings.php
presto-player / inc / Services Last commit date
API 1 month ago Blocks 10 months ago License 1 month ago AdminNotice.php 3 weeks ago AdminNotices.php 3 weeks ago AjaxActions.php 3 weeks ago Blocks.php 1 year ago Compatibility.php 10 months ago Learn.php 1 month ago Menu.php 1 month ago Migrations.php 1 year ago NpsSurvey.php 5 months ago Onboarding.php 3 weeks ago Player.php 3 days ago PluginInstaller.php 1 month ago PreloadService.php 1 year ago ProCompatibility.php 1 month ago ReusableVideos.php 2 weeks ago RewriteRulesManager.php 1 year ago Scripts.php 2 weeks ago Settings.php 1 month ago Shortcodes.php 1 month ago Streamer.php 3 weeks ago Translation.php 3 weeks ago Usage.php 3 weeks ago VideoPostType.php 2 weeks ago
Settings.php
471 lines
1 <?php
2 /**
3 * Plugin settings registration.
4 *
5 * @package presto-player
6 */
7
8 namespace PrestoPlayer\Services;
9
10 use PrestoPlayer\Models\Setting;
11 use PrestoPlayer\Plugin;
12 use PrestoPlayer\Support\Utility;
13
14 /**
15 * Settings service.
16 */
17 class Settings {
18
19 /**
20 * Register our settings
21 *
22 * @return void
23 */
24 public function register() {
25 // `init` covers admin, REST, front-end, AJAX, and cron in one shot — the
26 // previous admin_init + rest_api_init pair double-registered on every
27 // admin REST request and missed cron/front-end contexts entirely.
28 add_action( 'init', array( $this, 'registerSettings' ) );
29
30 // Default to "synced" when the option has never been stored. Covers the
31 // window before register_setting()'s default is wired up by admin_init /
32 // rest_api_init.
33 add_filter(
34 'default_option_presto_player_media_hub_sync_default',
35 function ( $default_value, $option, $passed_default ) {
36 if ( $passed_default ) {
37 return $default_value;
38 }
39 return true;
40 },
41 10,
42 3
43 );
44
45 // $wpdb writes option values via a `%s` placeholder, so PHP `false` lands
46 // in wp_options as ''. That '' then fails the `type: boolean` REST schema
47 // check and WP_REST_Settings_Controller swaps it out for `null` — which
48 // the dashboard hook falls back to the `true` default, silently overriding
49 // "Not Synced" on every page load. Unmarshal '' → false here so the
50 // round-trip through the schema preserves user intent. Direction matters:
51 // coercing '' → true (as an earlier fix did) masks legitimately saved
52 // false values behind update_option()'s no-change short-circuit.
53 add_filter(
54 'option_presto_player_media_hub_sync_default',
55 function ( $value ) {
56 return '' === $value ? false : $value;
57 }
58 );
59 }
60
61 /**
62 * Sanitize the branding option array before storage.
63 *
64 * Single choke-point for every save path: WP core's `/wp/v2/settings`,
65 * the plugin's `presto-player/v1/settings`, and any direct
66 * `update_option('presto_player_branding', ...)` from PHP all route
67 * through `sanitize_option`, which invokes this callback. Per-property
68 * `sanitize_callback` entries on a nested object schema are silently
69 * ignored by core REST (only `type`/`minimum`/`maximum`/`format` run),
70 * so anything mission-critical lives here.
71 *
72 * Responsibilities:
73 * - Strip HTML markup from `player_css` (defense in depth — the
74 * render pipeline also runs `Utility::sanitizeCSS`).
75 * - Constrain `logo` to http(s) URLs (blocks `data:` SVGs and
76 * similar schemes that core's `esc_url_raw` would otherwise pass).
77 * - Clamp `logo_width` to [1, 400] integers.
78 * - Coerce `color` to a valid hex value, falling back to the brand
79 * default when invalid (avoids the schema-validated `null` that
80 * the REST round-trip would otherwise produce).
81 * - Enforce the Pro gate on `logo` / `logo_width` so a non-Pro admin
82 * can't bypass the UI gate via a direct REST call.
83 *
84 * @param mixed $value Raw value posted to the REST/admin form.
85 * @return array Sanitized branding option.
86 */
87 public function sanitizeBranding( $value ) {
88 if ( ! is_array( $value ) ) {
89 return array();
90 }
91
92 if ( array_key_exists( 'player_css', $value ) && is_string( $value['player_css'] ) ) {
93 $value['player_css'] = Utility::sanitizeCSS( $value['player_css'] );
94 }
95
96 if ( array_key_exists( 'logo', $value ) ) {
97 $value['logo'] = is_string( $value['logo'] )
98 ? esc_url_raw( $value['logo'], array( 'http', 'https' ) )
99 : '';
100 }
101
102 if ( array_key_exists( 'logo_width', $value ) ) {
103 $width = (int) $value['logo_width'];
104 $value['logo_width'] = max( 1, min( 400, $width ) );
105 }
106
107 if ( array_key_exists( 'color', $value ) ) {
108 $hex = is_string( $value['color'] ) ? sanitize_hex_color( $value['color'] ) : null;
109 $value['color'] = $hex ?? Setting::getDefaultColor();
110 }
111
112 if ( ! Plugin::isPro() ) {
113 unset( $value['logo'], $value['logo_width'] );
114 }
115
116 return $value;
117 }
118
119 /**
120 * Sanitize usage tracking value to 'yes' or 'no'.
121 *
122 * @param mixed $value Input value. Accepts 'yes', 'no', boolean, or truthy/falsy values.
123 * @return string 'yes' or 'no'
124 */
125 public function sanitize_usage_tracking( $value ) {
126 if ( 'yes' === $value || 'no' === $value ) {
127 return $value;
128 }
129 if ( ! is_scalar( $value ) ) {
130 return 'no';
131 }
132 $bool = filter_var( $value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE );
133 if ( null === $bool ) {
134 $bool = (bool) $value;
135 }
136 return $bool ? 'yes' : 'no';
137 }
138
139 /**
140 * Register plugin settings for WP Settings API / REST.
141 *
142 * @return void
143 */
144 public function registerSettings() {
145 /**
146 * Analytics settings
147 */
148 \register_setting(
149 'presto_player',
150 'presto_player_analytics',
151 array(
152 'type' => 'object',
153 'description' => __( 'Analytics settings.', 'presto-player' ),
154 'show_in_rest' => array(
155 'name' => 'presto_player_analytics',
156 'type' => 'object',
157 'schema' => array(
158 'additionalProperties' => false,
159 'properties' => array(
160 'enable' => array(
161 'type' => 'boolean',
162 ),
163 'purge_data' => array(
164 'type' => 'boolean',
165 ),
166 ),
167 ),
168 ),
169 'default' => array(
170 'enable' => false,
171 'purge_data' => true,
172 ),
173 )
174 );
175
176 /**
177 * Branding settings
178 */
179 \register_setting(
180 'presto_player',
181 'presto_player_branding',
182 array(
183 'type' => 'object',
184 'description' => __( 'Branding settings.', 'presto-player' ),
185 'sanitize_callback' => array( $this, 'sanitizeBranding' ),
186 'show_in_rest' => array(
187 'name' => 'presto_player_branding',
188 'type' => 'object',
189 // Per-property `sanitize_callback` entries inside an object
190 // schema are NOT invoked by core's REST controller — only
191 // the type/minimum/maximum/format checks are. Everything
192 // else is done in `sanitizeBranding` below so it actually
193 // runs for every save path.
194 'schema' => array(
195 'additionalProperties' => false,
196 'properties' => array(
197 'logo' => array(
198 'type' => 'string',
199 ),
200 'logo_width' => array(
201 'type' => 'integer',
202 'minimum' => 1,
203 'maximum' => 400,
204 ),
205 'color' => array(
206 'type' => 'string',
207 ),
208 'player_css' => array(
209 'type' => 'string',
210 ),
211 ),
212 ),
213 ),
214 'default' => array(
215 'logo' => '',
216 'logo_width' => 150,
217 'color' => Setting::getDefaultColor(),
218 'player_css' => '',
219 ),
220 )
221 );
222
223 \register_setting(
224 'presto_player',
225 'presto_player_performance',
226 array(
227 'type' => 'object',
228 'description' => __( 'Performance settings.', 'presto-player' ),
229 'show_in_rest' => array(
230 'name' => 'presto_player_performance',
231 'type' => 'object',
232 'schema' => array(
233 'additionalProperties' => false,
234 'properties' => array(
235 'module_enabled' => array(
236 'type' => 'boolean',
237 ),
238 'automations' => array(
239 'type' => 'boolean',
240 ),
241 ),
242 ),
243 ),
244 'default' => array(
245 'module_enabled' => false,
246 'automations' => true,
247 ),
248 )
249 );
250
251 /**
252 * Uninstall settings
253 */
254 \register_setting(
255 'presto_player',
256 'presto_player_uninstall',
257 array(
258 'type' => 'object',
259 'description' => __( 'Uninstall settings.', 'presto-player' ),
260 'show_in_rest' => array(
261 'name' => 'presto_player_uninstall',
262 'type' => 'object',
263 'schema' => array(
264 'additionalProperties' => false,
265 'properties' => array(
266 'uninstall_data' => array(
267 'type' => 'boolean',
268 ),
269 ),
270 ),
271 ),
272 'default' => array(
273 'uninstall_data' => false,
274 ),
275 )
276 );
277
278 /**
279 * Analytics settings
280 */
281 \register_setting(
282 'presto_player',
283 'presto_player_google_analytics',
284 array(
285 'type' => 'object',
286 'description' => __( 'Google Analytics settings.', 'presto-player' ),
287 'show_in_rest' => array(
288 'name' => 'presto_player_google_analytics',
289 'type' => 'object',
290 'schema' => array(
291 'additionalProperties' => false,
292 'properties' => array(
293 'enable' => array(
294 'type' => 'boolean',
295 ),
296 'use_existing_tag' => array(
297 'type' => 'boolean',
298 ),
299 'measurement_id' => array(
300 'type' => 'string',
301 ),
302 ),
303 ),
304 ),
305 'default' => array(
306 'enable' => false,
307 'use_existing_tag' => false,
308 'measurement_id' => '',
309 ),
310 )
311 );
312
313 /**
314 * General settings
315 */
316 \register_setting(
317 'presto_player',
318 'presto_player_presets',
319 array(
320 'type' => 'object',
321 'description' => __( 'Preset settings.', 'presto-player' ),
322 'show_in_rest' => array(
323 'name' => 'presto_player_presets',
324 'type' => 'object',
325 'schema' => array(
326 'additionalProperties' => false,
327 'properties' => array(
328 'default_player_preset' => array(
329 'type' => 'integer',
330 'sanitize_callback' => 'intval',
331 ),
332 ),
333 ),
334 ),
335 'default' => array(
336 'default_player_preset' => 1,
337 ),
338 )
339 );
340
341 \register_setting(
342 'presto_player',
343 'presto_player_audio_presets',
344 array(
345 'type' => 'object',
346 'description' => __( 'Preset settings.', 'presto-player' ),
347 'show_in_rest' => array(
348 'name' => 'presto_player_audio_presets',
349 'type' => 'object',
350 'schema' => array(
351 'additionalProperties' => false,
352 'properties' => array(
353 'default_player_preset' => array(
354 'type' => 'integer',
355 'sanitize_callback' => 'intval',
356 ),
357 ),
358 ),
359 ),
360 'default' => array(
361 'default_player_preset' => 1,
362 ),
363 )
364 );
365
366 /**
367 * Youtube Settings
368 */
369 \register_setting(
370 'presto_player',
371 'presto_player_youtube',
372 array(
373 'type' => 'object',
374 'description' => __( 'Youtube settings.', 'presto-player' ),
375 'show_in_rest' => array(
376 'name' => 'presto_player_youtube',
377 'type' => 'object',
378 'schema' => array(
379 'additionalProperties' => false,
380 'properties' => array(
381 'nocookie' => array(
382 'type' => 'boolean',
383 ),
384 'channel_id' => array(
385 'type' => 'string',
386 ),
387 ),
388 ),
389 ),
390 'default' => array(
391 'nocookie' => false,
392 'channel_id' => '',
393 ),
394 )
395 );
396
397 /**
398 * Instant Video Width Setting
399 */
400 \register_setting(
401 'presto_player',
402 'presto_player_instant_video_width',
403 array(
404 'type' => 'string',
405 'description' => __( 'Instant video width.', 'presto-player' ),
406 'show_in_rest' => array(
407 'name' => 'presto_player_instant_video_width',
408 'type' => 'string',
409 'schema' => array(
410 'type' => 'string',
411 'default' => '800px',
412 ),
413 ),
414 'default' => '800px',
415 )
416 );
417
418 /**
419 * Set the default for media hub sync setting.
420 */
421 \register_setting(
422 'presto_player',
423 'presto_player_media_hub_sync_default',
424 array(
425 'type' => 'boolean',
426 'description' => __( 'Set the default for media hub sync setting.', 'presto-player' ),
427 'sanitize_callback' => function ( $value ) {
428 if ( null === $value || '' === $value ) {
429 return true;
430 }
431 return rest_sanitize_boolean( $value );
432 },
433 'show_in_rest' => array(
434 'name' => 'presto_player_media_hub_sync_default',
435 'type' => 'boolean',
436 'schema' => array(
437 'type' => 'boolean',
438 'default' => true,
439 ),
440 ),
441 'default' => true,
442 )
443 );
444
445 /**
446 * Usage tracking setting.
447 *
448 * Stored as 'yes' or 'no'. Single source of truth for usage analytics opt-in.
449 */
450 \register_setting(
451 'presto_player',
452 'presto-player_usage_optin',
453 array(
454 'type' => 'string',
455 'description' => __( 'Usage tracking opt-in.', 'presto-player' ),
456 'sanitize_callback' => array( $this, 'sanitize_usage_tracking' ),
457 'show_in_rest' => array(
458 'name' => 'presto-player_usage_optin',
459 'type' => 'string',
460 'schema' => array(
461 'type' => 'string',
462 'enum' => array( 'yes', 'no' ),
463 'default' => 'no',
464 ),
465 ),
466 'default' => 'no',
467 )
468 );
469 }
470 }
471