1b82f24afSAndreas Gohr<?php 2b82f24afSAndreas Gohr 3b82f24afSAndreas Gohr/** 4b82f24afSAndreas Gohr * Easily embed videos from various Video Sharing sites 5b82f24afSAndreas Gohr * 6b82f24afSAndreas Gohr * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 7b82f24afSAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org> 8b82f24afSAndreas Gohr */ 9b82f24afSAndreas Gohrclass syntax_plugin_vshare_video extends DokuWiki_Syntax_Plugin 10b82f24afSAndreas Gohr{ 11b82f24afSAndreas Gohr protected $sites; 12b82f24afSAndreas Gohr 13b82f24afSAndreas Gohr protected $sizes = [ 14b82f24afSAndreas Gohr 'small' => [255, 143], 15b82f24afSAndreas Gohr 'medium' => [425, 239], 16b82f24afSAndreas Gohr 'large' => [520, 293], 17b82f24afSAndreas Gohr 'full' => ['100%', ''], 18b82f24afSAndreas Gohr 'half' => ['50%', ''], 19b82f24afSAndreas Gohr ]; 20b82f24afSAndreas Gohr 21b82f24afSAndreas Gohr protected $alignments = [ 22b82f24afSAndreas Gohr 0 => 'none', 23b82f24afSAndreas Gohr 1 => 'right', 24b82f24afSAndreas Gohr 2 => 'left', 25b82f24afSAndreas Gohr 3 => 'center', 26b82f24afSAndreas Gohr ]; 27b82f24afSAndreas Gohr 28b82f24afSAndreas Gohr /** 29b82f24afSAndreas Gohr * Constructor. 30b82f24afSAndreas Gohr * Intitalizes the supported video sites 31b82f24afSAndreas Gohr */ 32b82f24afSAndreas Gohr public function __construct() 33b82f24afSAndreas Gohr { 34b82f24afSAndreas Gohr $this->sites = helper_plugin_vshare::loadSites(); 35b82f24afSAndreas Gohr } 36b82f24afSAndreas Gohr 37b82f24afSAndreas Gohr /** @inheritdoc */ 38b82f24afSAndreas Gohr public function getType() 39b82f24afSAndreas Gohr { 40b82f24afSAndreas Gohr return 'substition'; 41b82f24afSAndreas Gohr } 42b82f24afSAndreas Gohr 43b82f24afSAndreas Gohr /** @inheritdoc */ 44b82f24afSAndreas Gohr public function getPType() 45b82f24afSAndreas Gohr { 46b82f24afSAndreas Gohr return 'block'; 47b82f24afSAndreas Gohr } 48b82f24afSAndreas Gohr 49b82f24afSAndreas Gohr /** @inheritdoc */ 50b82f24afSAndreas Gohr public function getSort() 51b82f24afSAndreas Gohr { 52b82f24afSAndreas Gohr return 159; 53b82f24afSAndreas Gohr } 54b82f24afSAndreas Gohr 55b82f24afSAndreas Gohr /** @inheritdoc */ 56b82f24afSAndreas Gohr public function connectTo($mode) 57b82f24afSAndreas Gohr { 58b82f24afSAndreas Gohr $pattern = join('|', array_keys($this->sites)); 59b82f24afSAndreas Gohr $this->Lexer->addSpecialPattern('\{\{\s?(?:' . $pattern . ')>[^}]*\}\}', $mode, 'plugin_vshare_video'); 60b82f24afSAndreas Gohr } 61b82f24afSAndreas Gohr 62b82f24afSAndreas Gohr /** @inheritdoc */ 63b82f24afSAndreas Gohr public function handle($match, $state, $pos, Doku_Handler $handler) 64b82f24afSAndreas Gohr { 65b82f24afSAndreas Gohr $command = substr($match, 2, -2); 66b82f24afSAndreas Gohr 67b82f24afSAndreas Gohr // title 68b82f24afSAndreas Gohr list($command, $title) = array_pad(explode('|', $command), 2, ''); 69b82f24afSAndreas Gohr $title = trim($title); 70b82f24afSAndreas Gohr 71b82f24afSAndreas Gohr // alignment 72b82f24afSAndreas Gohr $align = 0; 73b82f24afSAndreas Gohr if (substr($command, 0, 1) == ' ') $align += 1; 74b82f24afSAndreas Gohr if (substr($command, -1) == ' ') $align += 2; 75b82f24afSAndreas Gohr $command = trim($command); 76b82f24afSAndreas Gohr 77b82f24afSAndreas Gohr // get site and video 78b82f24afSAndreas Gohr list($site, $vid) = explode('>', $command); 79b82f24afSAndreas Gohr if (!$this->sites[$site]) return null; // unknown site 80b82f24afSAndreas Gohr if (!$vid) return null; // no video!? 81b82f24afSAndreas Gohr 82b82f24afSAndreas Gohr // what size? 83b82f24afSAndreas Gohr list($vid, $pstr) = array_pad(explode('?', $vid, 2), 2, ''); 84b82f24afSAndreas Gohr parse_str($pstr, $userparams); 85b82f24afSAndreas Gohr list($width, $height) = $this->parseSize($userparams); 86b82f24afSAndreas Gohr 87b82f24afSAndreas Gohr // get URL 88b82f24afSAndreas Gohr $url = $this->insertPlaceholders($this->sites[$site]['url'], $vid, $width, $height); 89b82f24afSAndreas Gohr list($url, $urlpstr) = array_pad(explode('?', $url, 2), 2, ''); 90b82f24afSAndreas Gohr parse_str($urlpstr, $urlparams); 91b82f24afSAndreas Gohr 92b82f24afSAndreas Gohr // merge parameters 93b82f24afSAndreas Gohr $params = array_merge($urlparams, $userparams); 94b82f24afSAndreas Gohr $url = $url . '?' . buildURLparams($params, '&'); 95b82f24afSAndreas Gohr 96b82f24afSAndreas Gohr return array( 97b82f24afSAndreas Gohr 'site' => $site, 98b82f24afSAndreas Gohr 'domain' => parse_url($url, PHP_URL_HOST), 99b82f24afSAndreas Gohr 'video' => $vid, 100b82f24afSAndreas Gohr 'url' => $url, 101b82f24afSAndreas Gohr 'align' => $this->alignments[$align], 102b82f24afSAndreas Gohr 'width' => $width, 103b82f24afSAndreas Gohr 'height' => $height, 104b82f24afSAndreas Gohr 'title' => $title, 105b82f24afSAndreas Gohr ); 106b82f24afSAndreas Gohr } 107b82f24afSAndreas Gohr 108b82f24afSAndreas Gohr /** @inheritdoc */ 109b82f24afSAndreas Gohr public function render($mode, Doku_Renderer $R, $data) 110b82f24afSAndreas Gohr { 111b82f24afSAndreas Gohr if ($mode != 'xhtml') return false; 112b82f24afSAndreas Gohr if (is_null($data)) return false; 113b82f24afSAndreas Gohr 114b82f24afSAndreas Gohr if (is_a($R, 'renderer_plugin_dw2pdf')) { 115b82f24afSAndreas Gohr $R->doc .= $this->pdf($data); 116b82f24afSAndreas Gohr } else { 117b82f24afSAndreas Gohr $R->doc .= $this->iframe($data, $this->getConf('gdpr') ? 'div' : 'iframe'); 118b82f24afSAndreas Gohr } 119b82f24afSAndreas Gohr return true; 120b82f24afSAndreas Gohr } 121b82f24afSAndreas Gohr 122b82f24afSAndreas Gohr /** 123b82f24afSAndreas Gohr * Prepare the HTML for output of the embed iframe 124b82f24afSAndreas Gohr * @param array $data 125b82f24afSAndreas Gohr * @param string $element Can be used to not directly embed the iframe 126b82f24afSAndreas Gohr * @return string 127b82f24afSAndreas Gohr */ 128b82f24afSAndreas Gohr public function iframe($data, $element = 'iframe') 129b82f24afSAndreas Gohr { 130*548f7f69SAndreas Gohr $attributes = [ 131b82f24afSAndreas Gohr 'src' => $data['url'], 132b82f24afSAndreas Gohr 'width' => $data['width'], 133b82f24afSAndreas Gohr 'height' => $data['height'], 134b82f24afSAndreas Gohr 'style' => $this->sizeToStyle($data['width'], $data['height']), 135b82f24afSAndreas Gohr 'class' => 'vshare vshare__' . $data['align'], 136b82f24afSAndreas Gohr 'allowfullscreen' => '', 137b82f24afSAndreas Gohr 'frameborder' => 0, 138b82f24afSAndreas Gohr 'scrolling' => 'no', 139b82f24afSAndreas Gohr 'data-domain' => $data['domain'], 140*548f7f69SAndreas Gohr 'referrerpolicy' => 'no-referrer', 141*548f7f69SAndreas Gohr ]; 142*548f7f69SAndreas Gohr if($this->getConf('extrahard')) { 143*548f7f69SAndreas Gohr $attributes = array_merge($attributes, $this->hardenedIframeAttributes()); 144*548f7f69SAndreas Gohr } 145*548f7f69SAndreas Gohr 146*548f7f69SAndreas Gohr return "<$element " 147*548f7f69SAndreas Gohr . buildAttributes($attributes) 148b82f24afSAndreas Gohr . '><h3>' . hsc($data['title']) . "</h3></$element>"; 149b82f24afSAndreas Gohr } 150b82f24afSAndreas Gohr 151b82f24afSAndreas Gohr /** 152b82f24afSAndreas Gohr * Create a style attribute for the given size 153b82f24afSAndreas Gohr * 154b82f24afSAndreas Gohr * @param int|string $width 155b82f24afSAndreas Gohr * @param int|string $height 156b82f24afSAndreas Gohr * @return string 157b82f24afSAndreas Gohr */ 158b82f24afSAndreas Gohr public function sizeToStyle($width, $height) 159b82f24afSAndreas Gohr { 160b82f24afSAndreas Gohr // no unit? use px 161b82f24afSAndreas Gohr if ($width && $width == (int)$width) { 162b82f24afSAndreas Gohr $width = $width . 'px'; 163b82f24afSAndreas Gohr } 164b82f24afSAndreas Gohr // no unit? use px 165b82f24afSAndreas Gohr if ($height && $height == (int)$height) { 166b82f24afSAndreas Gohr $height = $height . 'px'; 167b82f24afSAndreas Gohr } 168b82f24afSAndreas Gohr 169b82f24afSAndreas Gohr $style = ''; 170b82f24afSAndreas Gohr if ($width) $style .= 'width:' . $width . ';'; 171b82f24afSAndreas Gohr if ($height) $style .= 'height:' . $height . ';'; 172b82f24afSAndreas Gohr return $style; 173b82f24afSAndreas Gohr } 174b82f24afSAndreas Gohr 175b82f24afSAndreas Gohr /** 176b82f24afSAndreas Gohr * Prepare the HTML for output in PDF exports 177b82f24afSAndreas Gohr * 178b82f24afSAndreas Gohr * @param array $data 179b82f24afSAndreas Gohr * @return string 180b82f24afSAndreas Gohr */ 181b82f24afSAndreas Gohr public function pdf($data) 182b82f24afSAndreas Gohr { 183b82f24afSAndreas Gohr $html = '<div class="vshare vshare__' . $data['align'] . '" 184b82f24afSAndreas Gohr width="' . $data['width'] . '" 185b82f24afSAndreas Gohr height="' . $data['height'] . '">'; 186b82f24afSAndreas Gohr 187b82f24afSAndreas Gohr $html .= '<a href="' . $data['url'] . '" class="vshare">'; 188b82f24afSAndreas Gohr $html .= '<img src="' . DOKU_BASE . 'lib/plugins/vshare/video.png" />'; 189b82f24afSAndreas Gohr $html .= '</a>'; 190b82f24afSAndreas Gohr 191b82f24afSAndreas Gohr $html .= '<br />'; 192b82f24afSAndreas Gohr 193b82f24afSAndreas Gohr $html .= '<a href="' . $data['url'] . '" class="vshare">'; 194b82f24afSAndreas Gohr $html .= ($data['title'] ? hsc($data['title']) : 'Video'); 195b82f24afSAndreas Gohr $html .= '</a>'; 196b82f24afSAndreas Gohr 197b82f24afSAndreas Gohr $html .= '</div>'; 198b82f24afSAndreas Gohr 199b82f24afSAndreas Gohr return $html; 200b82f24afSAndreas Gohr } 201b82f24afSAndreas Gohr 202b82f24afSAndreas Gohr /** 203b82f24afSAndreas Gohr * Fill the placeholders in the given URL 204b82f24afSAndreas Gohr * 205b82f24afSAndreas Gohr * @param string $url 206b82f24afSAndreas Gohr * @param string $vid 207b82f24afSAndreas Gohr * @param int|string $width 208b82f24afSAndreas Gohr * @param int|string $height 209b82f24afSAndreas Gohr * @return string 210b82f24afSAndreas Gohr */ 211b82f24afSAndreas Gohr public function insertPlaceholders($url, $vid, $width, $height) 212b82f24afSAndreas Gohr { 213b82f24afSAndreas Gohr global $INPUT; 214b82f24afSAndreas Gohr $url = str_replace('@VIDEO@', rawurlencode($vid), $url); 215b82f24afSAndreas Gohr $url = str_replace('@DOMAIN@', rawurlencode($INPUT->server->str('HTTP_HOST')), $url); 216b82f24afSAndreas Gohr $url = str_replace('@WIDTH@', $width, $url); 217b82f24afSAndreas Gohr $url = str_replace('@HEIGHT@', $height, $url); 218b82f24afSAndreas Gohr 219b82f24afSAndreas Gohr return $url; 220b82f24afSAndreas Gohr } 221b82f24afSAndreas Gohr 222b82f24afSAndreas Gohr /** 223b82f24afSAndreas Gohr * Extract the wanted size from the parameter list 224b82f24afSAndreas Gohr * 225b82f24afSAndreas Gohr * @param array $params 226b82f24afSAndreas Gohr * @return int[] 227b82f24afSAndreas Gohr */ 228b82f24afSAndreas Gohr public function parseSize(&$params) 229b82f24afSAndreas Gohr { 230b82f24afSAndreas Gohr $known = join('|', array_keys($this->sizes)); 231b82f24afSAndreas Gohr 232b82f24afSAndreas Gohr foreach ($params as $key => $value) { 233b82f24afSAndreas Gohr if (preg_match("/^((\d+)x(\d+))|($known)\$/i", $key, $m)) { 234b82f24afSAndreas Gohr unset($params[$key]); 235b82f24afSAndreas Gohr if (isset($m[4])) { 236b82f24afSAndreas Gohr return $this->sizes[strtolower($m[4])]; 237b82f24afSAndreas Gohr } else { 238b82f24afSAndreas Gohr return [$m[2], $m[3]]; 239b82f24afSAndreas Gohr } 240b82f24afSAndreas Gohr } 241b82f24afSAndreas Gohr } 242b82f24afSAndreas Gohr 243b82f24afSAndreas Gohr // default 244b82f24afSAndreas Gohr return $this->sizes['medium']; 245b82f24afSAndreas Gohr } 246*548f7f69SAndreas Gohr 247*548f7f69SAndreas Gohr /** 248*548f7f69SAndreas Gohr * Get additional attributes to set on the iframe to harden 249*548f7f69SAndreas Gohr * 250*548f7f69SAndreas Gohr * @link https://dustri.org/b/youtube-video-embedding-harm-reduction.html 251*548f7f69SAndreas Gohr * @return array 252*548f7f69SAndreas Gohr */ 253*548f7f69SAndreas Gohr protected function hardenedIframeAttributes() 254*548f7f69SAndreas Gohr { 255*548f7f69SAndreas Gohr $disallow = [ 256*548f7f69SAndreas Gohr 'accelerometer', 257*548f7f69SAndreas Gohr 'ambient-light-sensor', 258*548f7f69SAndreas Gohr 'autoplay', 259*548f7f69SAndreas Gohr 'battery', 260*548f7f69SAndreas Gohr 'browsing-topics', 261*548f7f69SAndreas Gohr 'camera', 262*548f7f69SAndreas Gohr 'display-capture', 263*548f7f69SAndreas Gohr 'domain-agent', 264*548f7f69SAndreas Gohr 'document-domain', 265*548f7f69SAndreas Gohr 'encrypted-media', 266*548f7f69SAndreas Gohr 'execution-while-not-rendered', 267*548f7f69SAndreas Gohr 'execution-while-out-of-viewport', 268*548f7f69SAndreas Gohr 'gamepad', 269*548f7f69SAndreas Gohr 'geolocation', 270*548f7f69SAndreas Gohr 'gyroscope', 271*548f7f69SAndreas Gohr 'hid', 272*548f7f69SAndreas Gohr 'identity-credentials-get', 273*548f7f69SAndreas Gohr 'idle-detection', 274*548f7f69SAndreas Gohr 'local-fonts', 275*548f7f69SAndreas Gohr 'magnetometer', 276*548f7f69SAndreas Gohr 'microphone', 277*548f7f69SAndreas Gohr 'midi', 278*548f7f69SAndreas Gohr 'otp-credentials', 279*548f7f69SAndreas Gohr 'payment', 280*548f7f69SAndreas Gohr 'picture-in-picture', 281*548f7f69SAndreas Gohr 'publickey-credentials-create', 282*548f7f69SAndreas Gohr 'publickey-credentials-get', 283*548f7f69SAndreas Gohr 'screen-wake-lock', 284*548f7f69SAndreas Gohr 'serial', 285*548f7f69SAndreas Gohr 'speaker-selection', 286*548f7f69SAndreas Gohr 'usb', 287*548f7f69SAndreas Gohr 'window-management', 288*548f7f69SAndreas Gohr 'xr-spatial-tracking', 289*548f7f69SAndreas Gohr ]; 290*548f7f69SAndreas Gohr 291*548f7f69SAndreas Gohr $disallow = join('; ', array_map(static fn($v) => "$v 'none'", $disallow)); 292*548f7f69SAndreas Gohr 293*548f7f69SAndreas Gohr return [ 294*548f7f69SAndreas Gohr 'credentialless' => '', 295*548f7f69SAndreas Gohr 'sandbox' => 'allow-scripts allow-same-origin', 296*548f7f69SAndreas Gohr 'allow' => $disallow, 297*548f7f69SAndreas Gohr 'csp' => 'sandbox allow-scripts allow-same-origin' 298*548f7f69SAndreas Gohr ]; 299*548f7f69SAndreas Gohr } 300b82f24afSAndreas Gohr} 301