&exp=&sig= * unsigned: ?student_id= * The secret never leaves this server; only the signature does. */ class BunStream_Embed { /** How long a signed link stays valid (BunStream accepts up to 7 days). */ const SIGNED_TTL = 21600; const UUID_PATTERN = '/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i'; public static function init() { add_shortcode('bunstream', array(__CLASS__, 'shortcode')); } public static function shortcode($atts) { $atts = shortcode_atts(array( 'id' => '', 'width' => '', 'max-width' => '', 'max_width' => '', ), $atts, 'bunstream'); return self::render( $atts['id'], $atts['width'], $atts['max-width'] !== '' ? $atts['max-width'] : $atts['max_width'] ); } public static function render($uuid, $width = '', $max_width = '') { $uuid = strtolower(trim((string) $uuid)); if (!preg_match(self::UUID_PATTERN, $uuid)) { return current_user_can('edit_posts') ? '

' . esc_html__('BunStream: رقم الدرس (id) غير صحيح.', 'bunstream') . '

' : ''; } $src = BunStream_Options::player_embed_base() . $uuid; $params = self::student_params($uuid); if ($params) { $src .= '?' . http_build_query($params, '', '&', PHP_QUERY_RFC3986); // This page now carries one student's id: a page cache that also // stores logged-in pages must not serve it to the next student, // whose watermark would then show this one's number. if (!defined('DONOTCACHEPAGE')) { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedConstantFound -- the name cache plugins read. define('DONOTCACHEPAGE', true); } if (!headers_sent()) { nocache_headers(); } } // Identical to BunStream's own embed code, src aside. $html = '
' . '' . '
'; $style = ''; if (($w = self::css_length($width)) !== '') { $style .= 'width:' . $w . ';'; } if (($mw = self::css_length($max_width)) !== '') { $style .= 'max-width:' . $mw . ';margin-left:auto;margin-right:auto;'; } return '
' . $html . '
'; } /** Query parameters carrying the signed-in user's id, or none. */ public static function student_params($uuid) { $sid = self::student_id(); if ($sid === '') { return array(); } $secret = (string) BunStream_Options::get('signing_secret'); if ($secret !== '') { $exp = time() + self::SIGNED_TTL; return array( 'sid' => $sid, 'exp' => $exp, 'sig' => hash_hmac('sha256', $sid . '|' . $uuid . '|' . $exp, $secret), ); } // An account that requires signed ids discards unsigned ones, so // sending it would only put a guessable id in the page. $account = BunStream_Options::account(); if (!empty($account['require_signed_student_id'])) { return array(); } return array('student_id' => $sid); } /** The signed-in user's id by the configured source, or ''. */ public static function student_id() { if (!is_user_logged_in()) { return ''; } $user = wp_get_current_user(); $source = (string) BunStream_Options::get('id_source'); switch ($source) { case 'email': $value = $user->user_email; break; case 'username': $value = $user->user_login; break; case 'none': $value = ''; break; default: $value = (string) $user->ID; } $value = trim((string) apply_filters('bunstream_student_id', $value, $user, $source)); // BunStream accepts up to 120 characters. return function_exists('mb_substr') ? mb_substr($value, 0, 120) : substr($value, 0, 120); } /** A safe CSS length (800px, 100%, 40rem...) or ''. */ public static function css_length($value) { $value = trim((string) $value); if ($value === '') { return ''; } if (preg_match('/^\d+$/', $value)) { return $value . 'px'; } return preg_match('/^\d+(\.\d+)?(px|%|em|rem|vw)$/', $value) ? $value : ''; } }