PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3
Jetpack – WP Security, Backup, Speed, & Growth v16.3
16.3 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 All 508 releases
jetpack / jetpack_vendor / automattic / jetpack-videopress / src / class-access-control.php

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

755 lines 26.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * VideoPress Access Control.
4 *
5 * @package automattic/jetpack-videopress
6 */
7
8 namespace Automattic\Jetpack\VideoPress;
9
10 use Automattic\Jetpack\Extensions\Premium_Content\Subscription_Service\Abstract_Token_Subscription_Service;
11 use Automattic\Jetpack\Modules;
12 use VIDEOPRESS_PRIVACY;
13 use WP_Post;
14
15 /**
16 * VideoPress video access control utilities.
17 *
18 * Note: this is also being used on WordPress.com.
19 * Use IS_WPCOM checks for functionality that is specific to WPCOM/Jetpack.
20 */
21 class Access_Control {
22
23 /**
24 * Singleton Access_Control instance.
25 *
26 * @var Access_Control
27 **/
28 private static $instance = null;
29
30 /**
31 * Guid to subscription plan, store, for when used inline on a page.
32 *
33 * @var array
34 */
35 private $guids_to_subscriptions = array();
36
37 /**
38 * Set that this guid is controlled by a subscription.
39 *
40 * @param string $guid The guid to set.
41 * @param string|int $subscription_id The subscription to set.
42 *
43 * @return Access_Control
44 */
45 public function set_guid_subscription( $guid, $subscription_id ) {
46 $this->guids_to_subscriptions[ $guid ] = $subscription_id;
47 return $this;
48 }
49
50 /**
51 * Get the subscription for a guid.
52 *
53 * @param string $guid The guid to get.
54 *
55 * @return string|int|false
56 */
57 public function get_subscription_plan_id( $guid ) {
58 return $this->guids_to_subscriptions[ $guid ] ?? false;
59 }
60
61 /**
62 * Get the singleton instance.
63 *
64 * @return self
65 */
66 public static function instance() {
67 if ( null === self::$instance ) {
68 self::$instance = new self();
69 }
70
71 return self::$instance;
72 }
73
74 /**
75 * Determines if Jetpack Memberships are available.
76 *
77 * @return bool
78 */
79 private function jetpack_memberships_available() {
80 return class_exists( '\Jetpack_Memberships' );
81 }
82
83 /**
84 * Determines if Jetpack Subscriptions are available.
85 *
86 * @return bool
87 */
88 private function jetpack_subscriptions_available() {
89 $is_module_active = ( new Modules() )->is_active( 'subscriptions' );
90 if ( ! $is_module_active ) {
91 return false;
92 }
93
94 if ( function_exists( '\Automattic\Jetpack\Extensions\Premium_Content\subscription_service' ) ) {
95 return true;
96 }
97
98 if ( ! defined( 'JETPACK__PLUGIN_DIR' ) ) {
99 return false;
100 }
101
102 $subscription_service_file_path = JETPACK__PLUGIN_DIR . 'extensions/blocks/premium-content/_inc/subscription-service/include.php';
103 if ( ! file_exists( $subscription_service_file_path ) ) {
104 return false;
105 }
106
107 require_once $subscription_service_file_path;
108
109 return function_exists( '\Automattic\Jetpack\Extensions\Premium_Content\subscription_service' );
110 }
111
112 /**
113 * Check default user access. By default, subscribers or higher can view videos.
114 *
115 * @param WP_Post $post_to_check The post to check.
116 *
117 * @return bool
118 **/
119 private function get_default_user_capability_for_post( $post_to_check ) {
120 if ( ! isset( $post_to_check->ID ) ) {
121 return false;
122 }
123
124 $default_auth = current_user_can( 'read_post', $post_to_check->ID );
125
126 return $default_auth;
127 }
128
129 /**
130 * Determines if the current user can access restricted content and builds the restriction_details array.
131 *
132 * @param string $guid the video guid.
133 * @param int $embedded_post_id the post id.
134 * @param int $selected_plan_id the selected plan id if applicable.
135 *
136 * @return array
137 */
138 private function build_restriction_details( $guid, $embedded_post_id, $selected_plan_id ) {
139 // A missing post ID does not fall back to the global post, as get_post( 0 ) would.
140 $post_to_check = $embedded_post_id > 0 ? get_post( $embedded_post_id ) : null;
141
142 // Only a published embedding post can authorize playback.
143 if ( ! $post_to_check instanceof WP_Post || 'publish' !== $post_to_check->post_status ) {
144 $restriction_details = $this->default_video_restriction_details( false );
145 return $this->filter_video_restriction_details( $restriction_details, $guid, $embedded_post_id, $selected_plan_id );
146 }
147
148 $default_auth = $this->get_default_user_capability_for_post( $post_to_check );
149 $restriction_details = $this->default_video_restriction_details( $default_auth );
150 // Logged-out visitors never have read_post, so a public post admits them on its status alone.
151 // Neither check knows about post passwords, which the page enforces for everyone.
152 $post_admits_visitor = ( $default_auth || is_post_publicly_viewable( $post_to_check ) )
153 && ! post_password_required( $post_to_check );
154
155 if ( $this->jetpack_memberships_available() ) {
156 $post_access_level = \Jetpack_Memberships::get_post_access_level( $embedded_post_id );
157 if ( 'everybody' !== $post_access_level ) {
158 $memberships_can_view_post = is_callable( array( '\Jetpack_Memberships', 'user_has_subscription_access' ) )
159 && \Jetpack_Memberships::user_has_subscription_access( $embedded_post_id );
160 $restriction_details = $this->get_subscriber_only_restriction_details( $default_auth );
161 $restriction_details['can_access'] = $memberships_can_view_post;
162 $post_admits_visitor = $post_admits_visitor && $memberships_can_view_post;
163 }
164 }
165
166 return $this->check_block_level_access(
167 $restriction_details,
168 $guid,
169 $embedded_post_id,
170 $selected_plan_id,
171 $post_admits_visitor
172 );
173 }
174
175 /**
176 * Determines if the current user can access restricted block content and updates the restriction_details array.
177 *
178 * @param array $restriction_details the restriction details array.
179 * @param string $guid the video guid.
180 * @param int $embedded_post_id the post id.
181 * @param int $selected_plan_id the plan id sent with the request. Only passed on to the filter below.
182 * @param bool $post_admits_visitor Whether the embedding post itself, and any paywall on it, lets the visitor in.
183 *
184 * @return array
185 */
186 private function check_block_level_access( $restriction_details, $guid, $embedded_post_id, $selected_plan_id, $post_admits_visitor ) {
187 // Plans come from the stored block; null means no Paid Content block wraps the guid.
188 $required_plan_ids = $this->get_required_plan_ids_for_guid( $embedded_post_id, $guid );
189
190 if ( $this->jetpack_subscriptions_available() && null !== $required_plan_ids ) {
191 $prior_can_access = $restriction_details['can_access'];
192 $restriction_details = $this->get_subscriber_only_restriction_details( $prior_can_access );
193
194 if ( empty( $required_plan_ids ) ) {
195 $restriction_details['can_access'] = false;
196 } else {
197 $can_view = $this->block_gate_grants_access( $required_plan_ids, $embedded_post_id );
198
199 // A block grant also requires the post-level decision.
200 $restriction_details['can_access'] = $post_admits_visitor && $can_view;
201 }
202 }
203
204 return $this->filter_video_restriction_details(
205 $restriction_details,
206 $guid,
207 $embedded_post_id,
208 $selected_plan_id
209 );
210 }
211
212 /**
213 * Ask the premium-content block's own gate whether the visitor may view content behind these plans.
214 *
215 * Sharing the gate keeps playback and the page in agreement, including on tier upgrades.
216 *
217 * @param int[] $required_plan_ids Plan ids derived from the block wrapping the guid.
218 * @param int $embedded_post_id The post the block lives in; there is no loop post to fall back on here.
219 *
220 * @return bool
221 */
222 private function block_gate_grants_access( $required_plan_ids, $embedded_post_id ) {
223 // The gate normally loads with the Paid Content block, which a site can have switched off.
224 if (
225 ! function_exists( '\Automattic\Jetpack\Extensions\Premium_Content\visitor_has_subscription_access_to_plan_ids' )
226 && defined( 'JETPACK__PLUGIN_DIR' )
227 ) {
228 $access_check_path = JETPACK__PLUGIN_DIR . 'extensions/blocks/premium-content/_inc/access-check.php';
229 if ( file_exists( $access_check_path ) ) {
230 require_once $access_check_path;
231 }
232 }
233
234 if ( function_exists( '\Automattic\Jetpack\Extensions\Premium_Content\visitor_has_subscription_access_to_plan_ids' ) ) {
235 return (bool) \Automattic\Jetpack\Extensions\Premium_Content\visitor_has_subscription_access_to_plan_ids( $required_plan_ids, $embedded_post_id );
236 }
237
238 // A newer standalone VideoPress can run beside an older Jetpack that lacks the shared gate.
239 // Exact plan matching is stricter than the gate for tiers, never more permissive.
240 $paywall = \Automattic\Jetpack\Extensions\Premium_Content\subscription_service();
241
242 // Only paid subscribers should be granted access to the premium content.
243 $access_level = '';
244 if ( class_exists( Abstract_Token_Subscription_Service::class ) ) {
245 $access_level = Abstract_Token_Subscription_Service::POST_ACCESS_LEVEL_PAID_SUBSCRIBERS;
246 }
247
248 // Deny when the service has no entitlement-only check.
249 return is_callable( array( $paywall, 'visitor_has_subscription_access' ) )
250 // @phan-suppress-next-line PhanUndeclaredMethod -- Optional method is checked above to support older services.
251 && $paywall->visitor_has_subscription_access( $required_plan_ids, $access_level, $embedded_post_id );
252 }
253
254 /**
255 * Returns the default restriction_details for a video.
256 *
257 * @param bool $default_can_access The default auth.
258 *
259 * @return array
260 **/
261 private function get_subscriber_only_restriction_details( $default_can_access = false ) {
262 return array(
263 'provider' => 'jetpack_memberships',
264 'title' => __( 'This video is subscriber-only', 'jetpack-videopress-pkg' ),
265 'unauthorized_message' => __( 'You need to be subscribed to view this video', 'jetpack-videopress-pkg' ),
266 'can_access' => $default_can_access,
267 );
268 }
269
270 /**
271 * Filters restriction details.
272 *
273 * @param array $video_restriction_details The restriction details.
274 * @param string $guid The video guid.
275 * @param int $embedded_post_id The post id.
276 * @param int $selected_plan_id The selected plan id if applicable.
277 *
278 * @return array
279 */
280 private function filter_video_restriction_details( $video_restriction_details, $guid, $embedded_post_id, $selected_plan_id ) {
281 /**
282 * Filters the video restriction details.
283 *
284 * @param array $video_restriction_details The restriction details.
285 * @param string $guid The video guid.
286 * @param int $embedded_post_id The post id.
287 * @param int $selected_plan_id The selected plan id if applicable.
288 *
289 * @return array
290 */
291 return (array) apply_filters( 'videopress_video_restriction_details', $video_restriction_details, $guid, $embedded_post_id, $selected_plan_id );
292 }
293
294 /**
295 * Returns the default restriction_details for a video.
296 *
297 * @param bool $default_can_access The default auth.
298 *
299 * @return array
300 **/
301 private function default_video_restriction_details( $default_can_access = false ) {
302 $restriction_details = array(
303 'version' => '1',
304 'provider' => 'auth',
305 'title' => __( 'Unauthorized', 'jetpack-videopress-pkg' ),
306 'unauthorized_message' => __( 'Unauthorized', 'jetpack-videopress-pkg' ),
307 'can_access' => $default_can_access,
308 );
309
310 return $restriction_details;
311 }
312
313 /**
314 * How long the per-post GUID list stays cached.
315 *
316 * @var int
317 */
318 const GUID_CACHE_EXPIRATION = 12 * HOUR_IN_SECONDS;
319
320 /**
321 * Maximum number of synced-pattern (wp_block) refs resolved per scan.
322 *
323 * @var int
324 */
325 const MAX_PATTERN_REFS = 20;
326
327 /**
328 * Build and cache the list of VideoPress GUIDs present in a post.
329 *
330 * Scans the post content for VideoPress video and playlist blocks, shortcodes, and
331 * URLs, then caches the GUID list in a transient for fast lookup during authorization
332 * checks. Synced pattern (core/block) refs are resolved manually: parse_blocks() does
333 * NOT expand them — their content lives in the referenced wp_block post and is only
334 * expanded by WordPress at render time.
335 *
336 * @param int $post_id The post ID to scan.
337 * @return array Array of VideoPress GUIDs found in the post.
338 */
339 public static function build_and_cache_post_guids( $post_id ) {
340 if ( empty( $post_id ) ) {
341 return array();
342 }
343
344 $post_id = absint( $post_id );
345 $transient_key = "videopress_guids_{$post_id}";
346
347 // Check if already cached.
348 $cached_guids = get_transient( $transient_key );
349 if ( false !== $cached_guids ) {
350 return (array) $cached_guids;
351 }
352
353 $post = get_post( $post_id );
354 if ( ! $post instanceof WP_Post || empty( $post->post_content ) ) {
355 set_transient( $transient_key, array(), self::GUID_CACHE_EXPIRATION );
356 return array();
357 }
358
359 $visited_refs = array();
360 $guids = self::collect_guids_from_content( $post->post_content, $visited_refs );
361
362 // Cache and return unique GUIDs.
363 $unique_guids = array_values( array_unique( array_filter( $guids ) ) );
364 set_transient( $transient_key, $unique_guids, self::GUID_CACHE_EXPIRATION );
365
366 return $unique_guids;
367 }
368
369 /**
370 * Ensure the given rendered GUIDs are present in a post's cached GUID list.
371 *
372 * Called from block render callbacks, where the block has already been expanded by
373 * WordPress (synced patterns, templates, template parts, widget areas…): the render
374 * itself is proof the video is embedded in the page being served for this post, so
375 * the GUID can be added even when the static content scan cannot see it.
376 *
377 * @param int $post_id The post ID the block is rendering on.
378 * @param string|string[] $guids The GUID(s) being rendered.
379 */
380 public static function ensure_post_guids_cached( $post_id, $guids ) {
381 $post_id = absint( $post_id );
382 $guids = array_filter( (array) $guids, 'is_string' );
383 if ( ! $post_id || ! $guids ) {
384 return;
385 }
386
387 $cached = self::build_and_cache_post_guids( $post_id );
388 $missing = array_diff( $guids, $cached );
389 if ( $missing ) {
390 set_transient(
391 "videopress_guids_{$post_id}",
392 array_values( array_merge( $cached, $missing ) ),
393 self::GUID_CACHE_EXPIRATION
394 );
395 }
396 }
397
398 /**
399 * Collect VideoPress GUIDs from a chunk of post content: blocks (with synced pattern
400 * refs resolved recursively), VideoPress URLs, and legacy shortcodes.
401 *
402 * @param string $content The post content to scan.
403 * @param array $visited_refs Accumulator of wp_block ref ids already resolved, keyed by id.
404 * @return array Array of VideoPress GUIDs found.
405 */
406 private static function collect_guids_from_content( $content, &$visited_refs ) {
407 $guids = self::collect_guids_from_blocks( parse_blocks( $content ), $visited_refs );
408
409 // Scan for VideoPress URLs (oEmbed, core/embed, core/video sources).
410 if ( preg_match_all( '#https?://[^\s"\'<>)]+#i', $content, $matches ) ) {
411 foreach ( $matches[0] as $url ) {
412 $guid = Utils::extract_videopress_guid_from_url( $url );
413 if ( $guid ) {
414 $guids[] = $guid;
415 }
416 }
417 }
418
419 // Scan for [videopress] and [wpvideo] shortcodes.
420 $pattern = get_shortcode_regex( array( 'videopress', 'wpvideo' ) );
421 $count = preg_match_all( '/' . $pattern . '/', $content, $matches, PREG_SET_ORDER );
422 if ( false !== $count && $count > 0 ) {
423 foreach ( $matches as $match ) {
424 $atts = shortcode_parse_atts( $match[3] );
425 // Only the positional argument identifies the video; named attributes must not satisfy the binding check.
426 if ( is_array( $atts ) && isset( $atts[0] ) && is_string( $atts[0] ) ) {
427 $guids[] = $atts[0];
428 }
429 }
430 }
431
432 return $guids;
433 }
434
435 /**
436 * Recursively collect VideoPress GUIDs from parsed blocks.
437 *
438 * Handles videopress/video blocks, videopress/playlist entries, and synced patterns:
439 * a core/block ref is resolved by loading the referenced wp_block post and scanning
440 * its content, mirroring render_block_core_block()'s constraints (published, unlocked
441 * wp_block posts only) with a visited guard against cycles.
442 *
443 * @param array $blocks Array of parsed blocks.
444 * @param array $visited_refs Accumulator of wp_block ref ids already resolved, keyed by id.
445 * @return array Array of VideoPress GUIDs found.
446 */
447 private static function collect_guids_from_blocks( $blocks, &$visited_refs ) {
448 $guids = array();
449
450 foreach ( $blocks as $block ) {
451 $block_name = $block['blockName'] ?? null;
452 $attrs = isset( $block['attrs'] ) && is_array( $block['attrs'] ) ? $block['attrs'] : array();
453 $attr_guid = $attrs['guid'] ?? null;
454 $attr_videos = $attrs['videos'] ?? null;
455 $attr_ref = $attrs['ref'] ?? null;
456
457 // A VideoPress video block with a GUID.
458 if ( 'videopress/video' === $block_name && is_string( $attr_guid ) ) {
459 $guids[] = $attr_guid;
460 }
461
462 // A VideoPress playlist block: each entry carries its own GUID.
463 if ( 'videopress/playlist' === $block_name && is_array( $attr_videos ) ) {
464 foreach ( $attr_videos as $entry ) {
465 if ( is_array( $entry ) && isset( $entry['guid'] ) && is_string( $entry['guid'] ) ) {
466 $guids[] = $entry['guid'];
467 }
468 }
469 }
470
471 // A synced pattern: resolve the wp_block ref, which parse_blocks() leaves unexpanded.
472 if ( 'core/block' === $block_name && ! empty( $attr_ref ) ) {
473 $ref = absint( $attr_ref );
474 if ( $ref && ! isset( $visited_refs[ $ref ] ) && count( $visited_refs ) < self::MAX_PATTERN_REFS ) {
475 $visited_refs[ $ref ] = true;
476
477 $pattern = get_post( $ref );
478 if (
479 $pattern instanceof WP_Post
480 && 'wp_block' === $pattern->post_type
481 && 'publish' === $pattern->post_status
482 && empty( $pattern->post_password )
483 && ! empty( $pattern->post_content )
484 ) {
485 $guids = array_merge( $guids, self::collect_guids_from_content( $pattern->post_content, $visited_refs ) );
486 }
487 }
488 }
489
490 // Recursively check inner blocks.
491 if ( ! empty( $block['innerBlocks'] ) && is_array( $block['innerBlocks'] ) ) {
492 $guids = array_merge( $guids, self::collect_guids_from_blocks( $block['innerBlocks'], $visited_refs ) );
493 }
494 }
495
496 return $guids;
497 }
498
499 /**
500 * Read the plan ids gating a video guid from the Paid Content block that wraps it in the stored post.
501 *
502 * Playback is authorized outside any render, so the post's blocks are re-parsed here.
503 *
504 * @param int $embedded_post_id The post the video is embedded in.
505 * @param string $guid The video guid.
506 *
507 * @return int[]|null Plan ids that gate the guid, an empty array when it is wrapped in a premium-content
508 * block that configures no plan, or null when it is not wrapped in one at all.
509 */
510 private function get_required_plan_ids_for_guid( $embedded_post_id, $guid ) {
511 $embedded_post_id = (int) $embedded_post_id;
512 if ( ! $embedded_post_id ) {
513 return null;
514 }
515
516 $post = get_post( $embedded_post_id );
517 if ( ! $post || false === strpos( (string) $post->post_content, 'wp:premium-content/container' ) ) {
518 return null;
519 }
520
521 return $this->find_gating_plan_ids( parse_blocks( $post->post_content ), $guid );
522 }
523
524 /**
525 * Recursively locate the premium-content/container block that wraps the given guid and return its plan ids.
526 *
527 * @param array $blocks Parsed blocks (parse_blocks() output or an innerBlocks array).
528 * @param string $guid The video guid to match.
529 *
530 * @return int[]|null Plan ids of the wrapping premium-content block (possibly an empty array when it
531 * configures none), or null when no premium-content block wraps the guid.
532 */
533 private function find_gating_plan_ids( $blocks, $guid ) {
534 foreach ( $blocks as $block ) {
535 if ( empty( $block['blockName'] ) ) {
536 continue;
537 }
538
539 // Descend first so the innermost container, the one directly gating the video, wins.
540 if ( ! empty( $block['innerBlocks'] ) && is_array( $block['innerBlocks'] ) ) {
541 $found = $this->find_gating_plan_ids( $block['innerBlocks'], $guid );
542 if ( null !== $found ) {
543 return $found;
544 }
545 }
546
547 if (
548 is_string( $block['blockName'] ) && 'premium-content/container' === $block['blockName']
549 && $this->container_gates_guid( $block, $guid )
550 ) {
551 return $this->extract_plan_ids( $block['attrs'] ?? array() );
552 }
553 }
554
555 return null;
556 }
557
558 /**
559 * Determine whether a premium-content/container block wraps the given guid.
560 *
561 * Uses the same scan as the per-post GUID cache, so every embed form it recognises counts.
562 *
563 * @param array $block A parsed premium-content/container block.
564 * @param string $guid The video guid to match.
565 *
566 * @return bool
567 */
568 private function container_gates_guid( $block, $guid ) {
569 $visited_refs = array();
570
571 return in_array( $guid, self::collect_guids_from_content( serialize_blocks( array( $block ) ), $visited_refs ), true );
572 }
573
574 /**
575 * Normalise a premium-content/container block's plan attributes into a list of plan ids.
576 *
577 * Mirrors the precedence used when rendering the block: the current `selectedPlanIds` array
578 * wins, falling back to the legacy single `selectedPlanId`.
579 *
580 * @param array $attrs The block attributes.
581 *
582 * @return int[] Plan ids, with zero/empty values removed.
583 */
584 private function extract_plan_ids( $attrs ) {
585 if ( isset( $attrs['selectedPlanIds'] ) && is_array( $attrs['selectedPlanIds'] ) ) {
586 return array_values( array_filter( array_map( 'intval', $attrs['selectedPlanIds'] ) ) );
587 }
588
589 if ( isset( $attrs['selectedPlanId'] ) ) {
590 $plan_id = (int) $attrs['selectedPlanId'];
591 return $plan_id ? array( $plan_id ) : array();
592 }
593
594 return array();
595 }
596
597 /**
598 * Check if a post contains a VideoPress GUID, using the cached GUID list for fast lookup.
599 *
600 * Used to prevent the embedded post id — which arrives from request input — from being
601 * treated as an authorization context when it has no relationship to the requested video.
602 * Matching the attachment id itself is not treated as proof of embedding: attachment ids
603 * are enumerable via the media REST route and would otherwise provide a second path around
604 * this check whenever the attachment has no parent and falls back to the `read` capability.
605 *
606 * @param int $embedded_post_id The post id to check.
607 * @param string $guid The video guid to find.
608 *
609 * @return bool
610 */
611 private function post_embeds_videopress_guid_cached( $embedded_post_id, $guid ) {
612 if ( empty( $embedded_post_id ) || empty( $guid ) ) {
613 return false;
614 }
615
616 // Try fast lookup from cache.
617 $transient_key = "videopress_guids_{$embedded_post_id}";
618 $cached_guids = get_transient( $transient_key );
619
620 if ( false !== $cached_guids ) {
621 return in_array( $guid, (array) $cached_guids, true );
622 }
623
624 // Cache miss: build and cache the GUID list, then check.
625 $guids = self::build_and_cache_post_guids( $embedded_post_id );
626 return in_array( $guid, $guids, true );
627 }
628
629 /**
630 * Determines if the current user can view the provided video. Only ever gets fired if site-wide private videos are enabled.
631 *
632 * Filterable for 3rd party plugins.
633 *
634 * @param string $guid The video id being checked.
635 * @param int $embedded_post_id The post id the video is embedded in or 0.
636 * @param int $selected_plan_id The plan id the earn block this video is embedded in has.
637 */
638 public function is_current_user_authed_for_video( $guid, $embedded_post_id, $selected_plan_id = 0 ) {
639 if ( current_user_can( 'upload_files' ) ) {
640 return $this->filter_is_current_user_authed_for_video( true, $guid, $embedded_post_id );
641 }
642
643 $attachment = false;
644 if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
645 $video_info = video_get_info_by_guid( $guid );
646 if ( ! empty( $video_info ) ) {
647 $attachment = get_blog_post( $video_info->blog_id, $video_info->post_id );
648 }
649 } else {
650 $attachment = videopress_get_post_by_guid( $guid );
651 }
652
653 if ( ! $attachment ) {
654 return false;
655 }
656
657 $video_info = video_get_info_by_blogpostid( get_current_blog_id(), $attachment->ID );
658 if ( null === $video_info->guid ) {
659 return false;
660 }
661
662 /*
663 * Default missing privacy_setting to SITE_DEFAULT to avoid an
664 * undefined-property warning and make the site-level fallback explicit.
665 */
666 $privacy_setting = $video_info->privacy_setting ?? VIDEOPRESS_PRIVACY::SITE_DEFAULT;
667
668 $embedded_post_id = (int) $embedded_post_id;
669 if (
670 $embedded_post_id
671 && VIDEOPRESS_PRIVACY::IS_PUBLIC !== $privacy_setting
672 && ! $this->post_embeds_videopress_guid_cached( $embedded_post_id, $guid )
673 ) {
674 $embedded_post_id = 0;
675 }
676
677 $is_user_authed = false;
678
679 // Determine if video is public, private or use site default.
680 switch ( $privacy_setting ) {
681 case VIDEOPRESS_PRIVACY::IS_PUBLIC:
682 $is_user_authed = true;
683 break;
684 case VIDEOPRESS_PRIVACY::IS_PRIVATE:
685 $restriction_details = $this->build_restriction_details( $guid, $embedded_post_id, $selected_plan_id );
686 $is_user_authed = $restriction_details['can_access'];
687 break;
688 case VIDEOPRESS_PRIVACY::SITE_DEFAULT:
689 default:
690 $is_videopress_private_for_site = Data::get_videopress_videos_private_for_site();
691 $is_user_authed = true;
692 if ( $is_videopress_private_for_site ) {
693 $restriction_details = $this->build_restriction_details( $guid, $embedded_post_id, $selected_plan_id );
694 $is_user_authed = $restriction_details['can_access'];
695 }
696 }
697
698 /**
699 * Overrides video view authorization for current user.
700 *
701 * Example of making all videos public:
702 *
703 * function jp_example_override_video_auth( $is_user_authed, $guid ) {
704 * return true
705 * };
706 * add_filter( 'videopress_is_current_user_authed_for_video', 'jp_example_override_video_auth', 10, 2 );
707 *
708 * @param bool $is_user_authed The current user authorization state.
709 * @param string $guid The video's unique identifier.
710 * @param int|null $embedded_post_id The post the video is embedded..
711 *
712 * @return bool
713 */
714 return $this->filter_is_current_user_authed_for_video( $is_user_authed, $guid, $embedded_post_id );
715 }
716
717 /**
718 * Overrides video view authorization for current user.
719 *
720 * @param bool $is_user_authed The current user authorization state.
721 * @param string $guid The video's unique identifier.
722 * @param int|null $embedded_post_id The post the video is embedded..
723 *
724 * @return bool
725 */
726 private function filter_is_current_user_authed_for_video( $is_user_authed, $guid, $embedded_post_id ) {
727 /**
728 * Overrides video view authorization for current user.
729 *
730 * Example of making all videos public:
731 *
732 * function jp_example_override_video_auth( $is_user_authed, $guid ) {
733 * return true
734 * };
735 * add_filter( 'videopress_is_current_user_authed_for_video', 'jp_example_override_video_auth', 10, 2 );
736 *
737 * @param bool $is_user_authed The current user authorization state.
738 * @param string $guid The video's unique identifier.
739 * @param int|null $embedded_post_id The post the video is embedded..
740 *
741 * @return bool
742 */
743 return (bool) apply_filters( 'videopress_is_current_user_authed_for_video', $is_user_authed, $guid, $embedded_post_id );
744 }
745
746 /**
747 * Returns the proper blog id depending on Jetpack or WP.com
748 *
749 * @return int the blog id
750 */
751 public function get_videopress_blog_id() {
752 return \Jetpack_Options::get_option( 'id' );
753 }
754 }
755