xref: /dokuwiki/inc/media.php (revision dd9e8e5ea54469964faab99223a61bd48146ac42)
13df72098SAndreas Gohr<?php
2d4f83172SAndreas Gohr
33df72098SAndreas Gohr/**
43df72098SAndreas Gohr * All output and handler function needed for the media management popup
53df72098SAndreas Gohr *
63df72098SAndreas Gohr * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
73df72098SAndreas Gohr * @author     Andreas Gohr <andi@splitbrain.org>
83df72098SAndreas Gohr */
93df72098SAndreas Gohr
1024870174SAndreas Gohruse dokuwiki\Ui\MediaRevisions;
1124870174SAndreas Gohruse dokuwiki\Cache\CacheImageMod;
1224870174SAndreas Gohruse splitbrain\slika\Exception;
1324870174SAndreas Gohruse dokuwiki\PassHash;
140c3a5702SAndreas Gohruse dokuwiki\ChangeLog\MediaChangeLog;
15b021f0b4SAndreas Gohruse dokuwiki\Extension\Event;
16*dd9e8e5eSAndreas Gohruse dokuwiki\File\MediaFile;
17b021f0b4SAndreas Gohruse dokuwiki\Form\Form;
185a8d6e48SMichael Großeuse dokuwiki\HTTP\DokuHTTPClient;
1979a2d784SGerrit Uitslaguse dokuwiki\Logger;
20704a815fSMichael Großeuse dokuwiki\Subscriptions\MediaSubscriptionSender;
21*dd9e8e5eSAndreas Gohruse dokuwiki\Ui\Media\Display;
2279a2d784SGerrit Uitslaguse dokuwiki\Ui\Media\DisplayRow;
2379a2d784SGerrit Uitslaguse dokuwiki\Ui\Media\DisplayTile;
2479a2d784SGerrit Uitslaguse dokuwiki\Ui\MediaDiff;
2579a2d784SGerrit Uitslaguse dokuwiki\Utf8\PhpString;
262d85e841SAndreas Gohruse dokuwiki\Utf8\Sort;
2779a2d784SGerrit Uitslaguse splitbrain\slika\Slika;
280c3a5702SAndreas Gohr
293df72098SAndreas Gohr/**
303df72098SAndreas Gohr * Lists pages which currently use a media file selected for deletion
313df72098SAndreas Gohr *
323df72098SAndreas Gohr * References uses the same visual as search results and share
333df72098SAndreas Gohr * their CSS tags except pagenames won't be links.
343df72098SAndreas Gohr *
353df72098SAndreas Gohr * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net>
3642ea7f44SGerrit Uitslag *
3742ea7f44SGerrit Uitslag * @param array $data
3842ea7f44SGerrit Uitslag * @param string $id
393df72098SAndreas Gohr */
40d868eb89SAndreas Gohrfunction media_filesinuse($data, $id)
41d868eb89SAndreas Gohr{
423df72098SAndreas Gohr    global $lang;
433df72098SAndreas Gohr    echo '<h1>' . $lang['reference'] . ' <code>' . hsc(noNS($id)) . '</code></h1>';
443df72098SAndreas Gohr    echo '<p>' . hsc($lang['ref_inuse']) . '</p>';
453df72098SAndreas Gohr
463df72098SAndreas Gohr    $hidden = 0; //count of hits without read permission
473df72098SAndreas Gohr    foreach ($data as $row) {
48a05e297aSAndreas Gohr        if (auth_quickaclcheck($row) >= AUTH_READ && isVisiblePage($row)) {
493df72098SAndreas Gohr            echo '<div class="search_result">';
50a05e297aSAndreas Gohr            echo '<span class="mediaref_ref">' . hsc($row) . '</span>';
513df72098SAndreas Gohr            echo '</div>';
52177d6836SAndreas Gohr        } else $hidden++;
533df72098SAndreas Gohr    }
543df72098SAndreas Gohr    if ($hidden) {
5526dfc232SAndreas Gohr        echo '<div class="mediaref_hidden">' . $lang['ref_hidden'] . '</div>';
563df72098SAndreas Gohr    }
573df72098SAndreas Gohr}
583df72098SAndreas Gohr
593df72098SAndreas Gohr/**
603df72098SAndreas Gohr * Handles the saving of image meta data
613df72098SAndreas Gohr *
623df72098SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org>
63cf832786SKate Arzamastseva * @author Kate Arzamastseva <pshns@ukr.net>
64e0c26282SGerrit Uitslag *
65e0c26282SGerrit Uitslag * @param string $id media id
66e0c26282SGerrit Uitslag * @param int $auth permission level
67e0c26282SGerrit Uitslag * @param array $data
6842ea7f44SGerrit Uitslag * @return false|string
693df72098SAndreas Gohr */
70d868eb89SAndreas Gohrfunction media_metasave($id, $auth, $data)
71d868eb89SAndreas Gohr{
723df72098SAndreas Gohr    if ($auth < AUTH_UPLOAD) return false;
73f2ea8432SAndreas Gohr    if (!checkSecurityToken()) return false;
743df72098SAndreas Gohr    global $lang;
750b308644SOtto Vainio    global $conf;
763df72098SAndreas Gohr    $src = mediaFN($id);
773df72098SAndreas Gohr
783df72098SAndreas Gohr    $meta = new JpegMeta($src);
793df72098SAndreas Gohr    $meta->_parseAll();
803df72098SAndreas Gohr
813df72098SAndreas Gohr    foreach ($data as $key => $val) {
823df72098SAndreas Gohr        $val = trim($val);
833df72098SAndreas Gohr        if (empty($val)) {
843df72098SAndreas Gohr            $meta->deleteField($key);
853df72098SAndreas Gohr        } else {
863df72098SAndreas Gohr            $meta->setField($key, $val);
873df72098SAndreas Gohr        }
883df72098SAndreas Gohr    }
893df72098SAndreas Gohr
90cf832786SKate Arzamastseva    $old = @filemtime($src);
9179e79377SAndreas Gohr    if (!file_exists(mediaFN($id, $old)) && file_exists($src)) {
92cf832786SKate Arzamastseva        // add old revision to the attic
93cf832786SKate Arzamastseva        media_saveOldRevision($id);
94cf832786SKate Arzamastseva    }
95ac3ed4afSGerrit Uitslag    $filesize_old = filesize($src);
963df72098SAndreas Gohr    if ($meta->save()) {
970b308644SOtto Vainio        if ($conf['fperm']) chmod($src, $conf['fperm']);
98ac3ed4afSGerrit Uitslag        @clearstatcache(true, $src);
99cf832786SKate Arzamastseva        $new = @filemtime($src);
100ac3ed4afSGerrit Uitslag        $filesize_new = filesize($src);
101ac3ed4afSGerrit Uitslag        $sizechange = $filesize_new - $filesize_old;
102ac3ed4afSGerrit Uitslag
103cf832786SKate Arzamastseva        // add a log entry to the media changelog
104ac3ed4afSGerrit Uitslag        addMediaLogEntry($new, $id, DOKU_CHANGE_TYPE_EDIT, $lang['media_meta_edited'], '', null, $sizechange);
105cf832786SKate Arzamastseva
1063df72098SAndreas Gohr        msg($lang['metasaveok'], 1);
1073df72098SAndreas Gohr        return $id;
1083df72098SAndreas Gohr    } else {
1093df72098SAndreas Gohr        msg($lang['metasaveerr'], -1);
1103df72098SAndreas Gohr        return false;
1113df72098SAndreas Gohr    }
1123df72098SAndreas Gohr}
1133df72098SAndreas Gohr
1143df72098SAndreas Gohr/**
115d54f7963SKlap-in * check if a media is external source
116d54f7963SKlap-in *
117d54f7963SKlap-in * @author Gerrit Uitslag <klapinklapin@gmail.com>
11842ea7f44SGerrit Uitslag *
119d54f7963SKlap-in * @param string $id the media ID or URL
120d54f7963SKlap-in * @return bool
121d54f7963SKlap-in */
122d868eb89SAndreas Gohrfunction media_isexternal($id)
123d868eb89SAndreas Gohr{
124fe578fe9SElan Ruusamäe    if (preg_match('#^(?:https?|ftp)://#i', $id)) return true;
125d54f7963SKlap-in    return false;
126d54f7963SKlap-in}
127d54f7963SKlap-in
128d54f7963SKlap-in/**
129add8678fSAndreas Gohr * Check if a media item is public (eg, external URL or readable by @ALL)
130add8678fSAndreas Gohr *
131add8678fSAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org>
13242ea7f44SGerrit Uitslag *
133add8678fSAndreas Gohr * @param string $id  the media ID or URL
134add8678fSAndreas Gohr * @return bool
135add8678fSAndreas Gohr */
136d868eb89SAndreas Gohrfunction media_ispublic($id)
137d868eb89SAndreas Gohr{
138d54f7963SKlap-in    if (media_isexternal($id)) return true;
139add8678fSAndreas Gohr    $id = cleanID($id);
14024870174SAndreas Gohr    if (auth_aclcheck(getNS($id) . ':*', '', []) >= AUTH_READ) return true;
141add8678fSAndreas Gohr    return false;
142add8678fSAndreas Gohr}
143add8678fSAndreas Gohr
144add8678fSAndreas Gohr/**
1453df72098SAndreas Gohr * Display the form to edit image meta data
1463df72098SAndreas Gohr *
1473df72098SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org>
148d9162c6cSKate Arzamastseva * @author Kate Arzamastseva <pshns@ukr.net>
149e0c26282SGerrit Uitslag *
150e0c26282SGerrit Uitslag * @param string $id media id
151e0c26282SGerrit Uitslag * @param int $auth permission level
152e0c26282SGerrit Uitslag * @return bool
1533df72098SAndreas Gohr */
154d868eb89SAndreas Gohrfunction media_metaform($id, $auth)
155d868eb89SAndreas Gohr{
156ebc28e69SAndreas Gohr    global $lang;
1573df72098SAndreas Gohr
15888a71175SKate Arzamastseva    if ($auth < AUTH_UPLOAD) {
159b960c74fSSatoshi Sahara        echo '<div class="nothing">' . $lang['media_perm_upload'] . '</div>' . DOKU_LF;
16088a71175SKate Arzamastseva        return false;
16188a71175SKate Arzamastseva    }
16288a71175SKate Arzamastseva
1633df72098SAndreas Gohr    // load the field descriptions
1643df72098SAndreas Gohr    static $fields = null;
165b960c74fSSatoshi Sahara    if ($fields === null) {
1663e98e685SKate Arzamastseva        $config_files = getConfigFiles('mediameta');
1673e98e685SKate Arzamastseva        foreach ($config_files as $config_file) {
16879e79377SAndreas Gohr            if (file_exists($config_file)) include($config_file);
1693df72098SAndreas Gohr        }
1703df72098SAndreas Gohr    }
1713df72098SAndreas Gohr
1723df72098SAndreas Gohr    $src = mediaFN($id);
1733df72098SAndreas Gohr
1743df72098SAndreas Gohr    // output
175b960c74fSSatoshi Sahara    $form = new Form([
176b960c74fSSatoshi Sahara            'action' => media_managerURL(['tab_details' => 'view'], '&'),
177b960c74fSSatoshi Sahara            'class' => 'meta'
178b960c74fSSatoshi Sahara    ]);
179b960c74fSSatoshi Sahara    $form->addTagOpen('div')->addClass('no');
180b960c74fSSatoshi Sahara    $form->setHiddenField('img', $id);
181b960c74fSSatoshi Sahara    $form->setHiddenField('mediado', 'save');
1823df72098SAndreas Gohr    foreach ($fields as $key => $field) {
1833df72098SAndreas Gohr        // get current value
1843e98e685SKate Arzamastseva        if (empty($field[0])) continue;
18524870174SAndreas Gohr        $tags = [$field[0]];
1865cefb623SEduardo Mozart de Oliveira        if (isset($field[3]) && is_array($field[3])) $tags = array_merge($tags, $field[3]);
1873df72098SAndreas Gohr        $value = tpl_img_getTag($tags, '', $src);
188ca6a0701SAndreas Gohr        $value = cleanText($value);
1893df72098SAndreas Gohr
1903df72098SAndreas Gohr        // prepare attributes
19124870174SAndreas Gohr        $p = [
192b960c74fSSatoshi Sahara            'class' => 'edit',
193b960c74fSSatoshi Sahara            'id'    => 'meta__' . $key,
19424870174SAndreas Gohr            'name'  => 'meta[' . $field[0] . ']'
19524870174SAndreas Gohr        ];
1963df72098SAndreas Gohr
197b960c74fSSatoshi Sahara        $form->addTagOpen('div')->addClass('row');
1983df72098SAndreas Gohr        if ($field[2] == 'text') {
199bde2a644SSatoshi Sahara            $form->addTextInput(
20064159a61SAndreas Gohr                $p['name'],
20179a2d784SGerrit Uitslag                ($lang[$field[1]] ?: $field[1] . ':')
202b960c74fSSatoshi Sahara            )->id($p['id'])->addClass($p['class'])->val($value);
2033df72098SAndreas Gohr        } else {
204b960c74fSSatoshi Sahara            $form->addTextarea($p['name'], $lang[$field[1]])->id($p['id'])
205b960c74fSSatoshi Sahara                ->val(formText($value))
206b960c74fSSatoshi Sahara                ->addClass($p['class'])
207b960c74fSSatoshi Sahara                ->attr('rows', '6')->attr('cols', '50');
2083df72098SAndreas Gohr        }
209b960c74fSSatoshi Sahara        $form->addTagClose('div');
2103df72098SAndreas Gohr    }
211b960c74fSSatoshi Sahara    $form->addTagOpen('div')->addClass('buttons');
212b960c74fSSatoshi Sahara    $form->addButton('mediado[save]', $lang['btn_save'])->attr('type', 'submit')
213b960c74fSSatoshi Sahara        ->attrs(['accesskey' => 's']);
214b960c74fSSatoshi Sahara    $form->addTagClose('div');
215ebc28e69SAndreas Gohr
216b960c74fSSatoshi Sahara    $form->addTagClose('div');
217b960c74fSSatoshi Sahara    echo $form->toHTML();
218ebc28e69SAndreas Gohr    return true;
2193df72098SAndreas Gohr}
2203df72098SAndreas Gohr
221666cdec5SMichael Klier/**
22287229c84SAdrian Lang * Convenience function to check if a media file is still in use
223666cdec5SMichael Klier *
224666cdec5SMichael Klier * @author Michael Klier <chi@chimeric.de>
225e0c26282SGerrit Uitslag *
226e0c26282SGerrit Uitslag * @param string $id media id
227e0c26282SGerrit Uitslag * @return array|bool
228666cdec5SMichael Klier */
229d868eb89SAndreas Gohrfunction media_inuse($id)
230d868eb89SAndreas Gohr{
231666cdec5SMichael Klier    global $conf;
232ebc28e69SAndreas Gohr
233666cdec5SMichael Klier    if ($conf['refcheck']) {
234ffec1009SMichael Hamann        $mediareferences = ft_mediause($id, true);
23524870174SAndreas Gohr        if ($mediareferences === []) {
2366dae2464SAndreas Gohr            return false;
237666cdec5SMichael Klier        } else {
238666cdec5SMichael Klier            return $mediareferences;
239666cdec5SMichael Klier        }
240666cdec5SMichael Klier    } else {
241666cdec5SMichael Klier        return false;
242666cdec5SMichael Klier    }
243666cdec5SMichael Klier}
244a05e297aSAndreas Gohr
2453df72098SAndreas Gohr/**
2463df72098SAndreas Gohr * Handles media file deletions
2473df72098SAndreas Gohr *
2483df72098SAndreas Gohr * If configured, checks for media references before deletion
2493df72098SAndreas Gohr *
2503df72098SAndreas Gohr * @author             Andreas Gohr <andi@splitbrain.org>
25142ea7f44SGerrit Uitslag *
252ebc28e69SAndreas Gohr * @param string $id media id
25322db8df7SAndreas Gohr * @param int $auth no longer used
25487229c84SAdrian Lang * @return int One of: 0,
25563703ba5SAndreas Gohr *                     DOKU_MEDIA_DELETED,
25663703ba5SAndreas Gohr *                     DOKU_MEDIA_DELETED | DOKU_MEDIA_EMPTY_NS,
25763703ba5SAndreas Gohr *                     DOKU_MEDIA_NOT_AUTH,
25863703ba5SAndreas Gohr *                     DOKU_MEDIA_INUSE
2593df72098SAndreas Gohr */
260d868eb89SAndreas Gohrfunction media_delete($id, $auth)
261d868eb89SAndreas Gohr{
2626183fb05SKate Arzamastseva    global $lang;
263ff04e8b7SAndreas Gohr    $auth = auth_quickaclcheck(ltrim(getNS($id) . ':*', ':'));
26487229c84SAdrian Lang    if ($auth < AUTH_DELETE) return DOKU_MEDIA_NOT_AUTH;
26587229c84SAdrian Lang    if (media_inuse($id)) return DOKU_MEDIA_INUSE;
2663df72098SAndreas Gohr
2673df72098SAndreas Gohr    $file = mediaFN($id);
2684a961e72SMichal Kolodziejski
2694a961e72SMichal Kolodziejski    // trigger an event - MEDIA_DELETE_FILE
27024870174SAndreas Gohr    $data = [];
271666cdec5SMichael Klier    $data['id']   = $id;
27279a2d784SGerrit Uitslag    $data['name'] = PhpString::basename($file);
2734a961e72SMichal Kolodziejski    $data['path'] = $file;
27479e79377SAndreas Gohr    $data['size'] = (file_exists($file)) ? filesize($file) : 0;
275666cdec5SMichael Klier
276666cdec5SMichael Klier    $data['unl'] = false;
277666cdec5SMichael Klier    $data['del'] = false;
278e1d9dcc8SAndreas Gohr    $evt = new Event('MEDIA_DELETE_FILE', $data);
2794a961e72SMichal Kolodziejski    if ($evt->advise_before()) {
2806183fb05SKate Arzamastseva        $old = @filemtime($file);
28179e79377SAndreas Gohr        if (!file_exists(mediaFN($id, $old)) && file_exists($file)) {
2826183fb05SKate Arzamastseva            // add old revision to the attic
2836183fb05SKate Arzamastseva            media_saveOldRevision($id);
2846183fb05SKate Arzamastseva        }
2856183fb05SKate Arzamastseva
286666cdec5SMichael Klier        $data['unl'] = @unlink($file);
287666cdec5SMichael Klier        if ($data['unl']) {
288ac3ed4afSGerrit Uitslag            $sizechange = 0 - $data['size'];
289ac3ed4afSGerrit Uitslag            addMediaLogEntry(time(), $id, DOKU_CHANGE_TYPE_DELETE, $lang['deleted'], '', null, $sizechange);
290ac3ed4afSGerrit Uitslag
291666cdec5SMichael Klier            $data['del'] = io_sweepNS($id, 'mediadir');
2923df72098SAndreas Gohr        }
2934a961e72SMichal Kolodziejski    }
2944a961e72SMichal Kolodziejski    $evt->advise_after();
2954a961e72SMichal Kolodziejski    unset($evt);
2964a961e72SMichal Kolodziejski
297666cdec5SMichael Klier    if ($data['unl'] && $data['del']) {
29887229c84SAdrian Lang        return DOKU_MEDIA_DELETED | DOKU_MEDIA_EMPTY_NS;
2993df72098SAndreas Gohr    }
3003df72098SAndreas Gohr
30187229c84SAdrian Lang    return $data['unl'] ? DOKU_MEDIA_DELETED : 0;
3023df72098SAndreas Gohr}
3033df72098SAndreas Gohr
3043df72098SAndreas Gohr/**
3052d6cc64fSKate Arzamastseva * Handle file uploads via XMLHttpRequest
3062d6cc64fSKate Arzamastseva *
307ebc28e69SAndreas Gohr * @param string $ns   target namespace
308ebc28e69SAndreas Gohr * @param int    $auth current auth check result
30942ea7f44SGerrit Uitslag * @return false|string false on error, id of the new file on success
3102d6cc64fSKate Arzamastseva */
311d868eb89SAndreas Gohrfunction media_upload_xhr($ns, $auth)
312d868eb89SAndreas Gohr{
313da45d883SKate Arzamastseva    if (!checkSecurityToken()) return false;
3148108113cSTom N Harris    global $INPUT;
315da45d883SKate Arzamastseva
3168108113cSTom N Harris    $id = $INPUT->get->str('qqfile');
31724870174SAndreas Gohr    [$ext, $mime] = mimetype($id);
3182d6cc64fSKate Arzamastseva    $input = fopen("php://input", "r");
3192d6cc64fSKate Arzamastseva    if (!($tmp = io_mktmpdir())) return false;
32062231793SKate Arzamastseva    $path = $tmp . '/' . md5($id);
3212d6cc64fSKate Arzamastseva    $target = fopen($path, "w");
322063fb5b5SAndreas Gohr    $realSize = stream_copy_to_stream($input, $target);
3232d6cc64fSKate Arzamastseva    fclose($target);
324063fb5b5SAndreas Gohr    fclose($input);
3252b9be456SAndreas Gohr    if ($INPUT->server->has('CONTENT_LENGTH') && ($realSize != $INPUT->server->int('CONTENT_LENGTH'))) {
326063fb5b5SAndreas Gohr        unlink($path);
327063fb5b5SAndreas Gohr        return false;
328063fb5b5SAndreas Gohr    }
329063fb5b5SAndreas Gohr
3302d6cc64fSKate Arzamastseva    $res = media_save(
33124870174SAndreas Gohr        ['name' => $path, 'mime' => $mime, 'ext'  => $ext],
3322d6cc64fSKate Arzamastseva        $ns . ':' . $id,
33379a2d784SGerrit Uitslag        ($INPUT->get->str('ow') == 'true'),
3342d6cc64fSKate Arzamastseva        $auth,
3352d6cc64fSKate Arzamastseva        'copy'
3362d6cc64fSKate Arzamastseva    );
3372d6cc64fSKate Arzamastseva    unlink($path);
338900a9e9eSGerrit Uitslag    if ($tmp) io_rmdir($tmp, true);
3392d6cc64fSKate Arzamastseva    if (is_array($res)) {
3402d6cc64fSKate Arzamastseva        msg($res[0], $res[1]);
3412d6cc64fSKate Arzamastseva        return false;
3422d6cc64fSKate Arzamastseva    }
3432d6cc64fSKate Arzamastseva    return $res;
3442d6cc64fSKate Arzamastseva}
3452d6cc64fSKate Arzamastseva
3462d6cc64fSKate Arzamastseva/**
3473df72098SAndreas Gohr * Handles media file uploads
3483df72098SAndreas Gohr *
3493df72098SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org>
35011d9dfa5SMichael Klier * @author Michael Klier <chi@chimeric.de>
35142ea7f44SGerrit Uitslag *
352ebc28e69SAndreas Gohr * @param string     $ns    target namespace
353ebc28e69SAndreas Gohr * @param int        $auth  current auth check result
354ebc28e69SAndreas Gohr * @param bool|array $file  $_FILES member, $_FILES['upload'] if false
35542ea7f44SGerrit Uitslag * @return false|string false on error, id of the new file on success
3563df72098SAndreas Gohr */
357d868eb89SAndreas Gohrfunction media_upload($ns, $auth, $file = false)
358d868eb89SAndreas Gohr{
359f2ea8432SAndreas Gohr    if (!checkSecurityToken()) return false;
3603df72098SAndreas Gohr    global $lang;
3618108113cSTom N Harris    global $INPUT;
3623df72098SAndreas Gohr
36344409c3dSAndreas Gohr    // get file and id
3648108113cSTom N Harris    $id   = $INPUT->post->str('mediaid');
3652d6cc64fSKate Arzamastseva    if (!$file) $file = $_FILES['upload'];
3663df72098SAndreas Gohr    if (empty($id)) $id = $file['name'];
36744409c3dSAndreas Gohr
36899766eefSAndreas Gohr    // check for errors (messages are done in lib/exe/mediamanager.php)
36999766eefSAndreas Gohr    if ($file['error']) return false;
3709676dc23SAndreas Gohr
37144409c3dSAndreas Gohr    // check extensions
37224870174SAndreas Gohr    [$fext, $fmime] = mimetype($file['name']);
37324870174SAndreas Gohr    [$iext, $imime] = mimetype($id);
37444409c3dSAndreas Gohr    if ($fext && !$iext) {
3758cb1eb01SAndreas Gohr        // no extension specified in id - read original one
37644409c3dSAndreas Gohr        $id   .= '.' . $fext;
3778cb1eb01SAndreas Gohr        $imime = $fmime;
37844409c3dSAndreas Gohr    } elseif ($fext && $fext != $iext) {
37944409c3dSAndreas Gohr        // extension was changed, print warning
38044409c3dSAndreas Gohr        msg(sprintf($lang['mediaextchange'], $fext, $iext));
38144409c3dSAndreas Gohr    }
38244409c3dSAndreas Gohr
38324870174SAndreas Gohr    $res = media_save(
38424870174SAndreas Gohr        [
38524870174SAndreas Gohr            'name' => $file['tmp_name'],
386ffb291f2SAdrian Lang            'mime' => $imime,
38724870174SAndreas Gohr            'ext' => $iext
38824870174SAndreas Gohr        ],
38924870174SAndreas Gohr        $ns . ':' . $id,
39024870174SAndreas Gohr        $INPUT->post->bool('ow'),
39124870174SAndreas Gohr        $auth,
39224870174SAndreas Gohr        'copy_uploaded_file'
39324870174SAndreas Gohr    );
394ffb291f2SAdrian Lang    if (is_array($res)) {
395ffb291f2SAdrian Lang        msg($res[0], $res[1]);
396ffb291f2SAdrian Lang        return false;
397ffb291f2SAdrian Lang    }
398ffb291f2SAdrian Lang    return $res;
399ffb291f2SAdrian Lang}
400ffb291f2SAdrian Lang
401ffb291f2SAdrian Lang/**
40271f17ac4SAndreas Gohr * An alternative to move_uploaded_file that copies
40371f17ac4SAndreas Gohr *
40471f17ac4SAndreas Gohr * Using copy, makes sure any setgid bits on the media directory are honored
40571f17ac4SAndreas Gohr *
40671f17ac4SAndreas Gohr * @see   move_uploaded_file()
40742ea7f44SGerrit Uitslag *
40871f17ac4SAndreas Gohr * @param string $from
40971f17ac4SAndreas Gohr * @param string $to
41071f17ac4SAndreas Gohr * @return bool
41171f17ac4SAndreas Gohr */
412d868eb89SAndreas Gohrfunction copy_uploaded_file($from, $to)
413d868eb89SAndreas Gohr{
41471f17ac4SAndreas Gohr    if (!is_uploaded_file($from)) return false;
41571f17ac4SAndreas Gohr    $ok = copy($from, $to);
41671f17ac4SAndreas Gohr    @unlink($from);
41771f17ac4SAndreas Gohr    return $ok;
41871f17ac4SAndreas Gohr}
41971f17ac4SAndreas Gohr
42071f17ac4SAndreas Gohr/**
421ffb291f2SAdrian Lang * This generates an action event and delegates to _media_upload_action().
422ffb291f2SAdrian Lang * Action plugins are allowed to pre/postprocess the uploaded file.
423ffb291f2SAdrian Lang * (The triggered event is preventable.)
424ffb291f2SAdrian Lang *
425ffb291f2SAdrian Lang * Event data:
426ffb291f2SAdrian Lang * $data[0]     fn_tmp:    the temporary file name (read from $_FILES)
427ffb291f2SAdrian Lang * $data[1]     fn:        the file name of the uploaded file
428ffb291f2SAdrian Lang * $data[2]     id:        the future directory id of the uploaded file
429ffb291f2SAdrian Lang * $data[3]     imime:     the mimetype of the uploaded file
430ffb291f2SAdrian Lang * $data[4]     overwrite: if an existing file is going to be overwritten
43152a281e8SGerrit Uitslag * $data[5]     move:      name of function that performs move/copy/..
432ffb291f2SAdrian Lang *
433ffb291f2SAdrian Lang * @triggers MEDIA_UPLOAD_FINISH
43442ea7f44SGerrit Uitslag *
435e0c26282SGerrit Uitslag * @param array  $file
43652a281e8SGerrit Uitslag * @param string $id   media id
43742ea7f44SGerrit Uitslag * @param bool   $ow   overwrite?
438e0c26282SGerrit Uitslag * @param int    $auth permission level
43952a281e8SGerrit Uitslag * @param string $move name of functions that performs move/copy/..
44042ea7f44SGerrit Uitslag * @return false|array|string
441e0c26282SGerrit Uitslag */
442d868eb89SAndreas Gohrfunction media_save($file, $id, $ow, $auth, $move)
443d868eb89SAndreas Gohr{
444ffb291f2SAdrian Lang    if ($auth < AUTH_UPLOAD) {
44524870174SAndreas Gohr        return ["You don't have permissions to upload files.", -1];
446ffb291f2SAdrian Lang    }
447ffb291f2SAdrian Lang
448ffb291f2SAdrian Lang    if (!isset($file['mime']) || !isset($file['ext'])) {
44924870174SAndreas Gohr        [$ext, $mime] = mimetype($id);
450ffb291f2SAdrian Lang        if (!isset($file['mime'])) {
451ffb291f2SAdrian Lang            $file['mime'] = $mime;
452ffb291f2SAdrian Lang        }
453ffb291f2SAdrian Lang        if (!isset($file['ext'])) {
454ffb291f2SAdrian Lang            $file['ext'] = $ext;
455ffb291f2SAdrian Lang        }
456ffb291f2SAdrian Lang    }
457ffb291f2SAdrian Lang
45892cac9a9SKate Arzamastseva    global $lang, $conf;
459ffb291f2SAdrian Lang
4603df72098SAndreas Gohr    // get filename
4613543c6deSAndreas Gohr    $id   = cleanID($id);
4623df72098SAndreas Gohr    $fn   = mediaFN($id);
4633df72098SAndreas Gohr
4643df72098SAndreas Gohr    // get filetype regexp
4653df72098SAndreas Gohr    $types = array_keys(getMimeTypes());
466bad6fc0dSAndreas Gohr    $types = array_map(
46724870174SAndreas Gohr        static fn($q) => preg_quote($q, "/"),
468bad6fc0dSAndreas Gohr        $types
469bad6fc0dSAndreas Gohr    );
47024870174SAndreas Gohr    $regex = implode('|', $types);
4713df72098SAndreas Gohr
4723df72098SAndreas Gohr    // because a temp file was created already
473ffb291f2SAdrian Lang    if (!preg_match('/\.(' . $regex . ')$/i', $fn)) {
47424870174SAndreas Gohr        return [$lang['uploadwrong'], -1];
475ffb291f2SAdrian Lang    }
476ffb291f2SAdrian Lang
4773df72098SAndreas Gohr    //check for overwrite
47879e79377SAndreas Gohr    $overwrite = file_exists($fn);
479e5d185e1SKate Arzamastseva    $auth_ow = (($conf['mediarevisions']) ? AUTH_UPLOAD : AUTH_DELETE);
480e5d185e1SKate Arzamastseva    if ($overwrite && (!$ow || $auth < $auth_ow)) {
48124870174SAndreas Gohr        return [$lang['uploadexist'], 0];
4823df72098SAndreas Gohr    }
4838cb1eb01SAndreas Gohr    // check for valid content
484ffb291f2SAdrian Lang    $ok = media_contentcheck($file['name'], $file['mime']);
4858cb1eb01SAndreas Gohr    if ($ok == -1) {
48624870174SAndreas Gohr        return [sprintf($lang['uploadbadcontent'], '.' . $file['ext']), -1];
4878cb1eb01SAndreas Gohr    } elseif ($ok == -2) {
48824870174SAndreas Gohr        return [$lang['uploadspam'], -1];
48926ceae18SAndreas Gohr    } elseif ($ok == -3) {
49024870174SAndreas Gohr        return [$lang['uploadxss'], -1];
4918cb1eb01SAndreas Gohr    }
4928cb1eb01SAndreas Gohr
49311d9dfa5SMichael Klier    // prepare event data
49424870174SAndreas Gohr    $data = [];
495ffb291f2SAdrian Lang    $data[0] = $file['name'];
49611d9dfa5SMichael Klier    $data[1] = $fn;
49711d9dfa5SMichael Klier    $data[2] = $id;
498ffb291f2SAdrian Lang    $data[3] = $file['mime'];
49999c8d7f2Smichael    $data[4] = $overwrite;
500ffb291f2SAdrian Lang    $data[5] = $move;
50111d9dfa5SMichael Klier
50211d9dfa5SMichael Klier    // trigger event
503cbb44eabSAndreas Gohr    return Event::createAndTrigger('MEDIA_UPLOAD_FINISH', $data, '_media_upload_action', true);
50411d9dfa5SMichael Klier}
50511d9dfa5SMichael Klier
50611d9dfa5SMichael Klier/**
50742ea7f44SGerrit Uitslag * Callback adapter for media_upload_finish() triggered by MEDIA_UPLOAD_FINISH
50842ea7f44SGerrit Uitslag *
50911d9dfa5SMichael Klier * @author Michael Klier <chi@chimeric.de>
51042ea7f44SGerrit Uitslag *
51142ea7f44SGerrit Uitslag * @param array $data event data
51242ea7f44SGerrit Uitslag * @return false|array|string
51311d9dfa5SMichael Klier */
514d868eb89SAndreas Gohrfunction _media_upload_action($data)
515d868eb89SAndreas Gohr{
51611d9dfa5SMichael Klier    // fixme do further sanity tests of given data?
517ffb291f2SAdrian Lang    if (is_array($data) && count($data) === 6) {
518ffb291f2SAdrian Lang        return media_upload_finish($data[0], $data[1], $data[2], $data[3], $data[4], $data[5]);
51911d9dfa5SMichael Klier    } else {
52011d9dfa5SMichael Klier        return false; //callback error
52111d9dfa5SMichael Klier    }
52211d9dfa5SMichael Klier}
52311d9dfa5SMichael Klier
52411d9dfa5SMichael Klier/**
52511d9dfa5SMichael Klier * Saves an uploaded media file
52611d9dfa5SMichael Klier *
52711d9dfa5SMichael Klier * @author Andreas Gohr <andi@splitbrain.org>
52811d9dfa5SMichael Klier * @author Michael Klier <chi@chimeric.de>
529cbe26ad6SKate Arzamastseva * @author Kate Arzamastseva <pshns@ukr.net>
53042ea7f44SGerrit Uitslag *
53142ea7f44SGerrit Uitslag * @param string $fn_tmp
53242ea7f44SGerrit Uitslag * @param string $fn
53342ea7f44SGerrit Uitslag * @param string $id        media id
53442ea7f44SGerrit Uitslag * @param string $imime     mime type
53542ea7f44SGerrit Uitslag * @param bool   $overwrite overwrite existing?
53642ea7f44SGerrit Uitslag * @param string $move      function name
53742ea7f44SGerrit Uitslag * @return array|string
53811d9dfa5SMichael Klier */
539d868eb89SAndreas Gohrfunction media_upload_finish($fn_tmp, $fn, $id, $imime, $overwrite, $move = 'move_uploaded_file')
540d868eb89SAndreas Gohr{
54111d9dfa5SMichael Klier    global $conf;
54211d9dfa5SMichael Klier    global $lang;
5439c1bd4bcSKate Arzamastseva    global $REV;
54411d9dfa5SMichael Klier
545e4f389efSKate Arzamastseva    $old = @filemtime($fn);
54679e79377SAndreas Gohr    if (!file_exists(mediaFN($id, $old)) && file_exists($fn)) {
547e4f389efSKate Arzamastseva        // add old revision to the attic if missing
548cbe26ad6SKate Arzamastseva        media_saveOldRevision($id);
549e4f389efSKate Arzamastseva    }
550e4f389efSKate Arzamastseva
5513df72098SAndreas Gohr    // prepare directory
552cc7d0c94SBen Coburn    io_createNamespace($id, 'media');
55311d9dfa5SMichael Klier
554ac3ed4afSGerrit Uitslag    $filesize_old = file_exists($fn) ? filesize($fn) : 0;
555ac3ed4afSGerrit Uitslag
556ffb291f2SAdrian Lang    if ($move($fn_tmp, $fn)) {
55723846a98SKate Arzamastseva        @clearstatcache(true, $fn);
558dad6764eSKate Arzamastseva        $new = @filemtime($fn);
55974400ea5SBen Coburn        // Set the correct permission here.
56074400ea5SBen Coburn        // Always chmod media because they may be saved with different permissions than expected from the php umask.
56174400ea5SBen Coburn        // (Should normally chmod to $conf['fperm'] only if $conf['fperm'] is set.)
56274400ea5SBen Coburn        chmod($fn, $conf['fmode']);
5633df72098SAndreas Gohr        msg($lang['uploadsucc'], 1);
56483734cddSPhy        media_notify($id, $fn, $imime, $old, $new);
56599c8d7f2Smichael        // add a log entry to the media changelog
566ac3ed4afSGerrit Uitslag        $filesize_new = filesize($fn);
567ac3ed4afSGerrit Uitslag        $sizechange = $filesize_new - $filesize_old;
5689c1bd4bcSKate Arzamastseva        if ($REV) {
56964159a61SAndreas Gohr            addMediaLogEntry(
57064159a61SAndreas Gohr                $new,
57164159a61SAndreas Gohr                $id,
57264159a61SAndreas Gohr                DOKU_CHANGE_TYPE_REVERT,
57364159a61SAndreas Gohr                sprintf($lang['restored'], dformat($REV)),
57464159a61SAndreas Gohr                $REV,
57564159a61SAndreas Gohr                null,
57664159a61SAndreas Gohr                $sizechange
57764159a61SAndreas Gohr            );
5789c1bd4bcSKate Arzamastseva        } elseif ($overwrite) {
579ac3ed4afSGerrit Uitslag            addMediaLogEntry($new, $id, DOKU_CHANGE_TYPE_EDIT, '', '', null, $sizechange);
58099c8d7f2Smichael        } else {
581ac3ed4afSGerrit Uitslag            addMediaLogEntry($new, $id, DOKU_CHANGE_TYPE_CREATE, $lang['created'], '', null, $sizechange);
58299c8d7f2Smichael        }
5833df72098SAndreas Gohr        return $id;
5843df72098SAndreas Gohr    } else {
58524870174SAndreas Gohr        return [$lang['uploadfail'], -1];
5863df72098SAndreas Gohr    }
5873df72098SAndreas Gohr}
5883df72098SAndreas Gohr
5898cb1eb01SAndreas Gohr/**
590cbe26ad6SKate Arzamastseva * Moves the current version of media file to the media_attic
591cbe26ad6SKate Arzamastseva * directory
592cbe26ad6SKate Arzamastseva *
593cbe26ad6SKate Arzamastseva * @author Kate Arzamastseva <pshns@ukr.net>
59442ea7f44SGerrit Uitslag *
595cbe26ad6SKate Arzamastseva * @param string $id
596cbe26ad6SKate Arzamastseva * @return int - revision date
597e4f389efSKate Arzamastseva */
598d868eb89SAndreas Gohrfunction media_saveOldRevision($id)
599d868eb89SAndreas Gohr{
600dbf57c96SKate Arzamastseva    global $conf, $lang;
601dbf57c96SKate Arzamastseva
602e4f389efSKate Arzamastseva    $oldf = mediaFN($id);
60379e79377SAndreas Gohr    if (!file_exists($oldf)) return '';
604e4f389efSKate Arzamastseva    $date = filemtime($oldf);
605e5d185e1SKate Arzamastseva    if (!$conf['mediarevisions']) return $date;
606e5d185e1SKate Arzamastseva
607047bad06SGerrit Uitslag    $medialog = new MediaChangeLog($id);
608047bad06SGerrit Uitslag    if (!$medialog->getRevisionInfo($date)) {
609dbf57c96SKate Arzamastseva        // there was an external edit,
610dbf57c96SKate Arzamastseva        // there is no log entry for current version of file
611ac3ed4afSGerrit Uitslag        $sizechange = filesize($oldf);
61279e79377SAndreas Gohr        if (!file_exists(mediaMetaFN($id, '.changes'))) {
613ac3ed4afSGerrit Uitslag            addMediaLogEntry($date, $id, DOKU_CHANGE_TYPE_CREATE, $lang['created'], '', null, $sizechange);
614dbf57c96SKate Arzamastseva        } else {
615ac3ed4afSGerrit Uitslag            $oldRev = $medialog->getRevisions(-1, 1); // from changelog
616ac3ed4afSGerrit Uitslag            $oldRev = (int) (empty($oldRev) ? 0 : $oldRev[0]);
617ac3ed4afSGerrit Uitslag            $filesize_old = filesize(mediaFN($id, $oldRev));
61824870174SAndreas Gohr            $sizechange -= $filesize_old;
619ac3ed4afSGerrit Uitslag
620ac3ed4afSGerrit Uitslag            addMediaLogEntry($date, $id, DOKU_CHANGE_TYPE_EDIT, '', '', null, $sizechange);
621dbf57c96SKate Arzamastseva        }
622dbf57c96SKate Arzamastseva    }
623dbf57c96SKate Arzamastseva
624e4f389efSKate Arzamastseva    $newf = mediaFN($id, $date);
625e4f389efSKate Arzamastseva    io_makeFileDir($newf);
626cbe26ad6SKate Arzamastseva    if (copy($oldf, $newf)) {
627e4f389efSKate Arzamastseva        // Set the correct permission here.
628e4f389efSKate Arzamastseva        // Always chmod media because they may be saved with different permissions than expected from the php umask.
629e4f389efSKate Arzamastseva        // (Should normally chmod to $conf['fperm'] only if $conf['fperm'] is set.)
630e4f389efSKate Arzamastseva        chmod($newf, $conf['fmode']);
631e4f389efSKate Arzamastseva    }
632e4f389efSKate Arzamastseva    return $date;
633e4f389efSKate Arzamastseva}
634e4f389efSKate Arzamastseva
635e4f389efSKate Arzamastseva/**
6368cb1eb01SAndreas Gohr * This function checks if the uploaded content is really what the
63726ceae18SAndreas Gohr * mimetype says it is. We also do spam checking for text types here.
6388cb1eb01SAndreas Gohr *
6398cb1eb01SAndreas Gohr * We need to do this stuff because we can not rely on the browser
6408cb1eb01SAndreas Gohr * to do this check correctly. Yes, IE is broken as usual.
6418cb1eb01SAndreas Gohr *
6428cb1eb01SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org>
64326ceae18SAndreas Gohr * @link   http://www.splitbrain.org/blog/2007-02/12-internet_explorer_facilitates_cross_site_scripting
6448cb1eb01SAndreas Gohr * @fixme  check all 26 magic IE filetypes here?
64542ea7f44SGerrit Uitslag *
64642ea7f44SGerrit Uitslag * @param string $file path to file
64742ea7f44SGerrit Uitslag * @param string $mime mimetype
64842ea7f44SGerrit Uitslag * @return int
6498cb1eb01SAndreas Gohr */
650d868eb89SAndreas Gohrfunction media_contentcheck($file, $mime)
651d868eb89SAndreas Gohr{
65226ceae18SAndreas Gohr    global $conf;
65326ceae18SAndreas Gohr    if ($conf['iexssprotect']) {
65426ceae18SAndreas Gohr        $fh = @fopen($file, 'rb');
65526ceae18SAndreas Gohr        if ($fh) {
65626ceae18SAndreas Gohr            $bytes = fread($fh, 256);
65726ceae18SAndreas Gohr            fclose($fh);
65826ceae18SAndreas Gohr            if (preg_match('/<(script|a|img|html|body|iframe)[\s>]/i', $bytes)) {
65942ea7f44SGerrit Uitslag                return -3; //XSS: possibly malicious content
66026ceae18SAndreas Gohr            }
66126ceae18SAndreas Gohr        }
66226ceae18SAndreas Gohr    }
6636c16a3a9Sfiwswe    if (str_starts_with($mime, 'image/')) {
6648cb1eb01SAndreas Gohr        $info = @getimagesize($file);
6658cb1eb01SAndreas Gohr        if ($mime == 'image/gif' && $info[2] != 1) {
66642ea7f44SGerrit Uitslag            return -1; // uploaded content did not match the file extension
6678cb1eb01SAndreas Gohr        } elseif ($mime == 'image/jpeg' && $info[2] != 2) {
6688cb1eb01SAndreas Gohr            return -1;
6698cb1eb01SAndreas Gohr        } elseif ($mime == 'image/png' && $info[2] != 3) {
6708cb1eb01SAndreas Gohr            return -1;
6718cb1eb01SAndreas Gohr        }
6728cb1eb01SAndreas Gohr        # fixme maybe check other images types as well
6736c16a3a9Sfiwswe    } elseif (str_starts_with($mime, 'text/')) {
6748cb1eb01SAndreas Gohr        global $TEXT;
6758cb1eb01SAndreas Gohr        $TEXT = io_readFile($file);
6768cb1eb01SAndreas Gohr        if (checkwordblock()) {
67742ea7f44SGerrit Uitslag            return -2; //blocked by the spam blacklist
6788cb1eb01SAndreas Gohr        }
6798cb1eb01SAndreas Gohr    }
6808cb1eb01SAndreas Gohr    return 0;
6818cb1eb01SAndreas Gohr}
6823df72098SAndreas Gohr
6833df72098SAndreas Gohr/**
68475030359SAndreas Gohr * Send a notify mail on uploads
68575030359SAndreas Gohr *
68675030359SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org>
68742ea7f44SGerrit Uitslag *
68842ea7f44SGerrit Uitslag * @param string   $id      media id
68942ea7f44SGerrit Uitslag * @param string   $file    path to file
69042ea7f44SGerrit Uitslag * @param string   $mime    mime type
69142ea7f44SGerrit Uitslag * @param bool|int $old_rev revision timestamp or false
69275030359SAndreas Gohr */
693d868eb89SAndreas Gohrfunction media_notify($id, $file, $mime, $old_rev = false, $current_rev = false)
694d868eb89SAndreas Gohr{
69575030359SAndreas Gohr    global $conf;
69679a2d784SGerrit Uitslag    if (empty($conf['notify'])) return; //notify enabled?
69775030359SAndreas Gohr
698704a815fSMichael Große    $subscription = new MediaSubscriptionSender();
69979a2d784SGerrit Uitslag    $subscription->sendMediaDiff($conf['notify'], 'uploadmail', $id, $old_rev, $current_rev);
70075030359SAndreas Gohr}
70175030359SAndreas Gohr
70275030359SAndreas Gohr/**
7033df72098SAndreas Gohr * List all files in a given Media namespace
70442ea7f44SGerrit Uitslag *
70521d806cdSGerrit Uitslag * @param string      $ns             namespace
70642ea7f44SGerrit Uitslag * @param null|int    $auth           permission level
70742ea7f44SGerrit Uitslag * @param string      $jump           id
70842ea7f44SGerrit Uitslag * @param bool        $fullscreenview
7098702de7fSGerrit Uitslag * @param bool|string $sort           sorting order, false skips sorting
7103df72098SAndreas Gohr */
711d868eb89SAndreas Gohrfunction media_filelist($ns, $auth = null, $jump = '', $fullscreenview = false, $sort = false)
712d868eb89SAndreas Gohr{
7133df72098SAndreas Gohr    global $conf;
7143df72098SAndreas Gohr    global $lang;
7153df72098SAndreas Gohr    $ns = cleanID($ns);
7163df72098SAndreas Gohr
7173df72098SAndreas Gohr    // check auth our self if not given (needed for ajax calls)
7183df72098SAndreas Gohr    if (is_null($auth)) $auth = auth_quickaclcheck("$ns:*");
7193df72098SAndreas Gohr
720d9162c6cSKate Arzamastseva    if (!$fullscreenview) echo '<h1 id="media__ns">:' . hsc($ns) . '</h1>' . NL;
7213df72098SAndreas Gohr
7223df72098SAndreas Gohr    if ($auth < AUTH_READ) {
7233df72098SAndreas Gohr        // FIXME: print permission warning here instead?
7243df72098SAndreas Gohr        echo '<div class="nothing">' . $lang['nothingfound'] . '</div>' . NL;
72556fe6664SAndreas Gohr    } else {
72689274c0dSChristopher Smith        if (!$fullscreenview) {
72789274c0dSChristopher Smith            media_uploadform($ns, $auth);
72889274c0dSChristopher Smith            media_searchform($ns);
72989274c0dSChristopher Smith        }
7303df72098SAndreas Gohr
7313df72098SAndreas Gohr        $dir = utf8_encodeFN(str_replace(':', '/', $ns));
73224870174SAndreas Gohr        $data = [];
733dccd6b2bSAndreas Gohr        search(
734dccd6b2bSAndreas Gohr            $data,
735dccd6b2bSAndreas Gohr            $conf['mediadir'],
736dccd6b2bSAndreas Gohr            'search_mediafiles',
737dccd6b2bSAndreas Gohr            ['showmsg' => true, 'depth' => 1],
738dccd6b2bSAndreas Gohr            $dir,
739dccd6b2bSAndreas Gohr            1,
740dccd6b2bSAndreas Gohr            $sort
741dccd6b2bSAndreas Gohr        );
7423df72098SAndreas Gohr
743093fe67eSAndreas Gohr        if ($data === []) {
7443df72098SAndreas Gohr            echo '<div class="nothing">' . $lang['nothingfound'] . '</div>' . NL;
7455b9353faSKate Arzamastseva        } else {
7465b9353faSKate Arzamastseva            if ($fullscreenview) {
747554a8c9fSAdrian Lang                echo '<ul class="' . _media_get_list_type() . '">';
7485b9353faSKate Arzamastseva            }
7495b9353faSKate Arzamastseva            foreach ($data as $item) {
7505b9353faSKate Arzamastseva                if (!$fullscreenview) {
7514f33babfSAndreas Gohr                    //FIXME old call: media_printfile($item,$auth,$jump);
75279a2d784SGerrit Uitslag                    $display = new DisplayRow($item);
7539453716dSAndreas Gohr                    $display->scrollIntoView($jump == $item->getID());
7544f33babfSAndreas Gohr                    $display->show();
7555b9353faSKate Arzamastseva                } else {
7564f33babfSAndreas Gohr                    //FIXME old call: media_printfile_thumbs($item,$auth,$jump);
7574f33babfSAndreas Gohr                    echo '<li>';
75879a2d784SGerrit Uitslag                    $display = new DisplayTile($item);
7599453716dSAndreas Gohr                    $display->scrollIntoView($jump == $item->getID());
7604f33babfSAndreas Gohr                    $display->show();
7614f33babfSAndreas Gohr                    echo '</li>';
7625b9353faSKate Arzamastseva                }
7635b9353faSKate Arzamastseva            }
76494add303SAnika Henke            if ($fullscreenview) echo '</ul>' . NL;
7653df72098SAndreas Gohr        }
76656fe6664SAndreas Gohr    }
767d9162c6cSKate Arzamastseva}
768d9162c6cSKate Arzamastseva
769d9162c6cSKate Arzamastseva/**
770d9162c6cSKate Arzamastseva * Prints tabs for files list actions
771d9162c6cSKate Arzamastseva *
772d9162c6cSKate Arzamastseva * @author Kate Arzamastseva <pshns@ukr.net>
77395b451bcSAdrian Lang * @author Adrian Lang <mail@adrianlang.de>
77495b451bcSAdrian Lang *
775035e07f1SKate Arzamastseva * @param string $selected_tab - opened tab
776d9162c6cSKate Arzamastseva */
77795b451bcSAdrian Lang
778d868eb89SAndreas Gohrfunction media_tabs_files($selected_tab = '')
779d868eb89SAndreas Gohr{
780d9162c6cSKate Arzamastseva    global $lang;
78124870174SAndreas Gohr    $tabs = [];
7827d34963bSAndreas Gohr    foreach (
7837d34963bSAndreas Gohr        [
78424870174SAndreas Gohr            'files' => 'mediaselect',
78595b451bcSAdrian Lang            'upload' => 'media_uploadtab',
78624870174SAndreas Gohr            'search' => 'media_searchtab'
7877d34963bSAndreas Gohr        ] as $tab => $caption
7887d34963bSAndreas Gohr    ) {
78924870174SAndreas Gohr        $tabs[$tab] = [
79024870174SAndreas Gohr            'href'    => media_managerURL(['tab_files' => $tab], '&'),
79124870174SAndreas Gohr            'caption' => $lang[$caption]
79224870174SAndreas Gohr        ];
79395b451bcSAdrian Lang    }
794d9162c6cSKate Arzamastseva
79595b451bcSAdrian Lang    html_tabs($tabs, $selected_tab);
796d9162c6cSKate Arzamastseva}
797d9162c6cSKate Arzamastseva
798d9162c6cSKate Arzamastseva/**
799d9162c6cSKate Arzamastseva * Prints tabs for files details actions
800d9162c6cSKate Arzamastseva *
801d9162c6cSKate Arzamastseva * @author Kate Arzamastseva <pshns@ukr.net>
802ebc28e69SAndreas Gohr * @param string $image filename of the current image
803ebc28e69SAndreas Gohr * @param string $selected_tab opened tab
804d9162c6cSKate Arzamastseva */
805d868eb89SAndreas Gohrfunction media_tabs_details($image, $selected_tab = '')
806d868eb89SAndreas Gohr{
807e5d185e1SKate Arzamastseva    global $lang, $conf;
808d9162c6cSKate Arzamastseva
80924870174SAndreas Gohr    $tabs = [];
81024870174SAndreas Gohr    $tabs['view'] = [
81124870174SAndreas Gohr        'href'    => media_managerURL(['tab_details' => 'view'], '&'),
81224870174SAndreas Gohr        'caption' => $lang['media_viewtab']
81324870174SAndreas Gohr    ];
814d9162c6cSKate Arzamastseva
81524870174SAndreas Gohr    [, $mime] = mimetype($image);
81679e79377SAndreas Gohr    if ($mime == 'image/jpeg' && file_exists(mediaFN($image))) {
81724870174SAndreas Gohr        $tabs['edit'] = [
81824870174SAndreas Gohr            'href'    => media_managerURL(['tab_details' => 'edit'], '&'),
81924870174SAndreas Gohr            'caption' => $lang['media_edittab']
82024870174SAndreas Gohr        ];
821dd9ba38eSKate Arzamastseva    }
822e5d185e1SKate Arzamastseva    if ($conf['mediarevisions']) {
82324870174SAndreas Gohr        $tabs['history'] = [
82424870174SAndreas Gohr            'href'    => media_managerURL(['tab_details' => 'history'], '&'),
82524870174SAndreas Gohr            'caption' => $lang['media_historytab']
82624870174SAndreas Gohr        ];
827e5d185e1SKate Arzamastseva    }
828d9162c6cSKate Arzamastseva
82995b451bcSAdrian Lang    html_tabs($tabs, $selected_tab);
830d9162c6cSKate Arzamastseva}
831d9162c6cSKate Arzamastseva
832d9162c6cSKate Arzamastseva/**
833d9162c6cSKate Arzamastseva * Prints options for the tab that displays a list of all files
834d9162c6cSKate Arzamastseva *
835d9162c6cSKate Arzamastseva * @author Kate Arzamastseva <pshns@ukr.net>
836d9162c6cSKate Arzamastseva */
837d868eb89SAndreas Gohrfunction media_tab_files_options()
838d868eb89SAndreas Gohr{
8398108113cSTom N Harris    global $lang;
8408108113cSTom N Harris    global $INPUT;
841b7e5821dSAndreas Gohr    global $ID;
842b960c74fSSatoshi Sahara
843b960c74fSSatoshi Sahara    $form = new Form([
844b960c74fSSatoshi Sahara            'method' => 'get',
845b960c74fSSatoshi Sahara            'action' => wl($ID),
846b960c74fSSatoshi Sahara            'class' => 'options'
847b960c74fSSatoshi Sahara    ]);
848b960c74fSSatoshi Sahara    $form->addTagOpen('div')->addClass('no');
849b960c74fSSatoshi Sahara    $form->setHiddenField('sectok', null);
850b960c74fSSatoshi Sahara    $media_manager_params = media_managerURL([], '', false, true);
85138b2bf35SAnika Henke    foreach ($media_manager_params as $pKey => $pVal) {
852b960c74fSSatoshi Sahara        $form->setHiddenField($pKey, $pVal);
85338b2bf35SAnika Henke    }
8548108113cSTom N Harris    if ($INPUT->has('q')) {
855b960c74fSSatoshi Sahara        $form->setHiddenField('q', $INPUT->str('q'));
856554a8c9fSAdrian Lang    }
857b960c74fSSatoshi Sahara    $form->addHTML('<ul>' . NL);
8587d34963bSAndreas Gohr    foreach (
8597d34963bSAndreas Gohr        [
86024870174SAndreas Gohr            'list' => ['listType', ['thumbs', 'rows']],
86124870174SAndreas Gohr            'sort' => ['sortBy', ['name', 'date']]
8627d34963bSAndreas Gohr        ] as $group => $content
8637d34963bSAndreas Gohr    ) {
864ec88e837SAndreas Gohr        $checked = "_media_get_{$group}_type";
865554a8c9fSAdrian Lang        $checked = $checked();
866d9162c6cSKate Arzamastseva
867b960c74fSSatoshi Sahara        $form->addHTML('<li class="' . $content[0] . '">');
86895b451bcSAdrian Lang        foreach ($content[1] as $option) {
86924870174SAndreas Gohr            $attrs = [];
870554a8c9fSAdrian Lang            if ($checked == $option) {
87195b451bcSAdrian Lang                $attrs['checked'] = 'checked';
87295b451bcSAdrian Lang            }
873b960c74fSSatoshi Sahara            $radio = $form->addRadioButton(
874b960c74fSSatoshi Sahara                $group . '_dwmedia',
875b960c74fSSatoshi Sahara                $lang['media_' . $group . '_' . $option]
876b960c74fSSatoshi Sahara            )->val($option)->id($content[0] . '__' . $option)->addClass($option);
877b960c74fSSatoshi Sahara            $radio->attrs($attrs);
87895b451bcSAdrian Lang        }
879b960c74fSSatoshi Sahara        $form->addHTML('</li>' . NL);
88095b451bcSAdrian Lang    }
881b960c74fSSatoshi Sahara    $form->addHTML('<li>');
882b960c74fSSatoshi Sahara    $form->addButton('', $lang['btn_apply'])->attr('type', 'submit');
883b960c74fSSatoshi Sahara    $form->addHTML('</li>' . NL);
884b960c74fSSatoshi Sahara    $form->addHTML('</ul>' . NL);
885b960c74fSSatoshi Sahara    $form->addTagClose('div');
88626dfc232SAndreas Gohr    echo $form->toHTML();
887d9162c6cSKate Arzamastseva}
888d9162c6cSKate Arzamastseva
889d9162c6cSKate Arzamastseva/**
89087deddfaSKate Arzamastseva * Returns type of sorting for the list of files in media manager
89187deddfaSKate Arzamastseva *
89287deddfaSKate Arzamastseva * @author Kate Arzamastseva <pshns@ukr.net>
89342ea7f44SGerrit Uitslag *
89487deddfaSKate Arzamastseva * @return string - sort type
89587deddfaSKate Arzamastseva */
896d868eb89SAndreas Gohrfunction _media_get_sort_type()
897d868eb89SAndreas Gohr{
89824870174SAndreas Gohr    return _media_get_display_param('sort', ['default' => 'name', 'date']);
89987deddfaSKate Arzamastseva}
900554a8c9fSAdrian Lang
9016ffaeda9SGerrit Uitslag/**
9026ffaeda9SGerrit Uitslag * Returns type of listing for the list of files in media manager
9036ffaeda9SGerrit Uitslag *
9046ffaeda9SGerrit Uitslag * @author Kate Arzamastseva <pshns@ukr.net>
90542ea7f44SGerrit Uitslag *
9066ffaeda9SGerrit Uitslag * @return string - list type
9076ffaeda9SGerrit Uitslag */
908d868eb89SAndreas Gohrfunction _media_get_list_type()
909d868eb89SAndreas Gohr{
91024870174SAndreas Gohr    return _media_get_display_param('list', ['default' => 'thumbs', 'rows']);
91187deddfaSKate Arzamastseva}
912554a8c9fSAdrian Lang
9136ffaeda9SGerrit Uitslag/**
9146ffaeda9SGerrit Uitslag * Get display parameters
9156ffaeda9SGerrit Uitslag *
9166ffaeda9SGerrit Uitslag * @param string $param   name of parameter
9176ffaeda9SGerrit Uitslag * @param array  $values  allowed values, where default value has index key 'default'
9186ffaeda9SGerrit Uitslag * @return string the parameter value
9196ffaeda9SGerrit Uitslag */
920d868eb89SAndreas Gohrfunction _media_get_display_param($param, $values)
921d868eb89SAndreas Gohr{
9228108113cSTom N Harris    global $INPUT;
9238108113cSTom N Harris    if (in_array($INPUT->str($param), $values)) {
924554a8c9fSAdrian Lang        // FIXME: Set cookie
9258108113cSTom N Harris        return $INPUT->str($param);
926554a8c9fSAdrian Lang    } else {
9273629bc8cSAdrian Lang        $val = get_doku_pref($param, $values['default']);
9283629bc8cSAdrian Lang        if (!in_array($val, $values)) {
9293629bc8cSAdrian Lang            $val = $values['default'];
9303629bc8cSAdrian Lang        }
9313629bc8cSAdrian Lang        return $val;
932554a8c9fSAdrian Lang    }
93387deddfaSKate Arzamastseva}
93487deddfaSKate Arzamastseva
93587deddfaSKate Arzamastseva/**
936d9162c6cSKate Arzamastseva * Prints tab that displays a list of all files
937d9162c6cSKate Arzamastseva *
938d9162c6cSKate Arzamastseva * @author Kate Arzamastseva <pshns@ukr.net>
93942ea7f44SGerrit Uitslag *
94042ea7f44SGerrit Uitslag * @param string    $ns
94142ea7f44SGerrit Uitslag * @param null|int  $auth permission level
94242ea7f44SGerrit Uitslag * @param string    $jump item id
943d9162c6cSKate Arzamastseva */
944d868eb89SAndreas Gohrfunction media_tab_files($ns, $auth = null, $jump = '')
945d868eb89SAndreas Gohr{
946d9162c6cSKate Arzamastseva    global $lang;
947d9162c6cSKate Arzamastseva    if (is_null($auth)) $auth = auth_quickaclcheck("$ns:*");
948d9162c6cSKate Arzamastseva
949d9162c6cSKate Arzamastseva    if ($auth < AUTH_READ) {
95088a71175SKate Arzamastseva        echo '<div class="nothing">' . $lang['media_perm_read'] . '</div>' . NL;
951d9162c6cSKate Arzamastseva    } else {
95295b451bcSAdrian Lang        media_filelist($ns, $auth, $jump, true, _media_get_sort_type());
953d9162c6cSKate Arzamastseva    }
954d9162c6cSKate Arzamastseva}
955d9162c6cSKate Arzamastseva
956d9162c6cSKate Arzamastseva/**
957d9162c6cSKate Arzamastseva * Prints tab that displays uploading form
958d9162c6cSKate Arzamastseva *
959d9162c6cSKate Arzamastseva * @author Kate Arzamastseva <pshns@ukr.net>
96042ea7f44SGerrit Uitslag *
96142ea7f44SGerrit Uitslag * @param string   $ns
96242ea7f44SGerrit Uitslag * @param null|int $auth permission level
96342ea7f44SGerrit Uitslag * @param string   $jump item id
964d9162c6cSKate Arzamastseva */
965d868eb89SAndreas Gohrfunction media_tab_upload($ns, $auth = null, $jump = '')
966d868eb89SAndreas Gohr{
967d9162c6cSKate Arzamastseva    global $lang;
968d9162c6cSKate Arzamastseva    if (is_null($auth)) $auth = auth_quickaclcheck("$ns:*");
969d9162c6cSKate Arzamastseva
97094add303SAnika Henke    echo '<div class="upload">' . NL;
97195b451bcSAdrian Lang    if ($auth >= AUTH_UPLOAD) {
97295b451bcSAdrian Lang        echo '<p>' . $lang['mediaupload'] . '</p>';
97395b451bcSAdrian Lang    }
974d9162c6cSKate Arzamastseva    media_uploadform($ns, $auth, true);
97594add303SAnika Henke    echo '</div>' . NL;
976d9162c6cSKate Arzamastseva}
977d9162c6cSKate Arzamastseva
978d9162c6cSKate Arzamastseva/**
979d9162c6cSKate Arzamastseva * Prints tab that displays search form
980d9162c6cSKate Arzamastseva *
981d9162c6cSKate Arzamastseva * @author Kate Arzamastseva <pshns@ukr.net>
98242ea7f44SGerrit Uitslag *
98342ea7f44SGerrit Uitslag * @param string $ns
98442ea7f44SGerrit Uitslag * @param null|int $auth permission level
985d9162c6cSKate Arzamastseva */
986d868eb89SAndreas Gohrfunction media_tab_search($ns, $auth = null)
987d868eb89SAndreas Gohr{
9888108113cSTom N Harris    global $INPUT;
989d9162c6cSKate Arzamastseva
9908108113cSTom N Harris    $do = $INPUT->str('mediado');
9918108113cSTom N Harris    $query = $INPUT->str('q');
99294add303SAnika Henke    echo '<div class="search">' . NL;
993d9162c6cSKate Arzamastseva
994d9162c6cSKate Arzamastseva    media_searchform($ns, $query, true);
9952dba8df4SAdrian Lang    if ($do == 'searchlist' || $query) {
99695b451bcSAdrian Lang        media_searchlist($query, $ns, $auth, true, _media_get_sort_type());
99795b451bcSAdrian Lang    }
99894add303SAnika Henke    echo '</div>' . NL;
999d9162c6cSKate Arzamastseva}
1000d9162c6cSKate Arzamastseva
1001d9162c6cSKate Arzamastseva/**
1002d9162c6cSKate Arzamastseva * Prints tab that displays mediafile details
1003d9162c6cSKate Arzamastseva *
1004d9162c6cSKate Arzamastseva * @author Kate Arzamastseva <pshns@ukr.net>
1005e0c26282SGerrit Uitslag *
1006e0c26282SGerrit Uitslag * @param string     $image media id
1007e0c26282SGerrit Uitslag * @param string     $ns
1008e0c26282SGerrit Uitslag * @param null|int   $auth  permission level
100942ea7f44SGerrit Uitslag * @param string|int $rev   revision timestamp or empty string
1010d9162c6cSKate Arzamastseva */
1011d868eb89SAndreas Gohrfunction media_tab_view($image, $ns, $auth = null, $rev = '')
1012d868eb89SAndreas Gohr{
1013ebc28e69SAndreas Gohr    global $lang;
1014d9162c6cSKate Arzamastseva    if (is_null($auth)) $auth = auth_quickaclcheck("$ns:*");
1015d9162c6cSKate Arzamastseva
1016e136d6ccSKate Arzamastseva    if ($image && $auth >= AUTH_READ) {
1017*dd9e8e5eSAndreas Gohr        $mf = new MediaFile($image, $rev);
1018*dd9e8e5eSAndreas Gohr        echo (new Display($mf))->getDetailHtml();
1019e136d6ccSKate Arzamastseva        media_preview_buttons($image, $auth, $rev);
1020*dd9e8e5eSAndreas Gohr        media_details($image, $auth, $rev, $mf->getMeta());
1021e136d6ccSKate Arzamastseva    } else {
102294add303SAnika Henke        echo '<div class="nothing">' . $lang['media_perm_read'] . '</div>' . NL;
1023e136d6ccSKate Arzamastseva    }
1024d9162c6cSKate Arzamastseva}
1025d9162c6cSKate Arzamastseva
1026d9162c6cSKate Arzamastseva/**
1027d9162c6cSKate Arzamastseva * Prints tab that displays form for editing mediafile metadata
1028d9162c6cSKate Arzamastseva *
1029d9162c6cSKate Arzamastseva * @author Kate Arzamastseva <pshns@ukr.net>
1030e0c26282SGerrit Uitslag *
1031e0c26282SGerrit Uitslag * @param string     $image media id
1032e0c26282SGerrit Uitslag * @param string     $ns
1033e0c26282SGerrit Uitslag * @param null|int   $auth permission level
1034d9162c6cSKate Arzamastseva */
1035d868eb89SAndreas Gohrfunction media_tab_edit($image, $ns, $auth = null)
1036d868eb89SAndreas Gohr{
1037d9162c6cSKate Arzamastseva    if (is_null($auth)) $auth = auth_quickaclcheck("$ns:*");
1038d9162c6cSKate Arzamastseva
10391eeeced2SKate Arzamastseva    if ($image) {
104024870174SAndreas Gohr        [, $mime] = mimetype($image);
104130fd72fbSKate Arzamastseva        if ($mime == 'image/jpeg') media_metaform($image, $auth);
10421eeeced2SKate Arzamastseva    }
1043d9162c6cSKate Arzamastseva}
1044d9162c6cSKate Arzamastseva
1045d9162c6cSKate Arzamastseva/**
1046d9162c6cSKate Arzamastseva * Prints tab that displays mediafile revisions
1047d9162c6cSKate Arzamastseva *
1048d9162c6cSKate Arzamastseva * @author Kate Arzamastseva <pshns@ukr.net>
1049e0c26282SGerrit Uitslag *
1050e0c26282SGerrit Uitslag * @param string     $image media id
1051e0c26282SGerrit Uitslag * @param string     $ns
1052e0c26282SGerrit Uitslag * @param null|int   $auth permission level
1053d9162c6cSKate Arzamastseva */
1054d868eb89SAndreas Gohrfunction media_tab_history($image, $ns, $auth = null)
1055d868eb89SAndreas Gohr{
1056d9162c6cSKate Arzamastseva    global $lang;
10578108113cSTom N Harris    global $INPUT;
10588108113cSTom N Harris
1059d9162c6cSKate Arzamastseva    if (is_null($auth)) $auth = auth_quickaclcheck("$ns:*");
10608108113cSTom N Harris    $do = $INPUT->str('mediado');
1061d9162c6cSKate Arzamastseva
10622e55802cSKate Arzamastseva    if ($auth >= AUTH_READ && $image) {
10632e55802cSKate Arzamastseva        if ($do == 'diff') {
106424870174SAndreas Gohr            (new MediaDiff($image))->show(); //media_diff($image, $ns, $auth);
10652e55802cSKate Arzamastseva        } else {
1066a46cc3dcSAndreas Gohr            $first = $INPUT->int('first', -1);
106724870174SAndreas Gohr            (new MediaRevisions($image))->show($first);
10682e55802cSKate Arzamastseva        }
106988a71175SKate Arzamastseva    } else {
107088a71175SKate Arzamastseva        echo '<div class="nothing">' . $lang['media_perm_read'] . '</div>' . NL;
10712e55802cSKate Arzamastseva    }
10723df72098SAndreas Gohr}
10733df72098SAndreas Gohr
10743df72098SAndreas Gohr/**
1075e136d6ccSKate Arzamastseva * Prints mediafile action buttons
1076e136d6ccSKate Arzamastseva *
1077e136d6ccSKate Arzamastseva * @author Kate Arzamastseva <pshns@ukr.net>
1078e0c26282SGerrit Uitslag *
1079e0c26282SGerrit Uitslag * @param string     $image media id
1080e0c26282SGerrit Uitslag * @param int        $auth  permission level
1081871895a7SSatoshi Sahara * @param int|string $rev   revision timestamp, or empty string
1082e136d6ccSKate Arzamastseva */
1083d868eb89SAndreas Gohrfunction media_preview_buttons($image, $auth, $rev = '')
1084d868eb89SAndreas Gohr{
1085e5d185e1SKate Arzamastseva    global $lang, $conf;
1086e136d6ccSKate Arzamastseva
1087871895a7SSatoshi Sahara    echo '<ul class="actions">';
10881eeeced2SKate Arzamastseva
108979e79377SAndreas Gohr    if ($auth >= AUTH_DELETE && !$rev && file_exists(mediaFN($image))) {
1090e136d6ccSKate Arzamastseva        // delete button
1091b960c74fSSatoshi Sahara        $form = new Form([
1092b960c74fSSatoshi Sahara            'id' => 'mediamanager__btn_delete',
1093b960c74fSSatoshi Sahara            'action' => media_managerURL(['delete' => $image], '&'),
1094b960c74fSSatoshi Sahara        ]);
1095b960c74fSSatoshi Sahara        $form->addTagOpen('div')->addClass('no');
1096b960c74fSSatoshi Sahara        $form->addButton('', $lang['btn_delete'])->attr('type', 'submit');
1097b960c74fSSatoshi Sahara        $form->addTagClose('div');
109895b451bcSAdrian Lang        echo '<li>';
1099b960c74fSSatoshi Sahara        echo $form->toHTML();
1100871895a7SSatoshi Sahara        echo '</li>';
1101e5d185e1SKate Arzamastseva    }
1102e5d185e1SKate Arzamastseva
1103e5d185e1SKate Arzamastseva    $auth_ow = (($conf['mediarevisions']) ? AUTH_UPLOAD : AUTH_DELETE);
1104e5d185e1SKate Arzamastseva    if ($auth >= $auth_ow && !$rev) {
1105e136d6ccSKate Arzamastseva        // upload new version button
1106b960c74fSSatoshi Sahara        $form = new Form([
1107b960c74fSSatoshi Sahara            'id' => 'mediamanager__btn_update',
1108b960c74fSSatoshi Sahara            'action' => media_managerURL(['image' => $image, 'mediado' => 'update'], '&'),
1109b960c74fSSatoshi Sahara        ]);
1110b960c74fSSatoshi Sahara        $form->addTagOpen('div')->addClass('no');
1111b960c74fSSatoshi Sahara        $form->addButton('', $lang['media_update'])->attr('type', 'submit');
1112b960c74fSSatoshi Sahara        $form->addTagClose('div');
111395b451bcSAdrian Lang        echo '<li>';
1114b960c74fSSatoshi Sahara        echo $form->toHTML();
1115871895a7SSatoshi Sahara        echo '</li>';
111670c3cc9aSKate Arzamastseva    }
1117e136d6ccSKate Arzamastseva
111879e79377SAndreas Gohr    if ($auth >= AUTH_UPLOAD && $rev && $conf['mediarevisions'] && file_exists(mediaFN($image, $rev))) {
1119e136d6ccSKate Arzamastseva        // restore button
1120b960c74fSSatoshi Sahara        $form = new Form([
1121b960c74fSSatoshi Sahara            'id' => 'mediamanager__btn_restore',
1122b960c74fSSatoshi Sahara            'action' => media_managerURL(['image' => $image], '&'),
1123b960c74fSSatoshi Sahara        ]);
1124b960c74fSSatoshi Sahara        $form->addTagOpen('div')->addClass('no');
1125b960c74fSSatoshi Sahara        $form->setHiddenField('mediado', 'restore');
1126b960c74fSSatoshi Sahara        $form->setHiddenField('rev', $rev);
1127b960c74fSSatoshi Sahara        $form->addButton('', $lang['media_restore'])->attr('type', 'submit');
1128b960c74fSSatoshi Sahara        $form->addTagClose('div');
112995b451bcSAdrian Lang        echo '<li>';
1130b960c74fSSatoshi Sahara        echo $form->toHTML();
1131871895a7SSatoshi Sahara        echo '</li>';
11329c1bd4bcSKate Arzamastseva    }
1133e136d6ccSKate Arzamastseva
1134871895a7SSatoshi Sahara    echo '</ul>';
11352e55802cSKate Arzamastseva}
11362e55802cSKate Arzamastseva
11372e55802cSKate Arzamastseva/**
1138fa8e5c77SKate Arzamastseva * Returns the requested EXIF/IPTC tag from the image meta
1139fa8e5c77SKate Arzamastseva *
1140fa8e5c77SKate Arzamastseva * @author Kate Arzamastseva <pshns@ukr.net>
1141e0c26282SGerrit Uitslag *
114242ea7f44SGerrit Uitslag * @param array    $tags array with tags, first existing is returned
1143fa8e5c77SKate Arzamastseva * @param JpegMeta $meta
114442ea7f44SGerrit Uitslag * @param string   $alt  alternative value
1145fa8e5c77SKate Arzamastseva * @return string
1146fa8e5c77SKate Arzamastseva */
1147d868eb89SAndreas Gohrfunction media_getTag($tags, $meta = false, $alt = '')
1148d868eb89SAndreas Gohr{
1149871895a7SSatoshi Sahara    if (!$meta) return $alt;
1150fa8e5c77SKate Arzamastseva    $info = $meta->getField($tags);
1151871895a7SSatoshi Sahara    if (!$info) return $alt;
1152fa8e5c77SKate Arzamastseva    return $info;
1153fa8e5c77SKate Arzamastseva}
1154fa8e5c77SKate Arzamastseva
1155fa8e5c77SKate Arzamastseva/**
1156e136d6ccSKate Arzamastseva * Returns mediafile tags
11572e55802cSKate Arzamastseva *
11582e55802cSKate Arzamastseva * @author Kate Arzamastseva <pshns@ukr.net>
1159e0c26282SGerrit Uitslag *
1160e136d6ccSKate Arzamastseva * @param JpegMeta $meta
116142ea7f44SGerrit Uitslag * @return array list of tags of the mediafile
11622e55802cSKate Arzamastseva */
1163d868eb89SAndreas Gohrfunction media_file_tags($meta)
1164d868eb89SAndreas Gohr{
1165532850edSKate Arzamastseva    // load the field descriptions
11663e98e685SKate Arzamastseva    static $fields = null;
11673e98e685SKate Arzamastseva    if (is_null($fields)) {
11683e98e685SKate Arzamastseva        $config_files = getConfigFiles('mediameta');
11693e98e685SKate Arzamastseva        foreach ($config_files as $config_file) {
117079e79377SAndreas Gohr            if (file_exists($config_file)) include($config_file);
1171532850edSKate Arzamastseva        }
1172532850edSKate Arzamastseva    }
11731eeeced2SKate Arzamastseva
117424870174SAndreas Gohr    $tags = [];
1175dd9ba38eSKate Arzamastseva
117624870174SAndreas Gohr    foreach ($fields as $tag) {
117724870174SAndreas Gohr        $t = [];
117824870174SAndreas Gohr        if (!empty($tag[0])) $t = [$tag[0]];
11790e80bb5eSChristopher Smith        if (isset($tag[3]) && is_array($tag[3])) $t = array_merge($t, $tag[3]);
1180e136d6ccSKate Arzamastseva        $value = media_getTag($t, $meta);
118124870174SAndreas Gohr        $tags[] = ['tag' => $tag, 'value' => $value];
1182e136d6ccSKate Arzamastseva    }
1183e136d6ccSKate Arzamastseva
1184e136d6ccSKate Arzamastseva    return $tags;
1185e136d6ccSKate Arzamastseva}
1186e136d6ccSKate Arzamastseva
1187e136d6ccSKate Arzamastseva/**
1188e136d6ccSKate Arzamastseva * Prints mediafile tags
1189e136d6ccSKate Arzamastseva *
1190e136d6ccSKate Arzamastseva * @author Kate Arzamastseva <pshns@ukr.net>
119142ea7f44SGerrit Uitslag *
1192e0c26282SGerrit Uitslag * @param string        $image image id
1193e0c26282SGerrit Uitslag * @param int           $auth  permission level
1194e0c26282SGerrit Uitslag * @param string|int    $rev   revision timestamp, or empty string
1195e0c26282SGerrit Uitslag * @param bool|JpegMeta $meta  image object, or create one if false
1196e0c26282SGerrit Uitslag */
1197d868eb89SAndreas Gohrfunction media_details($image, $auth, $rev = '', $meta = false)
1198d868eb89SAndreas Gohr{
1199e136d6ccSKate Arzamastseva    global $lang;
1200e136d6ccSKate Arzamastseva
1201e136d6ccSKate Arzamastseva    if (!$meta) $meta = new JpegMeta(mediaFN($image, $rev));
1202e136d6ccSKate Arzamastseva    $tags = media_file_tags($meta);
1203e136d6ccSKate Arzamastseva
120494add303SAnika Henke    echo '<dl>' . NL;
1205e136d6ccSKate Arzamastseva    foreach ($tags as $tag) {
1206e136d6ccSKate Arzamastseva        if ($tag['value']) {
1207e136d6ccSKate Arzamastseva            $value = cleanText($tag['value']);
1208fde860beSGerrit Uitslag            echo '<dt>' . $lang[$tag['tag'][1]] . '</dt><dd>';
1209e136d6ccSKate Arzamastseva            if ($tag['tag'][2] == 'date') echo dformat($value);
12103e98e685SKate Arzamastseva            else echo hsc($value);
121194add303SAnika Henke            echo '</dd>' . NL;
12121eeeced2SKate Arzamastseva        }
1213e136d6ccSKate Arzamastseva    }
121494add303SAnika Henke    echo '</dl>' . NL;
121522e68399Sflammy    echo '<dl>' . NL;
121622e68399Sflammy    echo '<dt>' . $lang['reference'] . ':</dt>';
121722e68399Sflammy    $media_usage = ft_mediause($image, true);
121824870174SAndreas Gohr    if ($media_usage !== []) {
121922e68399Sflammy        foreach ($media_usage as $path) {
122022e68399Sflammy            echo '<dd>' . html_wikilink($path) . '</dd>';
122122e68399Sflammy        }
122222e68399Sflammy    } else {
122322e68399Sflammy        echo '<dd>' . $lang['nothingfound'] . '</dd>';
122422e68399Sflammy    }
122522e68399Sflammy    echo '</dl>' . NL;
12261eeeced2SKate Arzamastseva}
12272e55802cSKate Arzamastseva
12282e55802cSKate Arzamastseva/**
12292e55802cSKate Arzamastseva * Shows difference between two revisions of file
12302e55802cSKate Arzamastseva *
12312e55802cSKate Arzamastseva * @author Kate Arzamastseva <pshns@ukr.net>
123242ea7f44SGerrit Uitslag *
1233e0c26282SGerrit Uitslag * @param string $image  image id
1234e0c26282SGerrit Uitslag * @param string $ns
1235e0c26282SGerrit Uitslag * @param int $auth permission level
1236e0c26282SGerrit Uitslag * @param bool $fromajax
123779a2d784SGerrit Uitslag *
1238297071beSSatoshi Sahara * @deprecated 2020-12-31
1239e0c26282SGerrit Uitslag */
1240d868eb89SAndreas Gohrfunction media_diff($image, $ns, $auth, $fromajax = false)
1241d868eb89SAndreas Gohr{
124279a2d784SGerrit Uitslag    dbg_deprecated('see ' . MediaDiff::class . '::show()');
124359e81a43SKate Arzamastseva}
124459e81a43SKate Arzamastseva
12456ffaeda9SGerrit Uitslag/**
12466ffaeda9SGerrit Uitslag * Callback for media file diff
12476ffaeda9SGerrit Uitslag *
124842ea7f44SGerrit Uitslag * @param array $data event data
124979a2d784SGerrit Uitslag *
1250297071beSSatoshi Sahara * @deprecated 2020-12-31
12516ffaeda9SGerrit Uitslag */
1252d868eb89SAndreas Gohrfunction _media_file_diff($data)
1253d868eb89SAndreas Gohr{
125479a2d784SGerrit Uitslag    dbg_deprecated('see ' . MediaDiff::class . '::show()');
125559e81a43SKate Arzamastseva}
125659e81a43SKate Arzamastseva
125759e81a43SKate Arzamastseva/**
125859e81a43SKate Arzamastseva * Shows difference between two revisions of image
125959e81a43SKate Arzamastseva *
126059e81a43SKate Arzamastseva * @author Kate Arzamastseva <pshns@ukr.net>
1261e0c26282SGerrit Uitslag *
1262e0c26282SGerrit Uitslag * @param string $image
1263e0c26282SGerrit Uitslag * @param string|int $l_rev revision timestamp, or empty string
1264e0c26282SGerrit Uitslag * @param string|int $r_rev revision timestamp, or empty string
1265e0c26282SGerrit Uitslag * @param string $ns
1266e0c26282SGerrit Uitslag * @param int $auth permission level
1267e0c26282SGerrit Uitslag * @param bool $fromajax
1268297071beSSatoshi Sahara * @deprecated 2020-12-31
126959e81a43SKate Arzamastseva */
1270d868eb89SAndreas Gohrfunction media_file_diff($image, $l_rev, $r_rev, $ns, $auth, $fromajax)
1271d868eb89SAndreas Gohr{
127279a2d784SGerrit Uitslag    dbg_deprecated('see ' . MediaDiff::class . '::showFileDiff()');
1273e136d6ccSKate Arzamastseva}
1274e136d6ccSKate Arzamastseva
1275e136d6ccSKate Arzamastseva/**
1276e136d6ccSKate Arzamastseva * Prints two images side by side
1277fa8e5c77SKate Arzamastseva * and slider
1278e136d6ccSKate Arzamastseva *
1279e136d6ccSKate Arzamastseva * @author Kate Arzamastseva <pshns@ukr.net>
128042ea7f44SGerrit Uitslag *
128142ea7f44SGerrit Uitslag * @param string $image   image id
1282e0c26282SGerrit Uitslag * @param int    $l_rev   revision timestamp, or empty string
1283e0c26282SGerrit Uitslag * @param int    $r_rev   revision timestamp, or empty string
128442ea7f44SGerrit Uitslag * @param array  $l_size  array with width and height
128542ea7f44SGerrit Uitslag * @param array  $r_size  array with width and height
128650fc55feSKate Arzamastseva * @param string $type
1287297071beSSatoshi Sahara * @deprecated 2020-12-31
1288e136d6ccSKate Arzamastseva */
1289d868eb89SAndreas Gohrfunction media_image_diff($image, $l_rev, $r_rev, $l_size, $r_size, $type)
1290d868eb89SAndreas Gohr{
129179a2d784SGerrit Uitslag    dbg_deprecated('see ' . MediaDiff::class . '::showImageDiff()');
12921eeeced2SKate Arzamastseva}
12931eeeced2SKate Arzamastseva
12941eeeced2SKate Arzamastseva/**
12959c1bd4bcSKate Arzamastseva * Restores an old revision of a media file
12969c1bd4bcSKate Arzamastseva *
129742ea7f44SGerrit Uitslag * @param string $image media id
129842ea7f44SGerrit Uitslag * @param int    $rev   revision timestamp or empty string
12999c1bd4bcSKate Arzamastseva * @param int    $auth
13009c1bd4bcSKate Arzamastseva * @return string - file's id
130142ea7f44SGerrit Uitslag *
13029c1bd4bcSKate Arzamastseva * @author Kate Arzamastseva <pshns@ukr.net>
13039c1bd4bcSKate Arzamastseva */
1304d868eb89SAndreas Gohrfunction media_restore($image, $rev, $auth)
1305d868eb89SAndreas Gohr{
1306e5d185e1SKate Arzamastseva    global $conf;
130780525638SKate Arzamastseva    if ($auth < AUTH_UPLOAD || !$conf['mediarevisions']) return false;
130892cac9a9SKate Arzamastseva    $removed = (!file_exists(mediaFN($image)) && file_exists(mediaMetaFN($image, '.changes')));
130992cac9a9SKate Arzamastseva    if (!$image || (!file_exists(mediaFN($image)) && !$removed)) return false;
13109c1bd4bcSKate Arzamastseva    if (!$rev || !file_exists(mediaFN($image, $rev))) return false;
131124870174SAndreas Gohr    [, $imime, ] = mimetype($image);
1312dccd6b2bSAndreas Gohr    $res = media_upload_finish(
1313dccd6b2bSAndreas Gohr        mediaFN($image, $rev),
13149c1bd4bcSKate Arzamastseva        mediaFN($image),
13159c1bd4bcSKate Arzamastseva        $image,
13169c1bd4bcSKate Arzamastseva        $imime,
13179c1bd4bcSKate Arzamastseva        true,
1318dccd6b2bSAndreas Gohr        'copy'
1319dccd6b2bSAndreas Gohr    );
13209c1bd4bcSKate Arzamastseva    if (is_array($res)) {
13219c1bd4bcSKate Arzamastseva        msg($res[0], $res[1]);
13229c1bd4bcSKate Arzamastseva        return false;
13239c1bd4bcSKate Arzamastseva    }
13249c1bd4bcSKate Arzamastseva    return $res;
13259c1bd4bcSKate Arzamastseva}
13269c1bd4bcSKate Arzamastseva
13279c1bd4bcSKate Arzamastseva/**
1328bf1f3ac4Ssarnowski * List all files found by the search request
1329c9f56829SAndreas Gohr *
1330c9f56829SAndreas Gohr * @author Tobias Sarnowski <sarnowski@cosmocode.de>
1331c9f56829SAndreas Gohr * @author Andreas Gohr <gohr@cosmocode.de>
1332d9162c6cSKate Arzamastseva * @author Kate Arzamastseva <pshns@ukr.net>
1333c9f56829SAndreas Gohr * @triggers MEDIA_SEARCH
1334e0c26282SGerrit Uitslag *
1335e0c26282SGerrit Uitslag * @param string $query
1336e0c26282SGerrit Uitslag * @param string $ns
1337e0c26282SGerrit Uitslag * @param null|int $auth
1338e0c26282SGerrit Uitslag * @param bool $fullscreen
1339e0c26282SGerrit Uitslag * @param string $sort
1340bf1f3ac4Ssarnowski */
1341d868eb89SAndreas Gohrfunction media_searchlist($query, $ns, $auth = null, $fullscreen = false, $sort = 'natural')
1342d868eb89SAndreas Gohr{
1343bf1f3ac4Ssarnowski    global $conf;
1344bf1f3ac4Ssarnowski    global $lang;
13458e69fd30SKate Arzamastseva
1346bf1f3ac4Ssarnowski    $ns = cleanID($ns);
134724870174SAndreas Gohr    $evdata = [
134856fe6664SAndreas Gohr        'ns'    => $ns,
134924870174SAndreas Gohr        'data'  => [],
135056fe6664SAndreas Gohr        'query' => $query
135124870174SAndreas Gohr    ];
13527ed31746SSzymon Olewniczak    if (!blank($query)) {
1353e1d9dcc8SAndreas Gohr        $evt = new Event('MEDIA_SEARCH', $evdata);
1354bf1f3ac4Ssarnowski        if ($evt->advise_before()) {
135556fe6664SAndreas Gohr            $dir = utf8_encodeFN(str_replace(':', '/', $evdata['ns']));
135676472096SSzymon Olewniczak            $quoted = preg_quote($evdata['query'], '/');
135776472096SSzymon Olewniczak            //apply globbing
135824870174SAndreas Gohr            $quoted = str_replace(['\*', '\?'], ['.*', '.'], $quoted, $count);
135923e31e76SSzymon Olewniczak
136023e31e76SSzymon Olewniczak            //if we use globbing file name must match entirely but may be preceded by arbitrary namespace
136123e31e76SSzymon Olewniczak            if ($count > 0) $quoted = '^([^:]*:)*' . $quoted . '$';
136223e31e76SSzymon Olewniczak
136376472096SSzymon Olewniczak            $pattern = '/' . $quoted . '/i';
1364dccd6b2bSAndreas Gohr            search(
1365dccd6b2bSAndreas Gohr                $evdata['data'],
136656fe6664SAndreas Gohr                $conf['mediadir'],
13674f33babfSAndreas Gohr                'search_mediafiles',
136824870174SAndreas Gohr                ['showmsg' => false, 'pattern' => $pattern],
136900e3e394SChristopher Smith                $dir,
137000e3e394SChristopher Smith                1,
1371dccd6b2bSAndreas Gohr                $sort
1372dccd6b2bSAndreas Gohr            );
1373bf1f3ac4Ssarnowski        }
1374bf1f3ac4Ssarnowski        $evt->advise_after();
1375bf1f3ac4Ssarnowski        unset($evt);
137656fe6664SAndreas Gohr    }
1377bf1f3ac4Ssarnowski
1378d9162c6cSKate Arzamastseva    if (!$fullscreen) {
137956fe6664SAndreas Gohr        echo '<h1 id="media__ns">' . sprintf($lang['searchmedia_in'], hsc($ns) . ':*') . '</h1>' . NL;
138056fe6664SAndreas Gohr        media_searchform($ns, $query);
1381d9162c6cSKate Arzamastseva    }
138256fe6664SAndreas Gohr
138356fe6664SAndreas Gohr    if (!count($evdata['data'])) {
1384bf1f3ac4Ssarnowski        echo '<div class="nothing">' . $lang['nothingfound'] . '</div>' . NL;
13855b9353faSKate Arzamastseva    } else {
13865b9353faSKate Arzamastseva        if ($fullscreen) {
1387554a8c9fSAdrian Lang            echo '<ul class="' . _media_get_list_type() . '">';
13885b9353faSKate Arzamastseva        }
13895b9353faSKate Arzamastseva        foreach ($evdata['data'] as $item) {
13904f33babfSAndreas Gohr            if (!$fullscreen) {
13914f33babfSAndreas Gohr                // FIXME old call: media_printfile($item,$item['perm'],'',true);
139279a2d784SGerrit Uitslag                $display = new DisplayRow($item);
139379b00823SAndreas Gohr                $display->relativeDisplay($ns);
13944f33babfSAndreas Gohr                $display->show();
13954f33babfSAndreas Gohr            } else {
13964f33babfSAndreas Gohr                // FIXME old call: media_printfile_thumbs($item,$item['perm'],false,true);
139779a2d784SGerrit Uitslag                $display = new DisplayTile($item);
139879b00823SAndreas Gohr                $display->relativeDisplay($ns);
13994f33babfSAndreas Gohr                echo '<li>';
14004f33babfSAndreas Gohr                $display->show();
14014f33babfSAndreas Gohr                echo '</li>';
14024f33babfSAndreas Gohr            }
1403bf1f3ac4Ssarnowski        }
140494add303SAnika Henke        if ($fullscreen) echo '</ul>' . NL;
14055b9353faSKate Arzamastseva    }
1406bf1f3ac4Ssarnowski}
1407bf1f3ac4Ssarnowski
1408bf1f3ac4Ssarnowski/**
1409c6571d58SAndreas Gohr * Display a media icon
1410c6571d58SAndreas Gohr *
141142ea7f44SGerrit Uitslag * @param string $filename media id
1412c6571d58SAndreas Gohr * @param string $size     the size subfolder, if not specified 16x16 is used
141342ea7f44SGerrit Uitslag * @return string html
1414c6571d58SAndreas Gohr */
1415d868eb89SAndreas Gohrfunction media_printicon($filename, $size = '')
1416d868eb89SAndreas Gohr{
141724870174SAndreas Gohr    [$ext] = mimetype(mediaFN($filename), false);
141823786fd7SKate Arzamastseva
141979e79377SAndreas Gohr    if (file_exists(DOKU_INC . 'lib/images/fileicons/' . $size . '/' . $ext . '.png')) {
1420c6571d58SAndreas Gohr        $icon = DOKU_BASE . 'lib/images/fileicons/' . $size . '/' . $ext . '.png';
142123786fd7SKate Arzamastseva    } else {
1422c6571d58SAndreas Gohr        $icon = DOKU_BASE . 'lib/images/fileicons/' . $size . '/file.png';
142323786fd7SKate Arzamastseva    }
142423786fd7SKate Arzamastseva
1425421ec38eSKate Arzamastseva    return '<img src="' . $icon . '" alt="' . $filename . '" class="icon" />';
142623786fd7SKate Arzamastseva}
142723786fd7SKate Arzamastseva
14283df72098SAndreas Gohr/**
142942ea7f44SGerrit Uitslag * Build link based on the current, adding/rewriting parameters
1430d9162c6cSKate Arzamastseva *
1431d9162c6cSKate Arzamastseva * @author Kate Arzamastseva <pshns@ukr.net>
143242ea7f44SGerrit Uitslag *
1433ebc28e69SAndreas Gohr * @param array|bool $params
143442ea7f44SGerrit Uitslag * @param string     $amp           separator
143542ea7f44SGerrit Uitslag * @param bool       $abs           absolute url?
143642ea7f44SGerrit Uitslag * @param bool       $params_array  return the parmeters array?
143742ea7f44SGerrit Uitslag * @return string|array - link or link parameters
1438d9162c6cSKate Arzamastseva */
1439d868eb89SAndreas Gohrfunction media_managerURL($params = false, $amp = '&amp;', $abs = false, $params_array = false)
1440d868eb89SAndreas Gohr{
1441d9162c6cSKate Arzamastseva    global $ID;
14428108113cSTom N Harris    global $INPUT;
1443d9162c6cSKate Arzamastseva
144424870174SAndreas Gohr    $gets = ['do' => 'media'];
144524870174SAndreas Gohr    $media_manager_params = ['tab_files', 'tab_details', 'image', 'ns', 'list', 'sort'];
144623846a98SKate Arzamastseva    foreach ($media_manager_params as $x) {
14478108113cSTom N Harris        if ($INPUT->has($x)) $gets[$x] = $INPUT->str($x);
144823846a98SKate Arzamastseva    }
1449d9162c6cSKate Arzamastseva
1450d9162c6cSKate Arzamastseva    if ($params) {
1451554a8c9fSAdrian Lang        $gets = $params + $gets;
1452d9162c6cSKate Arzamastseva    }
1453d9162c6cSKate Arzamastseva    unset($gets['id']);
1454554a8c9fSAdrian Lang    if (isset($gets['delete'])) {
14551eeeced2SKate Arzamastseva        unset($gets['image']);
14561eeeced2SKate Arzamastseva        unset($gets['tab_details']);
14571eeeced2SKate Arzamastseva    }
1458d9162c6cSKate Arzamastseva
1459035e07f1SKate Arzamastseva    if ($params_array) return $gets;
1460035e07f1SKate Arzamastseva
14616dd095f5SKate Arzamastseva    return wl($ID, $gets, $abs, $amp);
1462d9162c6cSKate Arzamastseva}
1463d9162c6cSKate Arzamastseva
1464d9162c6cSKate Arzamastseva/**
14653df72098SAndreas Gohr * Print the media upload form if permissions are correct
14663df72098SAndreas Gohr *
14673df72098SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org>
1468d9162c6cSKate Arzamastseva * @author Kate Arzamastseva <pshns@ukr.net>
1469e0c26282SGerrit Uitslag *
1470e0c26282SGerrit Uitslag * @param string $ns
1471e0c26282SGerrit Uitslag * @param int    $auth permission level
1472e0c26282SGerrit Uitslag * @param bool  $fullscreen
14733df72098SAndreas Gohr */
1474d868eb89SAndreas Gohrfunction media_uploadform($ns, $auth, $fullscreen = false)
1475d868eb89SAndreas Gohr{
14768108113cSTom N Harris    global $lang;
14778108113cSTom N Harris    global $conf;
14788108113cSTom N Harris    global $INPUT;
14793df72098SAndreas Gohr
148088a71175SKate Arzamastseva    if ($auth < AUTH_UPLOAD) {
148188a71175SKate Arzamastseva        echo '<div class="nothing">' . $lang['media_perm_upload'] . '</div>' . NL;
148288a71175SKate Arzamastseva        return;
148388a71175SKate Arzamastseva    }
148492cac9a9SKate Arzamastseva    $auth_ow = (($conf['mediarevisions']) ? AUTH_UPLOAD : AUTH_DELETE);
14853df72098SAndreas Gohr
148670c3cc9aSKate Arzamastseva    $update = false;
148770c3cc9aSKate Arzamastseva    $id = '';
14888108113cSTom N Harris    if ($auth >= $auth_ow && $fullscreen && $INPUT->str('mediado') == 'update') {
148970c3cc9aSKate Arzamastseva        $update = true;
14908108113cSTom N Harris        $id = cleanID($INPUT->str('image'));
149170c3cc9aSKate Arzamastseva    }
149270c3cc9aSKate Arzamastseva
1493d00ec455SAndreas Gohr    // The default HTML upload form
1494b960c74fSSatoshi Sahara    $form = new Form([
1495b960c74fSSatoshi Sahara        'id' => 'dw__upload',
1496b960c74fSSatoshi Sahara        'enctype' => 'multipart/form-data',
1497b960c74fSSatoshi Sahara        'action' => ($fullscreen)
1498b960c74fSSatoshi Sahara                    ? media_managerURL(['tab_files' => 'files', 'tab_details' => 'view'], '&')
1499b960c74fSSatoshi Sahara                    : DOKU_BASE . 'lib/exe/mediamanager.php',
1500b960c74fSSatoshi Sahara    ]);
1501b960c74fSSatoshi Sahara    $form->addTagOpen('div')->addClass('no');
1502b960c74fSSatoshi Sahara    $form->setHiddenField('ns', hsc($ns));  // FIXME hsc required?
1503b960c74fSSatoshi Sahara    $form->addTagOpen('p');
1504b960c74fSSatoshi Sahara    $form->addTextInput('upload', $lang['txt_upload'])->id('upload__file')
1505b960c74fSSatoshi Sahara            ->attrs(['type' => 'file']);
1506b960c74fSSatoshi Sahara    $form->addTagClose('p');
1507b960c74fSSatoshi Sahara    $form->addTagOpen('p');
1508b960c74fSSatoshi Sahara    $form->addTextInput('mediaid', $lang['txt_filename'])->id('upload__name')
1509b960c74fSSatoshi Sahara            ->val(noNS($id));
1510b960c74fSSatoshi Sahara    $form->addButton('', $lang['btn_upload'])->attr('type', 'submit');
1511b960c74fSSatoshi Sahara    $form->addTagClose('p');
151292cac9a9SKate Arzamastseva    if ($auth >= $auth_ow) {
1513b960c74fSSatoshi Sahara        $form->addTagOpen('p');
151424870174SAndreas Gohr        $attrs = [];
151570c3cc9aSKate Arzamastseva        if ($update) $attrs['checked'] = 'checked';
1516b960c74fSSatoshi Sahara        $form->addCheckbox('ow', $lang['txt_overwrt'])->id('dw__ow')->val('1')
1517b960c74fSSatoshi Sahara            ->addClass('check')->attrs($attrs);
1518b960c74fSSatoshi Sahara        $form->addTagClose('p');
1519b960c74fSSatoshi Sahara    }
1520b960c74fSSatoshi Sahara    $form->addTagClose('div');
1521b960c74fSSatoshi Sahara
1522b960c74fSSatoshi Sahara    if (!$fullscreen) {
1523b960c74fSSatoshi Sahara        echo '<div class="upload">' . $lang['mediaupload'] . '</div>' . DOKU_LF;
1524b960c74fSSatoshi Sahara    } else {
1525b960c74fSSatoshi Sahara        echo DOKU_LF;
15263df72098SAndreas Gohr    }
1527c472bad9SKate Arzamastseva
1528b960c74fSSatoshi Sahara    echo '<div id="mediamanager__uploader">' . DOKU_LF;
1529c6977b3aSSatoshi Sahara    echo $form->toHTML('Upload');
1530b960c74fSSatoshi Sahara    echo '</div>' . DOKU_LF;
1531f940e4a0SAndreas Gohr
1532f940e4a0SAndreas Gohr    echo '<p class="maxsize">';
1533f940e4a0SAndreas Gohr    printf($lang['maxuploadsize'], filesize_h(media_getuploadsize()));
153456fc6b15SAnna Dabrowska    echo ' <a class="allowedmime" href="#">' . $lang['allowedmime'] . '</a>';
1535499d9bcdSAndreas Gohr    echo ' <span>' . implode(', ', array_keys(getMimeTypes())) . '</span>';
1536b960c74fSSatoshi Sahara    echo '</p>' . DOKU_LF;
1537f940e4a0SAndreas Gohr}
1538f940e4a0SAndreas Gohr
1539f940e4a0SAndreas Gohr/**
1540f940e4a0SAndreas Gohr * Returns the size uploaded files may have
1541f940e4a0SAndreas Gohr *
1542f940e4a0SAndreas Gohr * This uses a conservative approach using the lowest number found
1543f940e4a0SAndreas Gohr * in any of the limiting ini settings
1544f940e4a0SAndreas Gohr *
1545f940e4a0SAndreas Gohr * @returns int size in bytes
1546f940e4a0SAndreas Gohr */
1547d868eb89SAndreas Gohrfunction media_getuploadsize()
1548d868eb89SAndreas Gohr{
1549f940e4a0SAndreas Gohr    $okay = 0;
1550f940e4a0SAndreas Gohr
155124870174SAndreas Gohr    $post = php_to_byte(@ini_get('post_max_size'));
155224870174SAndreas Gohr    $suho = php_to_byte(@ini_get('suhosin.post.max_value_length'));
155324870174SAndreas Gohr    $upld = php_to_byte(@ini_get('upload_max_filesize'));
1554f940e4a0SAndreas Gohr
155524870174SAndreas Gohr    if ($post && ($post < $okay || $okay === 0)) $okay = $post;
1556f940e4a0SAndreas Gohr    if ($suho && ($suho < $okay || $okay == 0)) $okay = $suho;
1557f940e4a0SAndreas Gohr    if ($upld && ($upld < $okay || $okay == 0)) $okay = $upld;
1558f940e4a0SAndreas Gohr
1559f940e4a0SAndreas Gohr    return $okay;
15609f5dde7fSMichael Klier}
15613df72098SAndreas Gohr
15623df72098SAndreas Gohr/**
1563bf1f3ac4Ssarnowski * Print the search field form
1564bf1f3ac4Ssarnowski *
1565bf1f3ac4Ssarnowski * @author Tobias Sarnowski <sarnowski@cosmocode.de>
1566d9162c6cSKate Arzamastseva * @author Kate Arzamastseva <pshns@ukr.net>
1567e0c26282SGerrit Uitslag *
1568e0c26282SGerrit Uitslag * @param string $ns
1569e0c26282SGerrit Uitslag * @param string $query
1570e0c26282SGerrit Uitslag * @param bool $fullscreen
1571bf1f3ac4Ssarnowski */
1572d868eb89SAndreas Gohrfunction media_searchform($ns, $query = '', $fullscreen = false)
1573d868eb89SAndreas Gohr{
1574bf1f3ac4Ssarnowski    global $lang;
1575bf1f3ac4Ssarnowski
1576bf1f3ac4Ssarnowski    // The default HTML search form
1577b960c74fSSatoshi Sahara    $form = new Form([
1578b960c74fSSatoshi Sahara        'id'     => 'dw__mediasearch',
1579b960c74fSSatoshi Sahara        'action' => ($fullscreen)
1580b960c74fSSatoshi Sahara                    ? media_managerURL([], '&')
1581b960c74fSSatoshi Sahara                    : DOKU_BASE . 'lib/exe/mediamanager.php',
1582b960c74fSSatoshi Sahara    ]);
1583b960c74fSSatoshi Sahara    $form->addTagOpen('div')->addClass('no');
1584b960c74fSSatoshi Sahara    $form->setHiddenField('ns', $ns);
1585b960c74fSSatoshi Sahara    $form->setHiddenField($fullscreen ? 'mediado' : 'do', 'searchlist');
1586554a8c9fSAdrian Lang
1587b960c74fSSatoshi Sahara    $form->addTagOpen('p');
1588bde2a644SSatoshi Sahara    $form->addTextInput('q', $lang['searchmedia'])
1589b960c74fSSatoshi Sahara            ->attr('title', sprintf($lang['searchmedia_in'], hsc($ns) . ':*'))
1590b960c74fSSatoshi Sahara            ->val($query);
1591bde2a644SSatoshi Sahara    $form->addHTML(' ');
1592b960c74fSSatoshi Sahara    $form->addButton('', $lang['btn_search'])->attr('type', 'submit');
1593b960c74fSSatoshi Sahara    $form->addTagClose('p');
1594b960c74fSSatoshi Sahara    $form->addTagClose('div');
159526dfc232SAndreas Gohr    echo $form->toHTML('SearchMedia');
1596bf1f3ac4Ssarnowski}
1597bf1f3ac4Ssarnowski
1598bf1f3ac4Ssarnowski/**
15993df72098SAndreas Gohr * Build a tree outline of available media namespaces
16003df72098SAndreas Gohr *
16013df72098SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org>
1602e0c26282SGerrit Uitslag *
1603e0c26282SGerrit Uitslag * @param string $ns
16043df72098SAndreas Gohr */
1605d868eb89SAndreas Gohrfunction media_nstree($ns)
1606d868eb89SAndreas Gohr{
16073df72098SAndreas Gohr    global $conf;
1608256ca81eSAndreas Gohr    global $lang;
16093df72098SAndreas Gohr
16103df72098SAndreas Gohr    // currently selected namespace
16113df72098SAndreas Gohr    $ns = cleanID($ns);
16123df72098SAndreas Gohr    if (empty($ns)) {
1613de3eb1d7SAdrian Lang        global $ID;
1614dd90013aSMichael Hamann        $ns = (string)getNS($ID);
16153df72098SAndreas Gohr    }
1616dd90013aSMichael Hamann
1617dd90013aSMichael Hamann    $ns_dir = utf8_encodeFN(str_replace(':', '/', $ns));
16183df72098SAndreas Gohr
161924870174SAndreas Gohr    $data = [];
162024870174SAndreas Gohr    search($data, $conf['mediadir'], 'search_index', ['ns' => $ns_dir, 'nofiles' => true]);
16213df72098SAndreas Gohr
16223df72098SAndreas Gohr    // wrap a list with the root level around the other namespaces
162324870174SAndreas Gohr    array_unshift($data, ['level' => 0, 'id' => '', 'open' => 'true', 'label' => '[' . $lang['mediaroot'] . ']']);
16243df72098SAndreas Gohr
1625dd90013aSMichael Hamann    // insert the current ns into the hierarchy if it isn't already part of it
1626dd90013aSMichael Hamann    $ns_parts = explode(':', $ns);
1627dd90013aSMichael Hamann    $tmp_ns = '';
1628dd90013aSMichael Hamann    $pos = 0;
1629de301a35SDamien Regad    $insert = false;
1630dd90013aSMichael Hamann    foreach ($ns_parts as $level => $part) {
1631186263b2SDamien Regad        if ($tmp_ns) {
1632186263b2SDamien Regad            $tmp_ns .= ':' . $part;
1633186263b2SDamien Regad        } else {
1634186263b2SDamien Regad            $tmp_ns = $part;
1635186263b2SDamien Regad        }
1636dd90013aSMichael Hamann
1637de301a35SDamien Regad        // find the namespace parts
16389988e853SDamien Regad        while (array_key_exists($pos, $data) && $data[$pos]['id'] != $tmp_ns) {
163964159a61SAndreas Gohr            if (
164064159a61SAndreas Gohr                $pos >= count($data) ||
16412d85e841SAndreas Gohr                ($data[$pos]['level'] <= $level + 1 && Sort::strcmp($data[$pos]['id'], $tmp_ns) > 0)
164264159a61SAndreas Gohr            ) {
1643de301a35SDamien Regad                $insert = true;
1644dd90013aSMichael Hamann                break;
1645dd90013aSMichael Hamann            }
1646dd90013aSMichael Hamann            ++$pos;
1647dd90013aSMichael Hamann        }
1648de301a35SDamien Regad        // insert namespace in hierarchy; if not found in above loop, append it to the end
1649b3894732Ssplitbrain        if ($insert || $pos === count($data)) {
1650de301a35SDamien Regad            array_splice($data, $pos, 0, [['level' => $level + 1, 'id' => $tmp_ns, 'open' => 'true']]);
1651de301a35SDamien Regad        }
1652dd90013aSMichael Hamann    }
1653dd90013aSMichael Hamann
16543df72098SAndreas Gohr    echo html_buildlist($data, 'idx', 'media_nstree_item', 'media_nstree_li');
16553df72098SAndreas Gohr}
16563df72098SAndreas Gohr
16573df72098SAndreas Gohr/**
16583df72098SAndreas Gohr * Userfunction for html_buildlist
16593df72098SAndreas Gohr *
16603df72098SAndreas Gohr * Prints a media namespace tree item
16613df72098SAndreas Gohr *
16623df72098SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org>
166342ea7f44SGerrit Uitslag *
166442ea7f44SGerrit Uitslag * @param array $item
166542ea7f44SGerrit Uitslag * @return string html
16663df72098SAndreas Gohr */
1667d868eb89SAndreas Gohrfunction media_nstree_item($item)
1668d868eb89SAndreas Gohr{
16698108113cSTom N Harris    global $INPUT;
16703df72098SAndreas Gohr    $pos   = strrpos($item['id'], ':');
16713df72098SAndreas Gohr    $label = substr($item['id'], $pos > 0 ? $pos + 1 : 0);
16720e80bb5eSChristopher Smith    if (empty($item['label'])) $item['label'] = $label;
16733df72098SAndreas Gohr
16743df72098SAndreas Gohr    $ret  = '';
167524870174SAndreas Gohr    if ($INPUT->str('do') != 'media')
16763df72098SAndreas Gohr    $ret .= '<a href="' . DOKU_BASE . 'lib/exe/mediamanager.php?ns=' . idfilter($item['id']) . '" class="idx_dir">';
1677b960c74fSSatoshi Sahara    else $ret .= '<a href="' . media_managerURL(['ns' => idfilter($item['id'], false), 'tab_files' => 'files'])
167823846a98SKate Arzamastseva        . '" class="idx_dir">';
16793df72098SAndreas Gohr    $ret .= $item['label'];
16803df72098SAndreas Gohr    $ret .= '</a>';
16813df72098SAndreas Gohr    return $ret;
16823df72098SAndreas Gohr}
16833df72098SAndreas Gohr
16843df72098SAndreas Gohr/**
16853df72098SAndreas Gohr * Userfunction for html_buildlist
16863df72098SAndreas Gohr *
16873df72098SAndreas Gohr * Prints a media namespace tree item opener
16883df72098SAndreas Gohr *
16893df72098SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org>
169042ea7f44SGerrit Uitslag *
169142ea7f44SGerrit Uitslag * @param array $item
169242ea7f44SGerrit Uitslag * @return string html
16933df72098SAndreas Gohr */
1694d868eb89SAndreas Gohrfunction media_nstree_li($item)
1695d868eb89SAndreas Gohr{
16963df72098SAndreas Gohr    $class = 'media level' . $item['level'];
16973df72098SAndreas Gohr    if ($item['open']) {
16983df72098SAndreas Gohr        $class .= ' open';
16993df72098SAndreas Gohr        $img   = DOKU_BASE . 'lib/images/minus.gif';
1700e260f93bSAnika Henke        $alt   = '−';
17013df72098SAndreas Gohr    } else {
17023df72098SAndreas Gohr        $class .= ' closed';
17033df72098SAndreas Gohr        $img   = DOKU_BASE . 'lib/images/plus.gif';
17047af1b404SAnika Henke        $alt   = '+';
17053df72098SAndreas Gohr    }
1706231b8c34SPierre Spring    // TODO: only deliver an image if it actually has a subtree...
17073df72098SAndreas Gohr    return '<li class="' . $class . '">' .
17087af1b404SAnika Henke        '<img src="' . $img . '" alt="' . $alt . '" />';
17093df72098SAndreas Gohr}
171013c08e2fSMichael Klier
171113c08e2fSMichael Klier/**
1712b021f0b4SAndreas Gohr * Resizes or crop the given image to the given size
1713b021f0b4SAndreas Gohr *
1714b021f0b4SAndreas Gohr * @author  Andreas Gohr <andi@splitbrain.org>
1715b021f0b4SAndreas Gohr *
1716b021f0b4SAndreas Gohr * @param string $file filename, path to file
1717b021f0b4SAndreas Gohr * @param string $ext  extension
1718b021f0b4SAndreas Gohr * @param int    $w    desired width
1719b021f0b4SAndreas Gohr * @param int    $h    desired height
1720b021f0b4SAndreas Gohr * @param bool   $crop should a center crop be used?
1721b021f0b4SAndreas Gohr * @return string path to resized or original size if failed
1722b021f0b4SAndreas Gohr */
1723b021f0b4SAndreas Gohrfunction media_mod_image($file, $ext, $w, $h = 0, $crop = false)
1724b021f0b4SAndreas Gohr{
1725b021f0b4SAndreas Gohr    global $conf;
1726d967ecd8SAndreas Gohr    if (!$h) $h = 0;
1727b021f0b4SAndreas Gohr    // we wont scale up to infinity
1728b021f0b4SAndreas Gohr    if ($w > 2000 || $h > 2000) return $file;
1729b021f0b4SAndreas Gohr
1730b021f0b4SAndreas Gohr    $operation = $crop ? 'crop' : 'resize';
1731b021f0b4SAndreas Gohr
1732b021f0b4SAndreas Gohr    $options = [
1733b021f0b4SAndreas Gohr        'quality' => $conf['jpg_quality'],
1734b021f0b4SAndreas Gohr        'imconvert' => $conf['im_convert'],
1735b021f0b4SAndreas Gohr    ];
1736b021f0b4SAndreas Gohr
173724870174SAndreas Gohr    $cache = new CacheImageMod($file, $w, $h, $ext, $crop);
1738b021f0b4SAndreas Gohr    if (!$cache->useCache()) {
1739b021f0b4SAndreas Gohr        try {
1740b021f0b4SAndreas Gohr            Slika::run($file, $options)
1741b021f0b4SAndreas Gohr                 ->autorotate()
1742b021f0b4SAndreas Gohr                 ->$operation($w, $h)
1743b021f0b4SAndreas Gohr                 ->save($cache->cache, $ext);
1744b021f0b4SAndreas Gohr            if ($conf['fperm']) @chmod($cache->cache, $conf['fperm']);
174524870174SAndreas Gohr        } catch (Exception $e) {
1746b021f0b4SAndreas Gohr            Logger::debug($e->getMessage());
1747b021f0b4SAndreas Gohr            return $file;
1748b021f0b4SAndreas Gohr        }
1749b021f0b4SAndreas Gohr    }
1750b021f0b4SAndreas Gohr
1751b021f0b4SAndreas Gohr    return $cache->cache;
1752b021f0b4SAndreas Gohr}
1753b021f0b4SAndreas Gohr
1754b021f0b4SAndreas Gohr/**
175513c08e2fSMichael Klier * Resizes the given image to the given size
175613c08e2fSMichael Klier *
175713c08e2fSMichael Klier * @author  Andreas Gohr <andi@splitbrain.org>
175842ea7f44SGerrit Uitslag *
175942ea7f44SGerrit Uitslag * @param string $file filename, path to file
176042ea7f44SGerrit Uitslag * @param string $ext  extension
176142ea7f44SGerrit Uitslag * @param int    $w    desired width
176242ea7f44SGerrit Uitslag * @param int    $h    desired height
176342ea7f44SGerrit Uitslag * @return string path to resized or original size if failed
176413c08e2fSMichael Klier */
1765b021f0b4SAndreas Gohrfunction media_resize_image($file, $ext, $w, $h = 0)
1766b021f0b4SAndreas Gohr{
1767b021f0b4SAndreas Gohr    return media_mod_image($file, $ext, $w, $h, false);
1768d2bd34a5SAndreas Gohr}
176913c08e2fSMichael Klier
177013c08e2fSMichael Klier/**
1771d2bd34a5SAndreas Gohr * Center crops the given image to the wanted size
177213c08e2fSMichael Klier *
177313c08e2fSMichael Klier * @author  Andreas Gohr <andi@splitbrain.org>
177442ea7f44SGerrit Uitslag *
177542ea7f44SGerrit Uitslag * @param string $file filename, path to file
177642ea7f44SGerrit Uitslag * @param string $ext  extension
177742ea7f44SGerrit Uitslag * @param int    $w    desired width
177842ea7f44SGerrit Uitslag * @param int    $h    desired height
177942ea7f44SGerrit Uitslag * @return string path to resized or original size if failed
178013c08e2fSMichael Klier */
1781b021f0b4SAndreas Gohrfunction media_crop_image($file, $ext, $w, $h = 0)
1782b021f0b4SAndreas Gohr{
1783b021f0b4SAndreas Gohr    return media_mod_image($file, $ext, $w, $h, true);
178413c08e2fSMichael Klier}
178513c08e2fSMichael Klier
178613c08e2fSMichael Klier/**
17870f4e0092SChristopher Smith * Calculate a token to be used to verify fetch requests for resized or
17880f4e0092SChristopher Smith * cropped images have been internally generated - and prevent external
17890f4e0092SChristopher Smith * DDOS attacks via fetch
17900f4e0092SChristopher Smith *
17913c124064SAndreas Gohr * @author Christopher Smith <chris@jalakai.co.uk>
17923c124064SAndreas Gohr *
17930f4e0092SChristopher Smith * @param string  $id    id of the image
17940f4e0092SChristopher Smith * @param int     $w     resize/crop width
17950f4e0092SChristopher Smith * @param int     $h     resize/crop height
179642ea7f44SGerrit Uitslag * @return string token or empty string if no token required
17970f4e0092SChristopher Smith */
1798d868eb89SAndreas Gohrfunction media_get_token($id, $w, $h)
1799d868eb89SAndreas Gohr{
18000f4e0092SChristopher Smith    // token is only required for modified images
1801cc036f74SKlap-in    if ($w || $h || media_isexternal($id)) {
18023c124064SAndreas Gohr        $token = $id;
18030f4e0092SChristopher Smith        if ($w) $token .= '.' . $w;
18040f4e0092SChristopher Smith        if ($h) $token .= '.' . $h;
18050f4e0092SChristopher Smith
180624870174SAndreas Gohr        return substr(PassHash::hmac('md5', $token, auth_cookiesalt()), 0, 6);
18070f4e0092SChristopher Smith    }
18080f4e0092SChristopher Smith
18090f4e0092SChristopher Smith    return '';
18100f4e0092SChristopher Smith}
18110f4e0092SChristopher Smith
18120f4e0092SChristopher Smith/**
181313c08e2fSMichael Klier * Download a remote file and return local filename
181413c08e2fSMichael Klier *
181513c08e2fSMichael Klier * returns false if download fails. Uses cached file if available and
181613c08e2fSMichael Klier * wanted
181713c08e2fSMichael Klier *
181813c08e2fSMichael Klier * @author  Andreas Gohr <andi@splitbrain.org>
181913c08e2fSMichael Klier * @author  Pavel Vitis <Pavel.Vitis@seznam.cz>
182042ea7f44SGerrit Uitslag *
182142ea7f44SGerrit Uitslag * @param string $url
182242ea7f44SGerrit Uitslag * @param string $ext   extension
182342ea7f44SGerrit Uitslag * @param int    $cache cachetime in seconds
182442ea7f44SGerrit Uitslag * @return false|string path to cached file
182513c08e2fSMichael Klier */
1826d868eb89SAndreas Gohrfunction media_get_from_URL($url, $ext, $cache)
1827d868eb89SAndreas Gohr{
182813c08e2fSMichael Klier    global $conf;
182913c08e2fSMichael Klier
183013c08e2fSMichael Klier    // if no cache or fetchsize just redirect
183113c08e2fSMichael Klier    if ($cache == 0)           return false;
183213c08e2fSMichael Klier    if (!$conf['fetchsize']) return false;
183313c08e2fSMichael Klier
183413c08e2fSMichael Klier    $local = getCacheName(strtolower($url), ".media.$ext");
183513c08e2fSMichael Klier    $mtime = @filemtime($local); // 0 if not exists
183613c08e2fSMichael Klier
183713c08e2fSMichael Klier    //decide if download needed:
18387d34963bSAndreas Gohr    if (
18397d34963bSAndreas Gohr        ($mtime == 0) || // cache does not exist
184013c08e2fSMichael Klier        ($cache != -1 && $mtime < time() - $cache) // 'recache' and cache has expired
184113c08e2fSMichael Klier    ) {
184213c08e2fSMichael Klier        if (media_image_download($url, $local)) {
184313c08e2fSMichael Klier            return $local;
184413c08e2fSMichael Klier        } else {
184513c08e2fSMichael Klier            return false;
184613c08e2fSMichael Klier        }
184713c08e2fSMichael Klier    }
184813c08e2fSMichael Klier
184913c08e2fSMichael Klier    //if cache exists use it else
185013c08e2fSMichael Klier    if ($mtime) return $local;
185113c08e2fSMichael Klier
185213c08e2fSMichael Klier    //else return false
185313c08e2fSMichael Klier    return false;
185413c08e2fSMichael Klier}
185513c08e2fSMichael Klier
185613c08e2fSMichael Klier/**
185713c08e2fSMichael Klier * Download image files
185813c08e2fSMichael Klier *
185913c08e2fSMichael Klier * @author Andreas Gohr <andi@splitbrain.org>
186042ea7f44SGerrit Uitslag *
186142ea7f44SGerrit Uitslag * @param string $url
186242ea7f44SGerrit Uitslag * @param string $file path to file in which to put the downloaded content
186342ea7f44SGerrit Uitslag * @return bool
186413c08e2fSMichael Klier */
1865d868eb89SAndreas Gohrfunction media_image_download($url, $file)
1866d868eb89SAndreas Gohr{
186713c08e2fSMichael Klier    global $conf;
186813c08e2fSMichael Klier    $http = new DokuHTTPClient();
1869a5951419SAndreas Gohr    $http->keep_alive = false; // we do single ops here, no need for keep-alive
1870a5951419SAndreas Gohr
187113c08e2fSMichael Klier    $http->max_bodysize = $conf['fetchsize'];
187213c08e2fSMichael Klier    $http->timeout = 25; //max. 25 sec
187313c08e2fSMichael Klier    $http->header_regexp = '!\r\nContent-Type: image/(jpe?g|gif|png)!i';
187413c08e2fSMichael Klier
187513c08e2fSMichael Klier    $data = $http->get($url);
187613c08e2fSMichael Klier    if (!$data) return false;
187713c08e2fSMichael Klier
187879e79377SAndreas Gohr    $fileexists = file_exists($file);
187913c08e2fSMichael Klier    $fp = @fopen($file, "w");
188013c08e2fSMichael Klier    if (!$fp) return false;
188113c08e2fSMichael Klier    fwrite($fp, $data);
188213c08e2fSMichael Klier    fclose($fp);
188324870174SAndreas Gohr    if (!$fileexists && $conf['fperm']) chmod($file, $conf['fperm']);
188413c08e2fSMichael Klier
188513c08e2fSMichael Klier    // check if it is really an image
188613c08e2fSMichael Klier    $info = @getimagesize($file);
188713c08e2fSMichael Klier    if (!$info) {
188813c08e2fSMichael Klier        @unlink($file);
188913c08e2fSMichael Klier        return false;
189013c08e2fSMichael Klier    }
189113c08e2fSMichael Klier
189213c08e2fSMichael Klier    return true;
189313c08e2fSMichael Klier}
189413c08e2fSMichael Klier
189513c08e2fSMichael Klier/**
189613c08e2fSMichael Klier * resize images using external ImageMagick convert program
189713c08e2fSMichael Klier *
189813c08e2fSMichael Klier * @author Pavel Vitis <Pavel.Vitis@seznam.cz>
189913c08e2fSMichael Klier * @author Andreas Gohr <andi@splitbrain.org>
190042ea7f44SGerrit Uitslag *
190142ea7f44SGerrit Uitslag * @param string $ext     extension
190242ea7f44SGerrit Uitslag * @param string $from    filename path to file
190342ea7f44SGerrit Uitslag * @param int    $from_w  original width
190442ea7f44SGerrit Uitslag * @param int    $from_h  original height
190542ea7f44SGerrit Uitslag * @param string $to      path to resized file
190642ea7f44SGerrit Uitslag * @param int    $to_w    desired width
190742ea7f44SGerrit Uitslag * @param int    $to_h    desired height
190842ea7f44SGerrit Uitslag * @return bool
190913c08e2fSMichael Klier */
1910d868eb89SAndreas Gohrfunction media_resize_imageIM($ext, $from, $from_w, $from_h, $to, $to_w, $to_h)
1911d868eb89SAndreas Gohr{
191213c08e2fSMichael Klier    global $conf;
191313c08e2fSMichael Klier
191413c08e2fSMichael Klier    // check if convert is configured
191513c08e2fSMichael Klier    if (!$conf['im_convert']) return false;
191613c08e2fSMichael Klier
191713c08e2fSMichael Klier    // prepare command
191813c08e2fSMichael Klier    $cmd  = $conf['im_convert'];
191913c08e2fSMichael Klier    $cmd .= ' -resize ' . $to_w . 'x' . $to_h . '!';
192013c08e2fSMichael Klier    if ($ext == 'jpg' || $ext == 'jpeg') {
192113c08e2fSMichael Klier        $cmd .= ' -quality ' . $conf['jpg_quality'];
192213c08e2fSMichael Klier    }
192313c08e2fSMichael Klier    $cmd .= " $from $to";
192413c08e2fSMichael Klier
192513c08e2fSMichael Klier    @exec($cmd, $out, $retval);
192613c08e2fSMichael Klier    if ($retval == 0) return true;
192713c08e2fSMichael Klier    return false;
192813c08e2fSMichael Klier}
192913c08e2fSMichael Klier
193013c08e2fSMichael Klier/**
193113c08e2fSMichael Klier * crop images using external ImageMagick convert program
193213c08e2fSMichael Klier *
193313c08e2fSMichael Klier * @author Andreas Gohr <andi@splitbrain.org>
193442ea7f44SGerrit Uitslag *
193542ea7f44SGerrit Uitslag * @param string $ext     extension
193642ea7f44SGerrit Uitslag * @param string $from    filename path to file
193742ea7f44SGerrit Uitslag * @param int    $from_w  original width
193842ea7f44SGerrit Uitslag * @param int    $from_h  original height
193942ea7f44SGerrit Uitslag * @param string $to      path to resized file
194042ea7f44SGerrit Uitslag * @param int    $to_w    desired width
194142ea7f44SGerrit Uitslag * @param int    $to_h    desired height
194242ea7f44SGerrit Uitslag * @param int    $ofs_x   offset of crop centre
194342ea7f44SGerrit Uitslag * @param int    $ofs_y   offset of crop centre
194442ea7f44SGerrit Uitslag * @return bool
1945d2bd34a5SAndreas Gohr * @deprecated 2020-09-01
194613c08e2fSMichael Klier */
1947d868eb89SAndreas Gohrfunction media_crop_imageIM($ext, $from, $from_w, $from_h, $to, $to_w, $to_h, $ofs_x, $ofs_y)
1948d868eb89SAndreas Gohr{
194913c08e2fSMichael Klier    global $conf;
1950d2bd34a5SAndreas Gohr    dbg_deprecated('splitbrain\\Slika');
195113c08e2fSMichael Klier
195213c08e2fSMichael Klier    // check if convert is configured
195313c08e2fSMichael Klier    if (!$conf['im_convert']) return false;
195413c08e2fSMichael Klier
195513c08e2fSMichael Klier    // prepare command
195613c08e2fSMichael Klier    $cmd  = $conf['im_convert'];
195713c08e2fSMichael Klier    $cmd .= ' -crop ' . $to_w . 'x' . $to_h . '+' . $ofs_x . '+' . $ofs_y;
195813c08e2fSMichael Klier    if ($ext == 'jpg' || $ext == 'jpeg') {
195913c08e2fSMichael Klier        $cmd .= ' -quality ' . $conf['jpg_quality'];
196013c08e2fSMichael Klier    }
196113c08e2fSMichael Klier    $cmd .= " $from $to";
196213c08e2fSMichael Klier
196313c08e2fSMichael Klier    @exec($cmd, $out, $retval);
196413c08e2fSMichael Klier    if ($retval == 0) return true;
196513c08e2fSMichael Klier    return false;
196613c08e2fSMichael Klier}
196713c08e2fSMichael Klier
196813c08e2fSMichael Klier/**
196913c08e2fSMichael Klier * resize or crop images using PHP's libGD support
197013c08e2fSMichael Klier *
197113c08e2fSMichael Klier * @author Andreas Gohr <andi@splitbrain.org>
197213c08e2fSMichael Klier * @author Sebastian Wienecke <s_wienecke@web.de>
197342ea7f44SGerrit Uitslag *
197442ea7f44SGerrit Uitslag * @param string $ext     extension
197542ea7f44SGerrit Uitslag * @param string $from    filename path to file
197642ea7f44SGerrit Uitslag * @param int    $from_w  original width
197742ea7f44SGerrit Uitslag * @param int    $from_h  original height
197842ea7f44SGerrit Uitslag * @param string $to      path to resized file
197942ea7f44SGerrit Uitslag * @param int    $to_w    desired width
198042ea7f44SGerrit Uitslag * @param int    $to_h    desired height
198142ea7f44SGerrit Uitslag * @param int    $ofs_x   offset of crop centre
198242ea7f44SGerrit Uitslag * @param int    $ofs_y   offset of crop centre
198342ea7f44SGerrit Uitslag * @return bool
1984d2bd34a5SAndreas Gohr * @deprecated 2020-09-01
198513c08e2fSMichael Klier */
1986d868eb89SAndreas Gohrfunction media_resize_imageGD($ext, $from, $from_w, $from_h, $to, $to_w, $to_h, $ofs_x = 0, $ofs_y = 0)
1987d868eb89SAndreas Gohr{
198813c08e2fSMichael Klier    global $conf;
1989d2bd34a5SAndreas Gohr    dbg_deprecated('splitbrain\\Slika');
199013c08e2fSMichael Klier
199113c08e2fSMichael Klier    if ($conf['gdlib'] < 1) return false; //no GDlib available or wanted
199213c08e2fSMichael Klier
199313c08e2fSMichael Klier    // check available memory
199413c08e2fSMichael Klier    if (!is_mem_available(($from_w * $from_h * 4) + ($to_w * $to_h * 4))) {
199513c08e2fSMichael Klier        return false;
199613c08e2fSMichael Klier    }
199713c08e2fSMichael Klier
199813c08e2fSMichael Klier    // create an image of the given filetype
199959bc3b48SGerrit Uitslag    $image = false;
200013c08e2fSMichael Klier    if ($ext == 'jpg' || $ext == 'jpeg') {
200113c08e2fSMichael Klier        if (!function_exists("imagecreatefromjpeg")) return false;
200213c08e2fSMichael Klier        $image = @imagecreatefromjpeg($from);
200313c08e2fSMichael Klier    } elseif ($ext == 'png') {
200413c08e2fSMichael Klier        if (!function_exists("imagecreatefrompng")) return false;
200513c08e2fSMichael Klier        $image = @imagecreatefrompng($from);
200613c08e2fSMichael Klier    } elseif ($ext == 'gif') {
200713c08e2fSMichael Klier        if (!function_exists("imagecreatefromgif")) return false;
200813c08e2fSMichael Klier        $image = @imagecreatefromgif($from);
200913c08e2fSMichael Klier    }
201013c08e2fSMichael Klier    if (!$image) return false;
201113c08e2fSMichael Klier
201259bc3b48SGerrit Uitslag    $newimg = false;
201313c08e2fSMichael Klier    if (($conf['gdlib'] > 1) && function_exists("imagecreatetruecolor") && $ext != 'gif') {
201413c08e2fSMichael Klier        $newimg = @imagecreatetruecolor($to_w, $to_h);
201513c08e2fSMichael Klier    }
201613c08e2fSMichael Klier    if (!$newimg) $newimg = @imagecreate($to_w, $to_h);
201713c08e2fSMichael Klier    if (!$newimg) {
201813c08e2fSMichael Klier        imagedestroy($image);
201913c08e2fSMichael Klier        return false;
202013c08e2fSMichael Klier    }
202113c08e2fSMichael Klier
202213c08e2fSMichael Klier    //keep png alpha channel if possible
202313c08e2fSMichael Klier    if ($ext == 'png' && $conf['gdlib'] > 1 && function_exists('imagesavealpha')) {
202413c08e2fSMichael Klier        imagealphablending($newimg, false);
202513c08e2fSMichael Klier        imagesavealpha($newimg, true);
202613c08e2fSMichael Klier    }
202713c08e2fSMichael Klier
202813c08e2fSMichael Klier    //keep gif transparent color if possible
202913c08e2fSMichael Klier    if ($ext == 'gif' && function_exists('imagefill') && function_exists('imagecolorallocate')) {
203013c08e2fSMichael Klier        if (function_exists('imagecolorsforindex') && function_exists('imagecolortransparent')) {
203113c08e2fSMichael Klier            $transcolorindex = @imagecolortransparent($image);
203213c08e2fSMichael Klier            if ($transcolorindex >= 0) { //transparent color exists
203313c08e2fSMichael Klier                $transcolor = @imagecolorsforindex($image, $transcolorindex);
203464159a61SAndreas Gohr                $transcolorindex = @imagecolorallocate(
203564159a61SAndreas Gohr                    $newimg,
203664159a61SAndreas Gohr                    $transcolor['red'],
203764159a61SAndreas Gohr                    $transcolor['green'],
203864159a61SAndreas Gohr                    $transcolor['blue']
203964159a61SAndreas Gohr                );
204013c08e2fSMichael Klier                @imagefill($newimg, 0, 0, $transcolorindex);
204113c08e2fSMichael Klier                @imagecolortransparent($newimg, $transcolorindex);
204213c08e2fSMichael Klier            } else { //filling with white
204313c08e2fSMichael Klier                $whitecolorindex = @imagecolorallocate($newimg, 255, 255, 255);
204413c08e2fSMichael Klier                @imagefill($newimg, 0, 0, $whitecolorindex);
204513c08e2fSMichael Klier            }
204613c08e2fSMichael Klier        } else { //filling with white
204713c08e2fSMichael Klier            $whitecolorindex = @imagecolorallocate($newimg, 255, 255, 255);
204813c08e2fSMichael Klier            @imagefill($newimg, 0, 0, $whitecolorindex);
204913c08e2fSMichael Klier        }
205013c08e2fSMichael Klier    }
205113c08e2fSMichael Klier
205213c08e2fSMichael Klier    //try resampling first
205313c08e2fSMichael Klier    if (function_exists("imagecopyresampled")) {
205413c08e2fSMichael Klier        if (!@imagecopyresampled($newimg, $image, 0, 0, $ofs_x, $ofs_y, $to_w, $to_h, $from_w, $from_h)) {
205513c08e2fSMichael Klier            imagecopyresized($newimg, $image, 0, 0, $ofs_x, $ofs_y, $to_w, $to_h, $from_w, $from_h);
205613c08e2fSMichael Klier        }
205713c08e2fSMichael Klier    } else {
205813c08e2fSMichael Klier        imagecopyresized($newimg, $image, 0, 0, $ofs_x, $ofs_y, $to_w, $to_h, $from_w, $from_h);
205913c08e2fSMichael Klier    }
206013c08e2fSMichael Klier
206113c08e2fSMichael Klier    $okay = false;
206213c08e2fSMichael Klier    if ($ext == 'jpg' || $ext == 'jpeg') {
206313c08e2fSMichael Klier        if (!function_exists('imagejpeg')) {
206413c08e2fSMichael Klier            $okay = false;
206513c08e2fSMichael Klier        } else {
206613c08e2fSMichael Klier            $okay = imagejpeg($newimg, $to, $conf['jpg_quality']);
206713c08e2fSMichael Klier        }
206813c08e2fSMichael Klier    } elseif ($ext == 'png') {
206913c08e2fSMichael Klier        if (!function_exists('imagepng')) {
207013c08e2fSMichael Klier            $okay = false;
207113c08e2fSMichael Klier        } else {
207213c08e2fSMichael Klier            $okay =  imagepng($newimg, $to);
207313c08e2fSMichael Klier        }
207413c08e2fSMichael Klier    } elseif ($ext == 'gif') {
207513c08e2fSMichael Klier        if (!function_exists('imagegif')) {
207613c08e2fSMichael Klier            $okay = false;
207713c08e2fSMichael Klier        } else {
207813c08e2fSMichael Klier            $okay = imagegif($newimg, $to);
207913c08e2fSMichael Klier        }
208013c08e2fSMichael Klier    }
208113c08e2fSMichael Klier
208279a2d784SGerrit Uitslag    // destroy GD image resources
208379a2d784SGerrit Uitslag    imagedestroy($image);
208479a2d784SGerrit Uitslag    imagedestroy($newimg);
208513c08e2fSMichael Klier
208613c08e2fSMichael Klier    return $okay;
208713c08e2fSMichael Klier}
208813c08e2fSMichael Klier
20893d7a9e0aSAnika Henke/**
20903d7a9e0aSAnika Henke * Return other media files with the same base name
209179e53fe5SAnika Henke * but different extensions.
20923d7a9e0aSAnika Henke *
20933d7a9e0aSAnika Henke * @param string   $src     - ID of media file
209442ea7f44SGerrit Uitslag * @param string[] $exts    - alternative extensions to find other files for
209542ea7f44SGerrit Uitslag * @return array            - array(mime type => file ID)
20963d7a9e0aSAnika Henke *
20973d7a9e0aSAnika Henke * @author Anika Henke <anika@selfthinker.org>
20983d7a9e0aSAnika Henke */
2099d868eb89SAndreas Gohrfunction media_alternativefiles($src, $exts)
2100d868eb89SAndreas Gohr{
21013d7a9e0aSAnika Henke
210224870174SAndreas Gohr    $files = [];
2103a19c9aa0SGerrit Uitslag    [$srcExt, /* srcMime */] = mimetype($src);
21043d7a9e0aSAnika Henke    $filebase = substr($src, 0, -1 * (strlen($srcExt) + 1));
21053d7a9e0aSAnika Henke
21063d7a9e0aSAnika Henke    foreach ($exts as $ext) {
21073d7a9e0aSAnika Henke        $fileid = $filebase . '.' . $ext;
21083d7a9e0aSAnika Henke        $file = mediaFN($fileid);
21093d7a9e0aSAnika Henke        if (file_exists($file)) {
2110a19c9aa0SGerrit Uitslag            [/* fileExt */, $fileMime] = mimetype($file);
211179e53fe5SAnika Henke            $files[$fileMime] = $fileid;
211299f943f6SAnika Henke        }
21133d7a9e0aSAnika Henke    }
21143d7a9e0aSAnika Henke    return $files;
21153d7a9e0aSAnika Henke}
21163d7a9e0aSAnika Henke
2117f50634f0SAnika Henke/**
2118f50634f0SAnika Henke * Check if video/audio is supported to be embedded.
2119f50634f0SAnika Henke *
2120b83a74f1SAnika Henke * @param string $mime      - mimetype of media file
212142ea7f44SGerrit Uitslag * @param string $type      - type of media files to check ('video', 'audio', or null for all)
2122f50634f0SAnika Henke * @return boolean
2123f50634f0SAnika Henke *
2124f50634f0SAnika Henke * @author Anika Henke <anika@selfthinker.org>
2125f50634f0SAnika Henke */
2126ed823bcdSAndreas Gohrfunction media_supportedav($mime, $type = null)
2127d868eb89SAndreas Gohr{
212824870174SAndreas Gohr    $supportedAudio = [
2129f50634f0SAnika Henke        'ogg' => 'audio/ogg',
2130f50634f0SAnika Henke        'mp3' => 'audio/mpeg',
213124870174SAndreas Gohr        'wav' => 'audio/wav'
213224870174SAndreas Gohr    ];
213324870174SAndreas Gohr    $supportedVideo = [
2134f50634f0SAnika Henke        'webm' => 'video/webm',
2135f50634f0SAnika Henke        'ogv' => 'video/ogg',
213624870174SAndreas Gohr        'mp4' => 'video/mp4'
213724870174SAndreas Gohr    ];
2138f50634f0SAnika Henke    if ($type == 'audio') {
2139f50634f0SAnika Henke        $supportedAv = $supportedAudio;
2140f50634f0SAnika Henke    } elseif ($type == 'video') {
2141f50634f0SAnika Henke        $supportedAv = $supportedVideo;
2142f50634f0SAnika Henke    } else {
2143f50634f0SAnika Henke        $supportedAv = array_merge($supportedAudio, $supportedVideo);
2144f50634f0SAnika Henke    }
2145f50634f0SAnika Henke    return in_array($mime, $supportedAv);
2146f50634f0SAnika Henke}
2147f50634f0SAnika Henke
21480877a1f1SSchplurtz le Déboulonné/**
21490877a1f1SSchplurtz le Déboulonné * Return track media files with the same base name
21500877a1f1SSchplurtz le Déboulonné * but extensions that indicate kind and lang.
21510877a1f1SSchplurtz le Déboulonné * ie for foo.webm search foo.sub.lang.vtt, foo.cap.lang.vtt...
21520877a1f1SSchplurtz le Déboulonné *
21530877a1f1SSchplurtz le Déboulonné * @param string   $src     - ID of media file
215401299338SSchplurtz le Déboulonné * @return array            - array(mediaID => array( kind, srclang ))
21550877a1f1SSchplurtz le Déboulonné *
21560877a1f1SSchplurtz le Déboulonné * @author Schplurtz le Déboulonné <Schplurtz@laposte.net>
21570877a1f1SSchplurtz le Déboulonné */
2158d868eb89SAndreas Gohrfunction media_trackfiles($src)
2159d868eb89SAndreas Gohr{
216024870174SAndreas Gohr    $kinds = [
21610877a1f1SSchplurtz le Déboulonné        'sub' => 'subtitles',
21620877a1f1SSchplurtz le Déboulonné        'cap' => 'captions',
21630877a1f1SSchplurtz le Déboulonné        'des' => 'descriptions',
21640877a1f1SSchplurtz le Déboulonné        'cha' => 'chapters',
21650877a1f1SSchplurtz le Déboulonné        'met' => 'metadata'
216624870174SAndreas Gohr    ];
21670877a1f1SSchplurtz le Déboulonné
216824870174SAndreas Gohr    $files = [];
21690877a1f1SSchplurtz le Déboulonné    $re = '/\\.(sub|cap|des|cha|met)\\.([^.]+)\\.vtt$/';
21700877a1f1SSchplurtz le Déboulonné    $baseid = pathinfo($src, PATHINFO_FILENAME);
21710877a1f1SSchplurtz le Déboulonné    $pattern = mediaFN($baseid) . '.*.*.vtt';
21720877a1f1SSchplurtz le Déboulonné    $list = glob($pattern);
21730877a1f1SSchplurtz le Déboulonné    foreach ($list as $track) {
21740877a1f1SSchplurtz le Déboulonné        if (preg_match($re, $track, $matches)) {
217524870174SAndreas Gohr            $files[$baseid . '.' . $matches[1] . '.' . $matches[2] . '.vtt'] = [$kinds[$matches[1]], $matches[2]];
21760877a1f1SSchplurtz le Déboulonné        }
21770877a1f1SSchplurtz le Déboulonné    }
21780877a1f1SSchplurtz le Déboulonné    return $files;
21790877a1f1SSchplurtz le Déboulonné}
21800877a1f1SSchplurtz le Déboulonné
2181365be586SAndreas Gohr/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
2182