' . '' . '', esc_attr( $padding ), esc_url( $embed_url ), esc_attr( $title ), esc_attr( $allow ), (int) $width ); } /** * Render player from a share/embed URL or bare share code. * * @param string $url Dadan URL. * @param int $width Width. * @param int $height Height (0 = 16:9 from width). * @param string $title Optional title. * @return string */ public static function render_from_url( $url, $width = 640, $height = 0, $title = '' ) { $embed_url = Dadan_Url::to_embed_url( $url ); if ( ! $embed_url ) { return ''; } $width = Dadan_Url::sanitize_dimension( $width, 640 ); if ( $height <= 0 ) { $height = (int) round( $width * 9 / 16 ); } else { $height = Dadan_Url::sanitize_dimension( $height, (int) round( $width * 9 / 16 ) ); } return self::build_player_html( $embed_url, $width, $height, $title ); } /** * KSES allowlist for build_player_html() output (PC-1). * * @return array> */ public static function kses_player() { return array( 'div' => array( 'class' => true, 'style' => true, ), 'iframe' => array( 'src' => true, 'title' => true, 'style' => true, 'allow' => true, 'allowfullscreen' => true, 'loading' => true, 'referrerpolicy' => true, ), ); } /** * Allow only https://…/video/embed.html?…&link={code} on Dadan hosts. * * @param string $url Candidate embed src. * @return string|null Sanitized URL or null. */ public static function sanitize_embed_src( $url ) { $url = trim( (string) $url ); if ( '' === $url ) { return null; } $parts = wp_parse_url( $url ); if ( ! is_array( $parts ) || empty( $parts['scheme'] ) || empty( $parts['host'] ) || empty( $parts['path'] ) ) { return null; } if ( 'https' !== strtolower( $parts['scheme'] ) ) { return null; } $host = strtolower( $parts['host'] ); $allowed_hosts = array( 'app.dadan.io', 'dadan.io', 'www.dadan.io', 'app.dadan.video', 'dadan.video', 'www.dadan.video' ); if ( ! in_array( $host, $allowed_hosts, true ) ) { return null; } $path = isset( $parts['path'] ) ? $parts['path'] : ''; // Production player. if ( preg_match( '#^/video/embed\.html$#i', $path ) ) { $query = array(); if ( ! empty( $parts['query'] ) ) { parse_str( $parts['query'], $query ); } // DADAN-06: reject non-string link (e.g. ?link[]=) — avoids PHP 8 TypeError. $link = isset( $query['link'] ) && is_string( $query['link'] ) ? $query['link'] : ''; if ( '' === $link || ! preg_match( '/^[A-Za-z0-9_-]{1,64}$/', $link ) ) { return null; } $params = Dadan_Url::EMBED_DEFAULT_PARAMS; $params['link'] = $link; return rtrim( DADAN_API_BASE, '/' ) . '/video/embed.html?' . http_build_query( $params, '', '&', PHP_QUERY_RFC3986 ); } // Legacy /embed/{id} → rewrite to production embed.html. if ( preg_match( '#^/embed/([A-Za-z0-9_-]+)/?$#', $path, $m ) ) { $params = Dadan_Url::EMBED_DEFAULT_PARAMS; $params['link'] = $m[1]; return rtrim( DADAN_API_BASE, '/' ) . '/video/embed.html?' . http_build_query( $params, '', '&', PHP_QUERY_RFC3986 ); } return null; } }