1b82f24afSAndreas Gohr<?php 2b82f24afSAndreas Gohr 35895dcbaSAndreas Gohruse dokuwiki\Extension\SyntaxPlugin; 45895dcbaSAndreas Gohr 5b82f24afSAndreas Gohr/** 6b82f24afSAndreas Gohr * Easily embed videos from various Video Sharing sites 7b82f24afSAndreas Gohr * 8b82f24afSAndreas Gohr * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 9b82f24afSAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org> 10b82f24afSAndreas Gohr */ 115895dcbaSAndreas Gohrclass syntax_plugin_vshare_video extends SyntaxPlugin 12b82f24afSAndreas Gohr{ 13b82f24afSAndreas Gohr protected $sites; 14b82f24afSAndreas Gohr 15b82f24afSAndreas Gohr protected $sizes = [ 16b82f24afSAndreas Gohr 'small' => [255, 143], 17b82f24afSAndreas Gohr 'medium' => [425, 239], 18b82f24afSAndreas Gohr 'large' => [520, 293], 19b82f24afSAndreas Gohr 'full' => ['100%', ''], 20b82f24afSAndreas Gohr 'half' => ['50%', ''], 21b82f24afSAndreas Gohr ]; 22b82f24afSAndreas Gohr 23b82f24afSAndreas Gohr protected $alignments = [ 24b82f24afSAndreas Gohr 0 => 'none', 25b82f24afSAndreas Gohr 1 => 'right', 26b82f24afSAndreas Gohr 2 => 'left', 27b82f24afSAndreas Gohr 3 => 'center', 28b82f24afSAndreas Gohr ]; 29b82f24afSAndreas Gohr 30b82f24afSAndreas Gohr /** 31b82f24afSAndreas Gohr * Constructor. 32b82f24afSAndreas Gohr * Intitalizes the supported video sites 33b82f24afSAndreas Gohr */ 34b82f24afSAndreas Gohr public function __construct() 35b82f24afSAndreas Gohr { 36b82f24afSAndreas Gohr $this->sites = helper_plugin_vshare::loadSites(); 37b82f24afSAndreas Gohr } 38b82f24afSAndreas Gohr 39b82f24afSAndreas Gohr /** @inheritdoc */ 40b82f24afSAndreas Gohr public function getType() 41b82f24afSAndreas Gohr { 42b82f24afSAndreas Gohr return 'substition'; 43b82f24afSAndreas Gohr } 44b82f24afSAndreas Gohr 45b82f24afSAndreas Gohr /** @inheritdoc */ 46b82f24afSAndreas Gohr public function getPType() 47b82f24afSAndreas Gohr { 48b82f24afSAndreas Gohr return 'block'; 49b82f24afSAndreas Gohr } 50b82f24afSAndreas Gohr 51b82f24afSAndreas Gohr /** @inheritdoc */ 52b82f24afSAndreas Gohr public function getSort() 53b82f24afSAndreas Gohr { 54b82f24afSAndreas Gohr return 159; 55b82f24afSAndreas Gohr } 56b82f24afSAndreas Gohr 57b82f24afSAndreas Gohr /** @inheritdoc */ 58b82f24afSAndreas Gohr public function connectTo($mode) 59b82f24afSAndreas Gohr { 605895dcbaSAndreas Gohr $pattern = implode('|', array_keys($this->sites)); 61b82f24afSAndreas Gohr $this->Lexer->addSpecialPattern('\{\{\s?(?:' . $pattern . ')>[^}]*\}\}', $mode, 'plugin_vshare_video'); 62b82f24afSAndreas Gohr } 63b82f24afSAndreas Gohr 64b82f24afSAndreas Gohr /** @inheritdoc */ 65b82f24afSAndreas Gohr public function handle($match, $state, $pos, Doku_Handler $handler) 66b82f24afSAndreas Gohr { 67b82f24afSAndreas Gohr $command = substr($match, 2, -2); 68b82f24afSAndreas Gohr 69b82f24afSAndreas Gohr // title 705895dcbaSAndreas Gohr [$command, $title] = sexplode('|', $command, 2, ''); 71b82f24afSAndreas Gohr $title = trim($title); 72b82f24afSAndreas Gohr 73b82f24afSAndreas Gohr // alignment 74b82f24afSAndreas Gohr $align = 0; 755895dcbaSAndreas Gohr if (substr($command, 0, 1) == ' ') ++$align; 76b82f24afSAndreas Gohr if (substr($command, -1) == ' ') $align += 2; 77b82f24afSAndreas Gohr $command = trim($command); 78b82f24afSAndreas Gohr 79b82f24afSAndreas Gohr // get site and video 805895dcbaSAndreas Gohr [$site, $vid] = explode('>', $command); 81b82f24afSAndreas Gohr if (!$this->sites[$site]) return null; // unknown site 82b82f24afSAndreas Gohr if (!$vid) return null; // no video!? 83b82f24afSAndreas Gohr 84b82f24afSAndreas Gohr // what size? 855895dcbaSAndreas Gohr [$vid, $pstr] = sexplode('?', $vid, 2, ''); 86b82f24afSAndreas Gohr parse_str($pstr, $userparams); 875895dcbaSAndreas Gohr [$width, $height] = $this->parseSize($userparams); 88b82f24afSAndreas Gohr 89b82f24afSAndreas Gohr // get URL 90b82f24afSAndreas Gohr $url = $this->insertPlaceholders($this->sites[$site]['url'], $vid, $width, $height); 915895dcbaSAndreas Gohr [$url, $urlpstr] = sexplode('?', $url, 2, ''); 92b82f24afSAndreas Gohr parse_str($urlpstr, $urlparams); 93b82f24afSAndreas Gohr 94b82f24afSAndreas Gohr // merge parameters 95b82f24afSAndreas Gohr $params = array_merge($urlparams, $userparams); 96b82f24afSAndreas Gohr $url = $url . '?' . buildURLparams($params, '&'); 97b82f24afSAndreas Gohr 985895dcbaSAndreas Gohr return [ 99b82f24afSAndreas Gohr 'site' => $site, 100b82f24afSAndreas Gohr 'domain' => parse_url($url, PHP_URL_HOST), 101b82f24afSAndreas Gohr 'video' => $vid, 102b82f24afSAndreas Gohr 'url' => $url, 103b82f24afSAndreas Gohr 'align' => $this->alignments[$align], 104b82f24afSAndreas Gohr 'width' => $width, 105b82f24afSAndreas Gohr 'height' => $height, 1065895dcbaSAndreas Gohr 'title' => $title 1075895dcbaSAndreas Gohr ]; 108b82f24afSAndreas Gohr } 109b82f24afSAndreas Gohr 110b82f24afSAndreas Gohr /** @inheritdoc */ 111b82f24afSAndreas Gohr public function render($mode, Doku_Renderer $R, $data) 112b82f24afSAndreas Gohr { 113b82f24afSAndreas Gohr if ($mode != 'xhtml') return false; 114b82f24afSAndreas Gohr if (is_null($data)) return false; 115b82f24afSAndreas Gohr 116b82f24afSAndreas Gohr if (is_a($R, 'renderer_plugin_dw2pdf')) { 117b82f24afSAndreas Gohr $R->doc .= $this->pdf($data); 118b82f24afSAndreas Gohr } else { 119b82f24afSAndreas Gohr $R->doc .= $this->iframe($data, $this->getConf('gdpr') ? 'div' : 'iframe'); 120b82f24afSAndreas Gohr } 121b82f24afSAndreas Gohr return true; 122b82f24afSAndreas Gohr } 123b82f24afSAndreas Gohr 124b82f24afSAndreas Gohr /** 125b82f24afSAndreas Gohr * Prepare the HTML for output of the embed iframe 126b82f24afSAndreas Gohr * @param array $data 127b82f24afSAndreas Gohr * @param string $element Can be used to not directly embed the iframe 128b82f24afSAndreas Gohr * @return string 129b82f24afSAndreas Gohr */ 130b82f24afSAndreas Gohr public function iframe($data, $element = 'iframe') 131b82f24afSAndreas Gohr { 132548f7f69SAndreas Gohr $attributes = [ 133b82f24afSAndreas Gohr 'src' => $data['url'], 134b82f24afSAndreas Gohr 'width' => $data['width'], 135b82f24afSAndreas Gohr 'height' => $data['height'], 136b82f24afSAndreas Gohr 'style' => $this->sizeToStyle($data['width'], $data['height']), 137b82f24afSAndreas Gohr 'class' => 'vshare vshare__' . $data['align'], 138b82f24afSAndreas Gohr 'allowfullscreen' => '', 139b82f24afSAndreas Gohr 'frameborder' => 0, 140b82f24afSAndreas Gohr 'scrolling' => 'no', 141b82f24afSAndreas Gohr 'data-domain' => $data['domain'], 142e7e00d33SAndreas Gohr 'loading' => 'lazy', 143548f7f69SAndreas Gohr ]; 144548f7f69SAndreas Gohr if ($this->getConf('extrahard')) { 145548f7f69SAndreas Gohr $attributes = array_merge($attributes, $this->hardenedIframeAttributes()); 146548f7f69SAndreas Gohr } 147548f7f69SAndreas Gohr 148548f7f69SAndreas Gohr return "<$element " 149548f7f69SAndreas Gohr . buildAttributes($attributes) 150b82f24afSAndreas Gohr . '><h3>' . hsc($data['title']) . "</h3></$element>"; 151b82f24afSAndreas Gohr } 152b82f24afSAndreas Gohr 153b82f24afSAndreas Gohr /** 154b82f24afSAndreas Gohr * Create a style attribute for the given size 155b82f24afSAndreas Gohr * 156b82f24afSAndreas Gohr * @param int|string $width 157b82f24afSAndreas Gohr * @param int|string $height 158b82f24afSAndreas Gohr * @return string 159b82f24afSAndreas Gohr */ 160b82f24afSAndreas Gohr public function sizeToStyle($width, $height) 161b82f24afSAndreas Gohr { 162b82f24afSAndreas Gohr // no unit? use px 163b82f24afSAndreas Gohr if ($width && $width == (int)$width) { 1645895dcbaSAndreas Gohr $width .= 'px'; 165b82f24afSAndreas Gohr } 166b82f24afSAndreas Gohr // no unit? use px 167b82f24afSAndreas Gohr if ($height && $height == (int)$height) { 1685895dcbaSAndreas Gohr $height .= 'px'; 169b82f24afSAndreas Gohr } 170b82f24afSAndreas Gohr 171b82f24afSAndreas Gohr $style = ''; 172b82f24afSAndreas Gohr if ($width) $style .= 'width:' . $width . ';'; 173b82f24afSAndreas Gohr if ($height) $style .= 'height:' . $height . ';'; 174b82f24afSAndreas Gohr return $style; 175b82f24afSAndreas Gohr } 176b82f24afSAndreas Gohr 177b82f24afSAndreas Gohr /** 178b82f24afSAndreas Gohr * Prepare the HTML for output in PDF exports 179b82f24afSAndreas Gohr * 180b82f24afSAndreas Gohr * @param array $data 181b82f24afSAndreas Gohr * @return string 182b82f24afSAndreas Gohr */ 183b82f24afSAndreas Gohr public function pdf($data) 184b82f24afSAndreas Gohr { 185b82f24afSAndreas Gohr $html = '<div class="vshare vshare__' . $data['align'] . '" 186b82f24afSAndreas Gohr width="' . $data['width'] . '" 187b82f24afSAndreas Gohr height="' . $data['height'] . '">'; 188b82f24afSAndreas Gohr 189b82f24afSAndreas Gohr $html .= '<a href="' . $data['url'] . '" class="vshare">'; 190b82f24afSAndreas Gohr $html .= '<img src="' . DOKU_BASE . 'lib/plugins/vshare/video.png" />'; 191b82f24afSAndreas Gohr $html .= '</a>'; 192b82f24afSAndreas Gohr 193b82f24afSAndreas Gohr $html .= '<br />'; 194b82f24afSAndreas Gohr 195b82f24afSAndreas Gohr $html .= '<a href="' . $data['url'] . '" class="vshare">'; 196b82f24afSAndreas Gohr $html .= ($data['title'] ? hsc($data['title']) : 'Video'); 197b82f24afSAndreas Gohr $html .= '</a>'; 198b82f24afSAndreas Gohr 199b82f24afSAndreas Gohr $html .= '</div>'; 200b82f24afSAndreas Gohr 201b82f24afSAndreas Gohr return $html; 202b82f24afSAndreas Gohr } 203b82f24afSAndreas Gohr 204b82f24afSAndreas Gohr /** 205b82f24afSAndreas Gohr * Fill the placeholders in the given URL 206b82f24afSAndreas Gohr * 207b82f24afSAndreas Gohr * @param string $url 208b82f24afSAndreas Gohr * @param string $vid 209b82f24afSAndreas Gohr * @param int|string $width 210b82f24afSAndreas Gohr * @param int|string $height 211b82f24afSAndreas Gohr * @return string 212b82f24afSAndreas Gohr */ 213b82f24afSAndreas Gohr public function insertPlaceholders($url, $vid, $width, $height) 214b82f24afSAndreas Gohr { 215b82f24afSAndreas Gohr global $INPUT; 216b82f24afSAndreas Gohr $url = str_replace('@VIDEO@', rawurlencode($vid), $url); 217b82f24afSAndreas Gohr $url = str_replace('@DOMAIN@', rawurlencode($INPUT->server->str('HTTP_HOST')), $url); 218b82f24afSAndreas Gohr $url = str_replace('@WIDTH@', $width, $url); 219b82f24afSAndreas Gohr $url = str_replace('@HEIGHT@', $height, $url); 220b82f24afSAndreas Gohr 221b82f24afSAndreas Gohr return $url; 222b82f24afSAndreas Gohr } 223b82f24afSAndreas Gohr 224b82f24afSAndreas Gohr /** 225b82f24afSAndreas Gohr * Extract the wanted size from the parameter list 226b82f24afSAndreas Gohr * 227b82f24afSAndreas Gohr * @param array $params 228b82f24afSAndreas Gohr * @return int[] 229b82f24afSAndreas Gohr */ 230b82f24afSAndreas Gohr public function parseSize(&$params) 231b82f24afSAndreas Gohr { 2325895dcbaSAndreas Gohr $known = implode('|', array_keys($this->sizes)); 233b82f24afSAndreas Gohr 2345895dcbaSAndreas Gohr foreach (array_keys($params) as $key) { 235b82f24afSAndreas Gohr if (preg_match("/^((\d+)x(\d+))|($known)\$/i", $key, $m)) { 236b82f24afSAndreas Gohr unset($params[$key]); 237b82f24afSAndreas Gohr if (isset($m[4])) { 238b82f24afSAndreas Gohr return $this->sizes[strtolower($m[4])]; 239b82f24afSAndreas Gohr } else { 240b82f24afSAndreas Gohr return [$m[2], $m[3]]; 241b82f24afSAndreas Gohr } 242b82f24afSAndreas Gohr } 243b82f24afSAndreas Gohr } 244b82f24afSAndreas Gohr 245b82f24afSAndreas Gohr // default 246b82f24afSAndreas Gohr return $this->sizes['medium']; 247b82f24afSAndreas Gohr } 248548f7f69SAndreas Gohr 249548f7f69SAndreas Gohr /** 250548f7f69SAndreas Gohr * Get additional attributes to set on the iframe to harden 251548f7f69SAndreas Gohr * 252548f7f69SAndreas Gohr * @link https://dustri.org/b/youtube-video-embedding-harm-reduction.html 253548f7f69SAndreas Gohr * @return array 254548f7f69SAndreas Gohr */ 255548f7f69SAndreas Gohr protected function hardenedIframeAttributes() 256548f7f69SAndreas Gohr { 257548f7f69SAndreas Gohr $disallow = [ 258548f7f69SAndreas Gohr 'accelerometer', 259548f7f69SAndreas Gohr 'ambient-light-sensor', 260548f7f69SAndreas Gohr 'autoplay', 261548f7f69SAndreas Gohr 'battery', 262548f7f69SAndreas Gohr 'browsing-topics', 263548f7f69SAndreas Gohr 'camera', 264548f7f69SAndreas Gohr 'display-capture', 265548f7f69SAndreas Gohr 'domain-agent', 266548f7f69SAndreas Gohr 'document-domain', 267548f7f69SAndreas Gohr 'encrypted-media', 268548f7f69SAndreas Gohr 'execution-while-not-rendered', 269548f7f69SAndreas Gohr 'execution-while-out-of-viewport', 270548f7f69SAndreas Gohr 'gamepad', 271548f7f69SAndreas Gohr 'geolocation', 272548f7f69SAndreas Gohr 'gyroscope', 273548f7f69SAndreas Gohr 'hid', 274548f7f69SAndreas Gohr 'identity-credentials-get', 275548f7f69SAndreas Gohr 'idle-detection', 276548f7f69SAndreas Gohr 'local-fonts', 277548f7f69SAndreas Gohr 'magnetometer', 278548f7f69SAndreas Gohr 'microphone', 279548f7f69SAndreas Gohr 'midi', 280548f7f69SAndreas Gohr 'otp-credentials', 281548f7f69SAndreas Gohr 'payment', 282548f7f69SAndreas Gohr 'picture-in-picture', 283548f7f69SAndreas Gohr 'publickey-credentials-create', 284548f7f69SAndreas Gohr 'publickey-credentials-get', 285548f7f69SAndreas Gohr 'screen-wake-lock', 286548f7f69SAndreas Gohr 'serial', 287548f7f69SAndreas Gohr 'speaker-selection', 288548f7f69SAndreas Gohr 'usb', 289548f7f69SAndreas Gohr 'window-management', 290548f7f69SAndreas Gohr 'xr-spatial-tracking', 291548f7f69SAndreas Gohr ]; 292548f7f69SAndreas Gohr 2935895dcbaSAndreas Gohr $disallow = implode('; ', array_map(static fn($v) => "$v 'none'", $disallow)); 294548f7f69SAndreas Gohr 295548f7f69SAndreas Gohr return [ 296548f7f69SAndreas Gohr 'credentialless' => '', 297548f7f69SAndreas Gohr 'sandbox' => 'allow-scripts allow-same-origin', 298548f7f69SAndreas Gohr 'allow' => $disallow, 299*08b6034eSAndreas Gohr 'csp' => 'sandbox allow-scripts allow-same-origin', 300*08b6034eSAndreas Gohr 'referrerpolicy' => 'no-referrer', 301548f7f69SAndreas Gohr ]; 302548f7f69SAndreas Gohr } 303b82f24afSAndreas Gohr} 304