xref: /plugin/imgpaste/action.php (revision 8f17bdb989e71f21d70c26305e5f92ba19f029d1)
14df872e4SAndreas Gohr<?php
2832cd161SAndreas Gohr
3*8f17bdb9SAndreas Gohruse dokuwiki\Extension\ActionPlugin;
4*8f17bdb9SAndreas Gohruse dokuwiki\Extension\Event;
5*8f17bdb9SAndreas Gohruse dokuwiki\Extension\EventHandler;
6*8f17bdb9SAndreas Gohruse dokuwiki\plugin\imgpaste\Exception as PasteException;
7*8f17bdb9SAndreas Gohr
84df872e4SAndreas Gohr/**
94df872e4SAndreas Gohr * DokuWiki Plugin imgpaste (Action Component)
104df872e4SAndreas Gohr *
114df872e4SAndreas Gohr * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
124df872e4SAndreas Gohr * @author  Andreas Gohr <gohr@cosmocode.de>
134df872e4SAndreas Gohr */
14*8f17bdb9SAndreas Gohrclass action_plugin_imgpaste extends ActionPlugin
15832cd161SAndreas Gohr{
164df872e4SAndreas Gohr
17584f4635SAndreas Gohr    protected $tempdir = '';
18584f4635SAndreas Gohr    protected $tempfile = '';
19584f4635SAndreas Gohr
20584f4635SAndreas Gohr    /**
21584f4635SAndreas Gohr     * Clean up on destruction
22584f4635SAndreas Gohr     */
23584f4635SAndreas Gohr    public function __destruct()
24584f4635SAndreas Gohr    {
25584f4635SAndreas Gohr        $this->clean();
26584f4635SAndreas Gohr    }
274df872e4SAndreas Gohr
28832cd161SAndreas Gohr    /** @inheritdoc */
29*8f17bdb9SAndreas Gohr    public function register(EventHandler $controller)
30832cd161SAndreas Gohr    {
31832cd161SAndreas Gohr        $controller->register_hook('AJAX_CALL_UNKNOWN', 'BEFORE', $this, 'handleAjaxUpload');
324df872e4SAndreas Gohr    }
334df872e4SAndreas Gohr
34*8f17bdb9SAndreas Gohr
35832cd161SAndreas Gohr    /**
36832cd161SAndreas Gohr     * Creates a new file from the given data URL
37832cd161SAndreas Gohr     *
38*8f17bdb9SAndreas Gohr     * @param Event $event AJAX_CALL_UNKNOWN
39832cd161SAndreas Gohr     */
40*8f17bdb9SAndreas Gohr    public function handleAjaxUpload(Event $event)
41832cd161SAndreas Gohr    {
424df872e4SAndreas Gohr        if ($event->data != 'plugin_imgpaste') return;
43*8f17bdb9SAndreas Gohr        $event->preventDefault();
44*8f17bdb9SAndreas Gohr        $event->stopPropagation();
454df872e4SAndreas Gohr
464df872e4SAndreas Gohr        global $INPUT;
47*8f17bdb9SAndreas Gohr        try {
48*8f17bdb9SAndreas Gohr            if ($INPUT->has('url')) {
49*8f17bdb9SAndreas Gohr                [$data, $type] = $this->externalUrlToData($INPUT->post->str('url'));
50*8f17bdb9SAndreas Gohr            } else {
51*8f17bdb9SAndreas Gohr                [$data, $type] = $this->dataUrlToData($INPUT->post->str('data'));
52*8f17bdb9SAndreas Gohr            }
53*8f17bdb9SAndreas Gohr            $result = $this->storeImage($data, $type);
54*8f17bdb9SAndreas Gohr        } catch (PasteException $e) {
55*8f17bdb9SAndreas Gohr            $this->clean();
56*8f17bdb9SAndreas Gohr            http_status($e->getCode(), $e->getMessage());
57*8f17bdb9SAndreas Gohr            exit;
58*8f17bdb9SAndreas Gohr        }
59*8f17bdb9SAndreas Gohr
60*8f17bdb9SAndreas Gohr        header('Content-Type: application/json');
61*8f17bdb9SAndreas Gohr        echo json_encode($result);
62*8f17bdb9SAndreas Gohr    }
63*8f17bdb9SAndreas Gohr
64*8f17bdb9SAndreas Gohr    /**
65*8f17bdb9SAndreas Gohr     * Get the binary data and mime type from a data URL
66*8f17bdb9SAndreas Gohr     *
67*8f17bdb9SAndreas Gohr     * @param string $dataUrl
68*8f17bdb9SAndreas Gohr     * @return array [data, type]
69*8f17bdb9SAndreas Gohr     * @throws PasteException
70*8f17bdb9SAndreas Gohr     */
71*8f17bdb9SAndreas Gohr    protected function dataUrlToData($dataUrl)
72*8f17bdb9SAndreas Gohr    {
73*8f17bdb9SAndreas Gohr        list($type, $data) = explode(';', $dataUrl);
74*8f17bdb9SAndreas Gohr        if (!$data) throw new PasteException($this->getLang('e_nodata'), 400);
754df872e4SAndreas Gohr
764df872e4SAndreas Gohr        // process data encoding
774df872e4SAndreas Gohr        $type = strtolower(substr($type, 5)); // strip 'data:' prefix
784df872e4SAndreas Gohr        $data = substr($data, 7); // strip 'base64,' prefix
794df872e4SAndreas Gohr        $data = base64_decode($data);
80*8f17bdb9SAndreas Gohr        return [$data, $type];
81*8f17bdb9SAndreas Gohr    }
82*8f17bdb9SAndreas Gohr
83*8f17bdb9SAndreas Gohr    /**
84*8f17bdb9SAndreas Gohr     * Download the file from an external URL
85*8f17bdb9SAndreas Gohr     *
86*8f17bdb9SAndreas Gohr     * @param string $externalUrl
87*8f17bdb9SAndreas Gohr     * @return array [data, type]
88*8f17bdb9SAndreas Gohr     * @throws PasteException
89*8f17bdb9SAndreas Gohr     */
90*8f17bdb9SAndreas Gohr    protected function externalUrlToData($externalUrl)
91*8f17bdb9SAndreas Gohr    {
92*8f17bdb9SAndreas Gohr        global $lang;
93*8f17bdb9SAndreas Gohr
94*8f17bdb9SAndreas Gohr        // download the file
95*8f17bdb9SAndreas Gohr        $http = new \dokuwiki\HTTP\DokuHTTPClient();
96*8f17bdb9SAndreas Gohr        $data = $http->get($externalUrl);
97*8f17bdb9SAndreas Gohr        if (!$data) throw new PasteException($lang['uploadfail'], 500);
98*8f17bdb9SAndreas Gohr        [$type] = explode(';', $http->resp_headers['content-type']);
99*8f17bdb9SAndreas Gohr        return [$data, $type];
100*8f17bdb9SAndreas Gohr    }
101*8f17bdb9SAndreas Gohr
102*8f17bdb9SAndreas Gohr    /**
103*8f17bdb9SAndreas Gohr     * @throws PasteException
104*8f17bdb9SAndreas Gohr     */
105*8f17bdb9SAndreas Gohr    protected function storeImage($data, $type)
106*8f17bdb9SAndreas Gohr    {
107*8f17bdb9SAndreas Gohr        global $lang;
108*8f17bdb9SAndreas Gohr        global $INPUT;
1094df872e4SAndreas Gohr
1104df872e4SAndreas Gohr        // check for supported mime type
1114df872e4SAndreas Gohr        $mimetypes = array_flip(getMimeTypes());
112*8f17bdb9SAndreas Gohr        if (!isset($mimetypes[$type])) throw new PasteException($lang['uploadwrong'], 415);
1134df872e4SAndreas Gohr
1144df872e4SAndreas Gohr        // prepare file names
1154df872e4SAndreas Gohr        $tempname = $this->storetemp($data);
116584f4635SAndreas Gohr        $filename = $this->createFileName($INPUT->post->str('id'), $mimetypes[$type], $_SERVER['REMOTE_USER']);
1174df872e4SAndreas Gohr
1184df872e4SAndreas Gohr        // check ACLs
1194df872e4SAndreas Gohr        $auth = auth_quickaclcheck($filename);
120*8f17bdb9SAndreas Gohr        if ($auth < AUTH_UPLOAD) throw new PasteException($lang['uploadfail'], 403);
1214df872e4SAndreas Gohr
1224df872e4SAndreas Gohr        // do the actual saving
1234df872e4SAndreas Gohr        $result = media_save(
124584f4635SAndreas Gohr            [
1254df872e4SAndreas Gohr                'name' => $tempname,
1264df872e4SAndreas Gohr                'mime' => $type,
127832cd161SAndreas Gohr                'ext' => $mimetypes[$type],
128584f4635SAndreas Gohr            ],
1294df872e4SAndreas Gohr            $filename,
1304df872e4SAndreas Gohr            false,
1314df872e4SAndreas Gohr            $auth,
1324df872e4SAndreas Gohr            'copy'
1334df872e4SAndreas Gohr        );
134*8f17bdb9SAndreas Gohr        if (is_array($result)) throw  new PasteException($result[0], 500);
1354df872e4SAndreas Gohr
1364df872e4SAndreas Gohr        //Still here? We had a successful upload
1374df872e4SAndreas Gohr        $this->clean();
138*8f17bdb9SAndreas Gohr        return [
1394df872e4SAndreas Gohr            'message' => $lang['uploadsucc'],
140832cd161SAndreas Gohr            'id' => $result,
141832cd161SAndreas Gohr            'mime' => $type,
142832cd161SAndreas Gohr            'ext' => $mimetypes[$type],
143832cd161SAndreas Gohr            'url' => ml($result),
144*8f17bdb9SAndreas Gohr        ];
1454df872e4SAndreas Gohr    }
1464df872e4SAndreas Gohr
1474df872e4SAndreas Gohr    /**
148584f4635SAndreas Gohr     * Create the filename for the new file
149584f4635SAndreas Gohr     *
150584f4635SAndreas Gohr     * @param string $pageid the original page the paste event happend on
151584f4635SAndreas Gohr     * @param string $ext the extension of the file
152584f4635SAndreas Gohr     * @param string $user the currently logged in user
153584f4635SAndreas Gohr     * @return string
154584f4635SAndreas Gohr     */
155584f4635SAndreas Gohr    protected function createFileName($pageid, $ext, $user)
156584f4635SAndreas Gohr    {
157*8f17bdb9SAndreas Gohr        $unique = '';
158584f4635SAndreas Gohr        $filename = $this->getConf('filename');
159584f4635SAndreas Gohr        $filename = str_replace(
160584f4635SAndreas Gohr            [
161584f4635SAndreas Gohr                '@NS@',
162584f4635SAndreas Gohr                '@ID@',
163584f4635SAndreas Gohr                '@USER@',
164584f4635SAndreas Gohr                '@PAGE@',
165584f4635SAndreas Gohr            ],
166584f4635SAndreas Gohr            [
167584f4635SAndreas Gohr                getNS($pageid),
168584f4635SAndreas Gohr                $pageid,
169584f4635SAndreas Gohr                $user,
170584f4635SAndreas Gohr                noNS($pageid),
171584f4635SAndreas Gohr            ],
172584f4635SAndreas Gohr            $filename
173584f4635SAndreas Gohr        );
174584f4635SAndreas Gohr        $filename = strftime($filename);
175*8f17bdb9SAndreas Gohr        $filename = cleanID($filename);
176*8f17bdb9SAndreas Gohr        while (media_exists($filename . $unique . '.' . $ext)) {
177*8f17bdb9SAndreas Gohr            $unique = (int)$unique + 1;
178*8f17bdb9SAndreas Gohr        }
179*8f17bdb9SAndreas Gohr        return $filename . $unique . '.' . $ext;
180584f4635SAndreas Gohr    }
181584f4635SAndreas Gohr
182584f4635SAndreas Gohr    /**
1834df872e4SAndreas Gohr     * Create a temporary file from the given data
1844df872e4SAndreas Gohr     *
1854df872e4SAndreas Gohr     * exits if an error occurs
1864df872e4SAndreas Gohr     *
1874df872e4SAndreas Gohr     * @param $data
1884df872e4SAndreas Gohr     * @return string
1894df872e4SAndreas Gohr     */
190584f4635SAndreas Gohr    protected function storetemp($data)
191832cd161SAndreas Gohr    {
1924df872e4SAndreas Gohr        // store in temporary file
1934df872e4SAndreas Gohr        $this->tempdir = io_mktmpdir();
194*8f17bdb9SAndreas Gohr        if (!$this->tempdir) throw new PasteException('', 500);
1954df872e4SAndreas Gohr        $this->tempfile = $this->tempdir . '/' . md5($data);
196*8f17bdb9SAndreas Gohr        if (!io_saveFile($this->tempfile, $data)) throw new PasteException('', 500);
1974df872e4SAndreas Gohr        return $this->tempfile;
1984df872e4SAndreas Gohr    }
1994df872e4SAndreas Gohr
2004df872e4SAndreas Gohr    /**
2014df872e4SAndreas Gohr     * remove temporary file and directory
2024df872e4SAndreas Gohr     */
203584f4635SAndreas Gohr    protected function clean()
204832cd161SAndreas Gohr    {
2054df872e4SAndreas Gohr        if ($this->tempfile && file_exists($this->tempfile)) @unlink($this->tempfile);
2064df872e4SAndreas Gohr        if ($this->tempdir && is_dir($this->tempdir)) @rmdir($this->tempdir);
2074df872e4SAndreas Gohr        $this->tempfile = '';
2084df872e4SAndreas Gohr        $this->tempdir = '';
2094df872e4SAndreas Gohr    }
2104df872e4SAndreas Gohr
2114df872e4SAndreas Gohr}
212