1*317bdfc2SAndreas Gohr<?php 2*317bdfc2SAndreas Gohr 3*317bdfc2SAndreas Gohr/** 4*317bdfc2SAndreas Gohr * Action component of diagrams plugin 5*317bdfc2SAndreas Gohr * 6*317bdfc2SAndreas Gohr * FIXME move out all mediafile related stuff to a separate class and make it check the mode config 7*317bdfc2SAndreas Gohr */ 8*317bdfc2SAndreas Gohrclass action_plugin_diagrams_action extends DokuWiki_Action_Plugin 9*317bdfc2SAndreas Gohr{ 10*317bdfc2SAndreas Gohr 11*317bdfc2SAndreas Gohr /** 12*317bdfc2SAndreas Gohr * Registers a callback function for a given event 13*317bdfc2SAndreas Gohr * 14*317bdfc2SAndreas Gohr * @param \Doku_Event_Handler $controller 15*317bdfc2SAndreas Gohr */ 16*317bdfc2SAndreas Gohr public function register(Doku_Event_Handler $controller) 17*317bdfc2SAndreas Gohr { 18*317bdfc2SAndreas Gohr $controller->register_hook('DOKUWIKI_STARTED', 'AFTER', $this, 'addJsinfo'); 19*317bdfc2SAndreas Gohr $controller->register_hook('MEDIAMANAGER_STARTED', 'AFTER', $this, 'addJsinfo'); 20*317bdfc2SAndreas Gohr $controller->register_hook('DOKUWIKI_STARTED', 'AFTER', $this, 'checkConf'); 21*317bdfc2SAndreas Gohr $controller->register_hook('AJAX_CALL_UNKNOWN', 'BEFORE', $this, 'handleAjaxImages'); 22*317bdfc2SAndreas Gohr $controller->register_hook('AJAX_CALL_UNKNOWN', 'BEFORE', $this, 'handleAjaxAcl'); 23*317bdfc2SAndreas Gohr $controller->register_hook('MEDIA_SENDFILE', 'BEFORE', $this, 'handleCSP'); 24*317bdfc2SAndreas Gohr } 25*317bdfc2SAndreas Gohr 26*317bdfc2SAndreas Gohr /** 27*317bdfc2SAndreas Gohr * Add data to JSINFO: full service URL and security token used for uploading 28*317bdfc2SAndreas Gohr * 29*317bdfc2SAndreas Gohr * @param Doku_Event $event 30*317bdfc2SAndreas Gohr */ 31*317bdfc2SAndreas Gohr public function addJsinfo(Doku_Event $event) 32*317bdfc2SAndreas Gohr { 33*317bdfc2SAndreas Gohr global $JSINFO; 34*317bdfc2SAndreas Gohr $JSINFO['sectok'] = getSecurityToken(); 35*317bdfc2SAndreas Gohr $JSINFO['plugins']['diagrams']['service_url'] = $this->getConf('service_url'); 36*317bdfc2SAndreas Gohr } 37*317bdfc2SAndreas Gohr 38*317bdfc2SAndreas Gohr /** 39*317bdfc2SAndreas Gohr * Check if DokuWiki is properly configured to handle SVG diagrams 40*317bdfc2SAndreas Gohr * 41*317bdfc2SAndreas Gohr * @param Doku_Event $event 42*317bdfc2SAndreas Gohr */ 43*317bdfc2SAndreas Gohr public function checkConf(Doku_Event $event) 44*317bdfc2SAndreas Gohr { 45*317bdfc2SAndreas Gohr $mime = getMimeTypes(); 46*317bdfc2SAndreas Gohr if (!array_key_exists('svg', $mime) || $mime['svg'] !== 'image/svg+xml') { 47*317bdfc2SAndreas Gohr msg($this->getLang('missingConfig'), -1); 48*317bdfc2SAndreas Gohr } 49*317bdfc2SAndreas Gohr } 50*317bdfc2SAndreas Gohr 51*317bdfc2SAndreas Gohr /** 52*317bdfc2SAndreas Gohr * Check all supplied images and return only editable diagrams 53*317bdfc2SAndreas Gohr * 54*317bdfc2SAndreas Gohr * @param Doku_Event $event 55*317bdfc2SAndreas Gohr */ 56*317bdfc2SAndreas Gohr public function handleAjaxImages(Doku_Event $event) 57*317bdfc2SAndreas Gohr { 58*317bdfc2SAndreas Gohr if ($event->data !== 'plugin_diagrams_images') return; 59*317bdfc2SAndreas Gohr $event->preventDefault(); 60*317bdfc2SAndreas Gohr $event->stopPropagation(); 61*317bdfc2SAndreas Gohr 62*317bdfc2SAndreas Gohr global $INPUT; 63*317bdfc2SAndreas Gohr $images = $INPUT->arr('images'); 64*317bdfc2SAndreas Gohr 65*317bdfc2SAndreas Gohr echo json_encode($this->editableDiagrams($images)); 66*317bdfc2SAndreas Gohr } 67*317bdfc2SAndreas Gohr 68*317bdfc2SAndreas Gohr /** 69*317bdfc2SAndreas Gohr * Check ACL for supplied namespace 70*317bdfc2SAndreas Gohr * 71*317bdfc2SAndreas Gohr * @param Doku_Event $event 72*317bdfc2SAndreas Gohr */ 73*317bdfc2SAndreas Gohr public function handleAjaxAcl(Doku_Event $event) 74*317bdfc2SAndreas Gohr { 75*317bdfc2SAndreas Gohr if ($event->data !== 'plugin_diagrams_acl') return; 76*317bdfc2SAndreas Gohr $event->preventDefault(); 77*317bdfc2SAndreas Gohr $event->stopPropagation(); 78*317bdfc2SAndreas Gohr 79*317bdfc2SAndreas Gohr global $INPUT; 80*317bdfc2SAndreas Gohr $ns = $INPUT->str('ns'); 81*317bdfc2SAndreas Gohr 82*317bdfc2SAndreas Gohr echo json_encode(auth_quickaclcheck($ns . ':*') >= AUTH_UPLOAD); 83*317bdfc2SAndreas Gohr } 84*317bdfc2SAndreas Gohr 85*317bdfc2SAndreas Gohr /** 86*317bdfc2SAndreas Gohr * Add CSP img-src directive to allow loading images from data source 87*317bdfc2SAndreas Gohr * 88*317bdfc2SAndreas Gohr * @param Doku_Event $event 89*317bdfc2SAndreas Gohr * @return void 90*317bdfc2SAndreas Gohr */ 91*317bdfc2SAndreas Gohr public function handleCSP(Doku_Event $event) 92*317bdfc2SAndreas Gohr { 93*317bdfc2SAndreas Gohr if ($this->isDiagram($event->data['media'])) { 94*317bdfc2SAndreas Gohr $event->data['csp']['img-src'] = "self data:"; 95*317bdfc2SAndreas Gohr $event->data['csp']['sandbox'] = "allow-popups allow-top-navigation allow-same-origin"; 96*317bdfc2SAndreas Gohr } 97*317bdfc2SAndreas Gohr } 98*317bdfc2SAndreas Gohr 99*317bdfc2SAndreas Gohr 100*317bdfc2SAndreas Gohr /** 101*317bdfc2SAndreas Gohr * Return an array of diagrams editable by the current user 102*317bdfc2SAndreas Gohr * 103*317bdfc2SAndreas Gohr * @param array $images 104*317bdfc2SAndreas Gohr * @return array 105*317bdfc2SAndreas Gohr */ 106*317bdfc2SAndreas Gohr protected function editableDiagrams($images) 107*317bdfc2SAndreas Gohr { 108*317bdfc2SAndreas Gohr $editable = []; 109*317bdfc2SAndreas Gohr 110*317bdfc2SAndreas Gohr foreach ($images as $image) { 111*317bdfc2SAndreas Gohr if (auth_quickaclcheck(cleanId($image)) >= AUTH_UPLOAD && $this->isDiagram($image)) { 112*317bdfc2SAndreas Gohr $editable[] = $image; 113*317bdfc2SAndreas Gohr } 114*317bdfc2SAndreas Gohr } 115*317bdfc2SAndreas Gohr 116*317bdfc2SAndreas Gohr return $editable; 117*317bdfc2SAndreas Gohr } 118*317bdfc2SAndreas Gohr 119*317bdfc2SAndreas Gohr /** 120*317bdfc2SAndreas Gohr * Return true if the image is recognized as our diagram 121*317bdfc2SAndreas Gohr * based on content ('embed.diagrams.net' or 'draw.io') 122*317bdfc2SAndreas Gohr * 123*317bdfc2SAndreas Gohr * @param string $image image id 124*317bdfc2SAndreas Gohr * @return bool 125*317bdfc2SAndreas Gohr */ 126*317bdfc2SAndreas Gohr protected function isDiagram($image) 127*317bdfc2SAndreas Gohr { 128*317bdfc2SAndreas Gohr global $conf; 129*317bdfc2SAndreas Gohr // strip nocache parameters from image 130*317bdfc2SAndreas Gohr $image = explode('&', $image); 131*317bdfc2SAndreas Gohr $image = $image[0]; 132*317bdfc2SAndreas Gohr 133*317bdfc2SAndreas Gohr // FIXME this should use mediaFN() 134*317bdfc2SAndreas Gohr $file = init_path( 135*317bdfc2SAndreas Gohr $conf['mediadir'] . 136*317bdfc2SAndreas Gohr DIRECTORY_SEPARATOR . 137*317bdfc2SAndreas Gohr preg_replace(['/:/'], [DIRECTORY_SEPARATOR], $image) 138*317bdfc2SAndreas Gohr ); 139*317bdfc2SAndreas Gohr 140*317bdfc2SAndreas Gohr // FIXME replace with helper_plugin_diagrams::isDiagram() 141*317bdfc2SAndreas Gohr if (!is_file($file)) return false; 142*317bdfc2SAndreas Gohr $begin = file_get_contents($file, false, null, 0, 500); 143*317bdfc2SAndreas Gohr $confServiceUrl = $this->getConf('service_url'); // like "https://diagrams.xyz.org/?embed=1&..." 144*317bdfc2SAndreas Gohr $serviceHost = parse_url($confServiceUrl, PHP_URL_HOST); // Host-Portion of the Url, e.g. "diagrams.xyz.org" 145*317bdfc2SAndreas Gohr return strpos($begin, 'embed.diagrams.net') || strpos($begin, 'draw.io') || strpos($begin, $serviceHost); 146*317bdfc2SAndreas Gohr } 147*317bdfc2SAndreas Gohr} 148