xref: /plugin/diagrams/action/action.php (revision 317bdfc2bd4bf051cf5d349bd5d8d27dc2a0b6c5)
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