13df72098SAndreas Gohr<?php 23df72098SAndreas Gohr/** 33df72098SAndreas Gohr * All output and handler function needed for the media management popup 43df72098SAndreas Gohr * 53df72098SAndreas Gohr * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 63df72098SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org> 73df72098SAndreas Gohr */ 824870174SAndreas Gohruse dokuwiki\Ui\MediaRevisions; 924870174SAndreas Gohruse dokuwiki\Cache\CacheImageMod; 1024870174SAndreas Gohruse splitbrain\slika\Exception; 1124870174SAndreas Gohruse dokuwiki\PassHash; 120c3a5702SAndreas Gohruse dokuwiki\ChangeLog\MediaChangeLog; 13b021f0b4SAndreas Gohruse dokuwiki\Extension\Event; 14b021f0b4SAndreas Gohruse dokuwiki\Form\Form; 155a8d6e48SMichael Großeuse dokuwiki\HTTP\DokuHTTPClient; 1679a2d784SGerrit Uitslaguse dokuwiki\Logger; 17704a815fSMichael Großeuse dokuwiki\Subscriptions\MediaSubscriptionSender; 1879a2d784SGerrit Uitslaguse dokuwiki\Ui\Media\DisplayRow; 1979a2d784SGerrit Uitslaguse dokuwiki\Ui\Media\DisplayTile; 2079a2d784SGerrit Uitslaguse dokuwiki\Ui\MediaDiff; 2179a2d784SGerrit Uitslaguse dokuwiki\Utf8\PhpString; 222d85e841SAndreas Gohruse dokuwiki\Utf8\Sort; 2379a2d784SGerrit Uitslaguse splitbrain\slika\Slika; 240c3a5702SAndreas Gohr 253df72098SAndreas Gohr/** 263df72098SAndreas Gohr * Lists pages which currently use a media file selected for deletion 273df72098SAndreas Gohr * 283df72098SAndreas Gohr * References uses the same visual as search results and share 293df72098SAndreas Gohr * their CSS tags except pagenames won't be links. 303df72098SAndreas Gohr * 313df72098SAndreas Gohr * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net> 3242ea7f44SGerrit Uitslag * 3342ea7f44SGerrit Uitslag * @param array $data 3442ea7f44SGerrit Uitslag * @param string $id 353df72098SAndreas Gohr */ 36d868eb89SAndreas Gohrfunction media_filesinuse($data, $id) 37d868eb89SAndreas Gohr{ 383df72098SAndreas Gohr global $lang; 393df72098SAndreas Gohr echo '<h1>'.$lang['reference'].' <code>'.hsc(noNS($id)).'</code></h1>'; 403df72098SAndreas Gohr echo '<p>'.hsc($lang['ref_inuse']).'</p>'; 413df72098SAndreas Gohr 423df72098SAndreas Gohr $hidden=0; //count of hits without read permission 433df72098SAndreas Gohr foreach ($data as $row) { 44a05e297aSAndreas Gohr if (auth_quickaclcheck($row) >= AUTH_READ && isVisiblePage($row)) { 453df72098SAndreas Gohr echo '<div class="search_result">'; 46a05e297aSAndreas Gohr echo '<span class="mediaref_ref">'.hsc($row).'</span>'; 473df72098SAndreas Gohr echo '</div>'; 48177d6836SAndreas Gohr } else $hidden++; 493df72098SAndreas Gohr } 503df72098SAndreas Gohr if ($hidden) { 51*26dfc232SAndreas Gohr echo '<div class="mediaref_hidden">'.$lang['ref_hidden'].'</div>'; 523df72098SAndreas Gohr } 533df72098SAndreas Gohr} 543df72098SAndreas Gohr 553df72098SAndreas Gohr/** 563df72098SAndreas Gohr * Handles the saving of image meta data 573df72098SAndreas Gohr * 583df72098SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org> 59cf832786SKate Arzamastseva * @author Kate Arzamastseva <pshns@ukr.net> 60e0c26282SGerrit Uitslag * 61e0c26282SGerrit Uitslag * @param string $id media id 62e0c26282SGerrit Uitslag * @param int $auth permission level 63e0c26282SGerrit Uitslag * @param array $data 6442ea7f44SGerrit Uitslag * @return false|string 653df72098SAndreas Gohr */ 66d868eb89SAndreas Gohrfunction media_metasave($id, $auth, $data) 67d868eb89SAndreas Gohr{ 683df72098SAndreas Gohr if ($auth < AUTH_UPLOAD) return false; 69f2ea8432SAndreas Gohr if (!checkSecurityToken()) return false; 703df72098SAndreas Gohr global $lang; 710b308644SOtto Vainio global $conf; 723df72098SAndreas Gohr $src = mediaFN($id); 733df72098SAndreas Gohr 743df72098SAndreas Gohr $meta = new JpegMeta($src); 753df72098SAndreas Gohr $meta->_parseAll(); 763df72098SAndreas Gohr 773df72098SAndreas Gohr foreach ($data as $key => $val) { 783df72098SAndreas Gohr $val=trim($val); 793df72098SAndreas Gohr if (empty($val)) { 803df72098SAndreas Gohr $meta->deleteField($key); 813df72098SAndreas Gohr } else { 823df72098SAndreas Gohr $meta->setField($key, $val); 833df72098SAndreas Gohr } 843df72098SAndreas Gohr } 853df72098SAndreas Gohr 86cf832786SKate Arzamastseva $old = @filemtime($src); 8779e79377SAndreas Gohr if (!file_exists(mediaFN($id, $old)) && file_exists($src)) { 88cf832786SKate Arzamastseva // add old revision to the attic 89cf832786SKate Arzamastseva media_saveOldRevision($id); 90cf832786SKate Arzamastseva } 91ac3ed4afSGerrit Uitslag $filesize_old = filesize($src); 923df72098SAndreas Gohr if ($meta->save()) { 930b308644SOtto Vainio if ($conf['fperm']) chmod($src, $conf['fperm']); 94ac3ed4afSGerrit Uitslag @clearstatcache(true, $src); 95cf832786SKate Arzamastseva $new = @filemtime($src); 96ac3ed4afSGerrit Uitslag $filesize_new = filesize($src); 97ac3ed4afSGerrit Uitslag $sizechange = $filesize_new - $filesize_old; 98ac3ed4afSGerrit Uitslag 99cf832786SKate Arzamastseva // add a log entry to the media changelog 100ac3ed4afSGerrit Uitslag addMediaLogEntry($new, $id, DOKU_CHANGE_TYPE_EDIT, $lang['media_meta_edited'], '', null, $sizechange); 101cf832786SKate Arzamastseva 1023df72098SAndreas Gohr msg($lang['metasaveok'], 1); 1033df72098SAndreas Gohr return $id; 1043df72098SAndreas Gohr } else { 1053df72098SAndreas Gohr msg($lang['metasaveerr'], -1); 1063df72098SAndreas Gohr return false; 1073df72098SAndreas Gohr } 1083df72098SAndreas Gohr} 1093df72098SAndreas Gohr 1103df72098SAndreas Gohr/** 111d54f7963SKlap-in * check if a media is external source 112d54f7963SKlap-in * 113d54f7963SKlap-in * @author Gerrit Uitslag <klapinklapin@gmail.com> 11442ea7f44SGerrit Uitslag * 115d54f7963SKlap-in * @param string $id the media ID or URL 116d54f7963SKlap-in * @return bool 117d54f7963SKlap-in */ 118d868eb89SAndreas Gohrfunction media_isexternal($id) 119d868eb89SAndreas Gohr{ 120fe578fe9SElan Ruusamäe if (preg_match('#^(?:https?|ftp)://#i', $id)) return true; 121d54f7963SKlap-in return false; 122d54f7963SKlap-in} 123d54f7963SKlap-in 124d54f7963SKlap-in/** 125add8678fSAndreas Gohr * Check if a media item is public (eg, external URL or readable by @ALL) 126add8678fSAndreas Gohr * 127add8678fSAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org> 12842ea7f44SGerrit Uitslag * 129add8678fSAndreas Gohr * @param string $id the media ID or URL 130add8678fSAndreas Gohr * @return bool 131add8678fSAndreas Gohr */ 132d868eb89SAndreas Gohrfunction media_ispublic($id) 133d868eb89SAndreas Gohr{ 134d54f7963SKlap-in if (media_isexternal($id)) return true; 135add8678fSAndreas Gohr $id = cleanID($id); 13624870174SAndreas Gohr if (auth_aclcheck(getNS($id).':*', '', []) >= AUTH_READ) return true; 137add8678fSAndreas Gohr return false; 138add8678fSAndreas Gohr} 139add8678fSAndreas Gohr 140add8678fSAndreas Gohr/** 1413df72098SAndreas Gohr * Display the form to edit image meta data 1423df72098SAndreas Gohr * 1433df72098SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org> 144d9162c6cSKate Arzamastseva * @author Kate Arzamastseva <pshns@ukr.net> 145e0c26282SGerrit Uitslag * 146e0c26282SGerrit Uitslag * @param string $id media id 147e0c26282SGerrit Uitslag * @param int $auth permission level 148e0c26282SGerrit Uitslag * @return bool 1493df72098SAndreas Gohr */ 150d868eb89SAndreas Gohrfunction media_metaform($id, $auth) 151d868eb89SAndreas Gohr{ 152ebc28e69SAndreas Gohr global $lang; 1533df72098SAndreas Gohr 15488a71175SKate Arzamastseva if ($auth < AUTH_UPLOAD) { 155b960c74fSSatoshi Sahara echo '<div class="nothing">'.$lang['media_perm_upload'].'</div>'.DOKU_LF; 15688a71175SKate Arzamastseva return false; 15788a71175SKate Arzamastseva } 15888a71175SKate Arzamastseva 1593df72098SAndreas Gohr // load the field descriptions 1603df72098SAndreas Gohr static $fields = null; 161b960c74fSSatoshi Sahara if ($fields === null) { 1623e98e685SKate Arzamastseva $config_files = getConfigFiles('mediameta'); 1633e98e685SKate Arzamastseva foreach ($config_files as $config_file) { 16479e79377SAndreas Gohr if (file_exists($config_file)) include($config_file); 1653df72098SAndreas Gohr } 1663df72098SAndreas Gohr } 1673df72098SAndreas Gohr 1683df72098SAndreas Gohr $src = mediaFN($id); 1693df72098SAndreas Gohr 1703df72098SAndreas Gohr // output 171b960c74fSSatoshi Sahara $form = new Form([ 172b960c74fSSatoshi Sahara 'action' => media_managerURL(['tab_details' => 'view'], '&'), 173b960c74fSSatoshi Sahara 'class' => 'meta' 174b960c74fSSatoshi Sahara ]); 175b960c74fSSatoshi Sahara $form->addTagOpen('div')->addClass('no'); 176b960c74fSSatoshi Sahara $form->setHiddenField('img', $id); 177b960c74fSSatoshi Sahara $form->setHiddenField('mediado', 'save'); 1783df72098SAndreas Gohr foreach ($fields as $key => $field) { 1793df72098SAndreas Gohr // get current value 1803e98e685SKate Arzamastseva if (empty($field[0])) continue; 18124870174SAndreas Gohr $tags = [$field[0]]; 1823df72098SAndreas Gohr if (is_array($field[3])) $tags = array_merge($tags, $field[3]); 1833df72098SAndreas Gohr $value = tpl_img_getTag($tags, '', $src); 184ca6a0701SAndreas Gohr $value = cleanText($value); 1853df72098SAndreas Gohr 1863df72098SAndreas Gohr // prepare attributes 18724870174SAndreas Gohr $p = [ 188b960c74fSSatoshi Sahara 'class' => 'edit', 189b960c74fSSatoshi Sahara 'id' => 'meta__'.$key, 19024870174SAndreas Gohr 'name' => 'meta['.$field[0].']' 19124870174SAndreas Gohr ]; 1923df72098SAndreas Gohr 193b960c74fSSatoshi Sahara $form->addTagOpen('div')->addClass('row'); 1943df72098SAndreas Gohr if ($field[2] == 'text') { 195bde2a644SSatoshi Sahara $form->addTextInput( 19664159a61SAndreas Gohr $p['name'], 19779a2d784SGerrit Uitslag ($lang[$field[1]] ?: $field[1] . ':') 198b960c74fSSatoshi Sahara )->id($p['id'])->addClass($p['class'])->val($value); 1993df72098SAndreas Gohr } else { 200b960c74fSSatoshi Sahara $form->addTextarea($p['name'], $lang[$field[1]])->id($p['id']) 201b960c74fSSatoshi Sahara ->val(formText($value)) 202b960c74fSSatoshi Sahara ->addClass($p['class']) 203b960c74fSSatoshi Sahara ->attr('rows', '6')->attr('cols', '50'); 2043df72098SAndreas Gohr } 205b960c74fSSatoshi Sahara $form->addTagClose('div'); 2063df72098SAndreas Gohr } 207b960c74fSSatoshi Sahara $form->addTagOpen('div')->addClass('buttons'); 208b960c74fSSatoshi Sahara $form->addButton('mediado[save]', $lang['btn_save'])->attr('type', 'submit') 209b960c74fSSatoshi Sahara ->attrs(['accesskey' => 's']); 210b960c74fSSatoshi Sahara $form->addTagClose('div'); 211ebc28e69SAndreas Gohr 212b960c74fSSatoshi Sahara $form->addTagClose('div'); 213b960c74fSSatoshi Sahara echo $form->toHTML(); 214ebc28e69SAndreas Gohr return true; 2153df72098SAndreas Gohr} 2163df72098SAndreas Gohr 217666cdec5SMichael Klier/** 21887229c84SAdrian Lang * Convenience function to check if a media file is still in use 219666cdec5SMichael Klier * 220666cdec5SMichael Klier * @author Michael Klier <chi@chimeric.de> 221e0c26282SGerrit Uitslag * 222e0c26282SGerrit Uitslag * @param string $id media id 223e0c26282SGerrit Uitslag * @return array|bool 224666cdec5SMichael Klier */ 225d868eb89SAndreas Gohrfunction media_inuse($id) 226d868eb89SAndreas Gohr{ 227666cdec5SMichael Klier global $conf; 228ebc28e69SAndreas Gohr 229666cdec5SMichael Klier if ($conf['refcheck']) { 230ffec1009SMichael Hamann $mediareferences = ft_mediause($id, true); 23124870174SAndreas Gohr if ($mediareferences === []) { 2326dae2464SAndreas Gohr return false; 233666cdec5SMichael Klier } else { 234666cdec5SMichael Klier return $mediareferences; 235666cdec5SMichael Klier } 236666cdec5SMichael Klier } else { 237666cdec5SMichael Klier return false; 238666cdec5SMichael Klier } 239666cdec5SMichael Klier} 240a05e297aSAndreas Gohr 2413df72098SAndreas Gohr/** 2423df72098SAndreas Gohr * Handles media file deletions 2433df72098SAndreas Gohr * 2443df72098SAndreas Gohr * If configured, checks for media references before deletion 2453df72098SAndreas Gohr * 2463df72098SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org> 24742ea7f44SGerrit Uitslag * 248ebc28e69SAndreas Gohr * @param string $id media id 24922db8df7SAndreas Gohr * @param int $auth no longer used 25087229c84SAdrian Lang * @return int One of: 0, 25163703ba5SAndreas Gohr * DOKU_MEDIA_DELETED, 25263703ba5SAndreas Gohr * DOKU_MEDIA_DELETED | DOKU_MEDIA_EMPTY_NS, 25363703ba5SAndreas Gohr * DOKU_MEDIA_NOT_AUTH, 25463703ba5SAndreas Gohr * DOKU_MEDIA_INUSE 2553df72098SAndreas Gohr */ 256d868eb89SAndreas Gohrfunction media_delete($id, $auth) 257d868eb89SAndreas Gohr{ 2586183fb05SKate Arzamastseva global $lang; 259ff04e8b7SAndreas Gohr $auth = auth_quickaclcheck(ltrim(getNS($id).':*', ':')); 26087229c84SAdrian Lang if ($auth < AUTH_DELETE) return DOKU_MEDIA_NOT_AUTH; 26187229c84SAdrian Lang if (media_inuse($id)) return DOKU_MEDIA_INUSE; 2623df72098SAndreas Gohr 2633df72098SAndreas Gohr $file = mediaFN($id); 2644a961e72SMichal Kolodziejski 2654a961e72SMichal Kolodziejski // trigger an event - MEDIA_DELETE_FILE 26624870174SAndreas Gohr $data = []; 267666cdec5SMichael Klier $data['id'] = $id; 26879a2d784SGerrit Uitslag $data['name'] = PhpString::basename($file); 2694a961e72SMichal Kolodziejski $data['path'] = $file; 27079e79377SAndreas Gohr $data['size'] = (file_exists($file)) ? filesize($file) : 0; 271666cdec5SMichael Klier 272666cdec5SMichael Klier $data['unl'] = false; 273666cdec5SMichael Klier $data['del'] = false; 274e1d9dcc8SAndreas Gohr $evt = new Event('MEDIA_DELETE_FILE', $data); 2754a961e72SMichal Kolodziejski if ($evt->advise_before()) { 2766183fb05SKate Arzamastseva $old = @filemtime($file); 27779e79377SAndreas Gohr if (!file_exists(mediaFN($id, $old)) && file_exists($file)) { 2786183fb05SKate Arzamastseva // add old revision to the attic 2796183fb05SKate Arzamastseva media_saveOldRevision($id); 2806183fb05SKate Arzamastseva } 2816183fb05SKate Arzamastseva 282666cdec5SMichael Klier $data['unl'] = @unlink($file); 283666cdec5SMichael Klier if ($data['unl']) { 284ac3ed4afSGerrit Uitslag $sizechange = 0 - $data['size']; 285ac3ed4afSGerrit Uitslag addMediaLogEntry(time(), $id, DOKU_CHANGE_TYPE_DELETE, $lang['deleted'], '', null, $sizechange); 286ac3ed4afSGerrit Uitslag 287666cdec5SMichael Klier $data['del'] = io_sweepNS($id, 'mediadir'); 2883df72098SAndreas Gohr } 2894a961e72SMichal Kolodziejski } 2904a961e72SMichal Kolodziejski $evt->advise_after(); 2914a961e72SMichal Kolodziejski unset($evt); 2924a961e72SMichal Kolodziejski 293666cdec5SMichael Klier if ($data['unl'] && $data['del']) { 29487229c84SAdrian Lang return DOKU_MEDIA_DELETED | DOKU_MEDIA_EMPTY_NS; 2953df72098SAndreas Gohr } 2963df72098SAndreas Gohr 29787229c84SAdrian Lang return $data['unl'] ? DOKU_MEDIA_DELETED : 0; 2983df72098SAndreas Gohr} 2993df72098SAndreas Gohr 3003df72098SAndreas Gohr/** 3012d6cc64fSKate Arzamastseva * Handle file uploads via XMLHttpRequest 3022d6cc64fSKate Arzamastseva * 303ebc28e69SAndreas Gohr * @param string $ns target namespace 304ebc28e69SAndreas Gohr * @param int $auth current auth check result 30542ea7f44SGerrit Uitslag * @return false|string false on error, id of the new file on success 3062d6cc64fSKate Arzamastseva */ 307d868eb89SAndreas Gohrfunction media_upload_xhr($ns, $auth) 308d868eb89SAndreas Gohr{ 309da45d883SKate Arzamastseva if (!checkSecurityToken()) return false; 3108108113cSTom N Harris global $INPUT; 311da45d883SKate Arzamastseva 3128108113cSTom N Harris $id = $INPUT->get->str('qqfile'); 31324870174SAndreas Gohr [$ext, $mime] = mimetype($id); 3142d6cc64fSKate Arzamastseva $input = fopen("php://input", "r"); 3152d6cc64fSKate Arzamastseva if (!($tmp = io_mktmpdir())) return false; 31662231793SKate Arzamastseva $path = $tmp.'/'.md5($id); 3172d6cc64fSKate Arzamastseva $target = fopen($path, "w"); 318063fb5b5SAndreas Gohr $realSize = stream_copy_to_stream($input, $target); 3192d6cc64fSKate Arzamastseva fclose($target); 320063fb5b5SAndreas Gohr fclose($input); 3212b9be456SAndreas Gohr if ($INPUT->server->has('CONTENT_LENGTH') && ($realSize != $INPUT->server->int('CONTENT_LENGTH'))) { 322063fb5b5SAndreas Gohr unlink($path); 323063fb5b5SAndreas Gohr return false; 324063fb5b5SAndreas Gohr } 325063fb5b5SAndreas Gohr 3262d6cc64fSKate Arzamastseva $res = media_save( 32724870174SAndreas Gohr ['name' => $path, 'mime' => $mime, 'ext' => $ext], 3282d6cc64fSKate Arzamastseva $ns.':'.$id, 32979a2d784SGerrit Uitslag ($INPUT->get->str('ow') == 'true'), 3302d6cc64fSKate Arzamastseva $auth, 3312d6cc64fSKate Arzamastseva 'copy' 3322d6cc64fSKate Arzamastseva ); 3332d6cc64fSKate Arzamastseva unlink($path); 334900a9e9eSGerrit Uitslag if ($tmp) io_rmdir($tmp, true); 3352d6cc64fSKate Arzamastseva if (is_array($res)) { 3362d6cc64fSKate Arzamastseva msg($res[0], $res[1]); 3372d6cc64fSKate Arzamastseva return false; 3382d6cc64fSKate Arzamastseva } 3392d6cc64fSKate Arzamastseva return $res; 3402d6cc64fSKate Arzamastseva} 3412d6cc64fSKate Arzamastseva 3422d6cc64fSKate Arzamastseva/** 3433df72098SAndreas Gohr * Handles media file uploads 3443df72098SAndreas Gohr * 3453df72098SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org> 34611d9dfa5SMichael Klier * @author Michael Klier <chi@chimeric.de> 34742ea7f44SGerrit Uitslag * 348ebc28e69SAndreas Gohr * @param string $ns target namespace 349ebc28e69SAndreas Gohr * @param int $auth current auth check result 350ebc28e69SAndreas Gohr * @param bool|array $file $_FILES member, $_FILES['upload'] if false 35142ea7f44SGerrit Uitslag * @return false|string false on error, id of the new file on success 3523df72098SAndreas Gohr */ 353d868eb89SAndreas Gohrfunction media_upload($ns, $auth, $file = false) 354d868eb89SAndreas Gohr{ 355f2ea8432SAndreas Gohr if (!checkSecurityToken()) return false; 3563df72098SAndreas Gohr global $lang; 3578108113cSTom N Harris global $INPUT; 3583df72098SAndreas Gohr 35944409c3dSAndreas Gohr // get file and id 3608108113cSTom N Harris $id = $INPUT->post->str('mediaid'); 3612d6cc64fSKate Arzamastseva if (!$file) $file = $_FILES['upload']; 3623df72098SAndreas Gohr if (empty($id)) $id = $file['name']; 36344409c3dSAndreas Gohr 36499766eefSAndreas Gohr // check for errors (messages are done in lib/exe/mediamanager.php) 36599766eefSAndreas Gohr if ($file['error']) return false; 3669676dc23SAndreas Gohr 36744409c3dSAndreas Gohr // check extensions 36824870174SAndreas Gohr [$fext, $fmime] = mimetype($file['name']); 36924870174SAndreas Gohr [$iext, $imime] = mimetype($id); 37044409c3dSAndreas Gohr if ($fext && !$iext) { 3718cb1eb01SAndreas Gohr // no extension specified in id - read original one 37244409c3dSAndreas Gohr $id .= '.'.$fext; 3738cb1eb01SAndreas Gohr $imime = $fmime; 37444409c3dSAndreas Gohr } elseif ($fext && $fext != $iext) { 37544409c3dSAndreas Gohr // extension was changed, print warning 37644409c3dSAndreas Gohr msg(sprintf($lang['mediaextchange'], $fext, $iext)); 37744409c3dSAndreas Gohr } 37844409c3dSAndreas Gohr 37924870174SAndreas Gohr $res = media_save( 38024870174SAndreas Gohr [ 38124870174SAndreas Gohr 'name' => $file['tmp_name'], 382ffb291f2SAdrian Lang 'mime' => $imime, 38324870174SAndreas Gohr 'ext' => $iext 38424870174SAndreas Gohr ], 38524870174SAndreas Gohr $ns . ':' . $id, 38624870174SAndreas Gohr $INPUT->post->bool('ow'), 38724870174SAndreas Gohr $auth, 38824870174SAndreas Gohr 'copy_uploaded_file' 38924870174SAndreas Gohr ); 390ffb291f2SAdrian Lang if (is_array($res)) { 391ffb291f2SAdrian Lang msg($res[0], $res[1]); 392ffb291f2SAdrian Lang return false; 393ffb291f2SAdrian Lang } 394ffb291f2SAdrian Lang return $res; 395ffb291f2SAdrian Lang} 396ffb291f2SAdrian Lang 397ffb291f2SAdrian Lang/** 39871f17ac4SAndreas Gohr * An alternative to move_uploaded_file that copies 39971f17ac4SAndreas Gohr * 40071f17ac4SAndreas Gohr * Using copy, makes sure any setgid bits on the media directory are honored 40171f17ac4SAndreas Gohr * 40271f17ac4SAndreas Gohr * @see move_uploaded_file() 40342ea7f44SGerrit Uitslag * 40471f17ac4SAndreas Gohr * @param string $from 40571f17ac4SAndreas Gohr * @param string $to 40671f17ac4SAndreas Gohr * @return bool 40771f17ac4SAndreas Gohr */ 408d868eb89SAndreas Gohrfunction copy_uploaded_file($from, $to) 409d868eb89SAndreas Gohr{ 41071f17ac4SAndreas Gohr if (!is_uploaded_file($from)) return false; 41171f17ac4SAndreas Gohr $ok = copy($from, $to); 41271f17ac4SAndreas Gohr @unlink($from); 41371f17ac4SAndreas Gohr return $ok; 41471f17ac4SAndreas Gohr} 41571f17ac4SAndreas Gohr 41671f17ac4SAndreas Gohr/** 417ffb291f2SAdrian Lang * This generates an action event and delegates to _media_upload_action(). 418ffb291f2SAdrian Lang * Action plugins are allowed to pre/postprocess the uploaded file. 419ffb291f2SAdrian Lang * (The triggered event is preventable.) 420ffb291f2SAdrian Lang * 421ffb291f2SAdrian Lang * Event data: 422ffb291f2SAdrian Lang * $data[0] fn_tmp: the temporary file name (read from $_FILES) 423ffb291f2SAdrian Lang * $data[1] fn: the file name of the uploaded file 424ffb291f2SAdrian Lang * $data[2] id: the future directory id of the uploaded file 425ffb291f2SAdrian Lang * $data[3] imime: the mimetype of the uploaded file 426ffb291f2SAdrian Lang * $data[4] overwrite: if an existing file is going to be overwritten 42752a281e8SGerrit Uitslag * $data[5] move: name of function that performs move/copy/.. 428ffb291f2SAdrian Lang * 429ffb291f2SAdrian Lang * @triggers MEDIA_UPLOAD_FINISH 43042ea7f44SGerrit Uitslag * 431e0c26282SGerrit Uitslag * @param array $file 43252a281e8SGerrit Uitslag * @param string $id media id 43342ea7f44SGerrit Uitslag * @param bool $ow overwrite? 434e0c26282SGerrit Uitslag * @param int $auth permission level 43552a281e8SGerrit Uitslag * @param string $move name of functions that performs move/copy/.. 43642ea7f44SGerrit Uitslag * @return false|array|string 437e0c26282SGerrit Uitslag */ 438d868eb89SAndreas Gohrfunction media_save($file, $id, $ow, $auth, $move) 439d868eb89SAndreas Gohr{ 440ffb291f2SAdrian Lang if ($auth < AUTH_UPLOAD) { 44124870174SAndreas Gohr return ["You don't have permissions to upload files.", -1]; 442ffb291f2SAdrian Lang } 443ffb291f2SAdrian Lang 444ffb291f2SAdrian Lang if (!isset($file['mime']) || !isset($file['ext'])) { 44524870174SAndreas Gohr [$ext, $mime] = mimetype($id); 446ffb291f2SAdrian Lang if (!isset($file['mime'])) { 447ffb291f2SAdrian Lang $file['mime'] = $mime; 448ffb291f2SAdrian Lang } 449ffb291f2SAdrian Lang if (!isset($file['ext'])) { 450ffb291f2SAdrian Lang $file['ext'] = $ext; 451ffb291f2SAdrian Lang } 452ffb291f2SAdrian Lang } 453ffb291f2SAdrian Lang 45492cac9a9SKate Arzamastseva global $lang, $conf; 455ffb291f2SAdrian Lang 4563df72098SAndreas Gohr // get filename 4573543c6deSAndreas Gohr $id = cleanID($id); 4583df72098SAndreas Gohr $fn = mediaFN($id); 4593df72098SAndreas Gohr 4603df72098SAndreas Gohr // get filetype regexp 4613df72098SAndreas Gohr $types = array_keys(getMimeTypes()); 462bad6fc0dSAndreas Gohr $types = array_map( 46324870174SAndreas Gohr static fn($q) => preg_quote($q, "/"), 464bad6fc0dSAndreas Gohr $types 465bad6fc0dSAndreas Gohr ); 46624870174SAndreas Gohr $regex = implode('|', $types); 4673df72098SAndreas Gohr 4683df72098SAndreas Gohr // because a temp file was created already 469ffb291f2SAdrian Lang if (!preg_match('/\.('.$regex.')$/i', $fn)) { 47024870174SAndreas Gohr return [$lang['uploadwrong'], -1]; 471ffb291f2SAdrian Lang } 472ffb291f2SAdrian Lang 4733df72098SAndreas Gohr //check for overwrite 47479e79377SAndreas Gohr $overwrite = file_exists($fn); 475e5d185e1SKate Arzamastseva $auth_ow = (($conf['mediarevisions']) ? AUTH_UPLOAD : AUTH_DELETE); 476e5d185e1SKate Arzamastseva if ($overwrite && (!$ow || $auth < $auth_ow)) { 47724870174SAndreas Gohr return [$lang['uploadexist'], 0]; 4783df72098SAndreas Gohr } 4798cb1eb01SAndreas Gohr // check for valid content 480ffb291f2SAdrian Lang $ok = media_contentcheck($file['name'], $file['mime']); 4818cb1eb01SAndreas Gohr if ($ok == -1) { 48224870174SAndreas Gohr return [sprintf($lang['uploadbadcontent'], '.' . $file['ext']), -1]; 4838cb1eb01SAndreas Gohr } elseif ($ok == -2) { 48424870174SAndreas Gohr return [$lang['uploadspam'], -1]; 48526ceae18SAndreas Gohr } elseif ($ok == -3) { 48624870174SAndreas Gohr return [$lang['uploadxss'], -1]; 4878cb1eb01SAndreas Gohr } 4888cb1eb01SAndreas Gohr 48911d9dfa5SMichael Klier // prepare event data 49024870174SAndreas Gohr $data = []; 491ffb291f2SAdrian Lang $data[0] = $file['name']; 49211d9dfa5SMichael Klier $data[1] = $fn; 49311d9dfa5SMichael Klier $data[2] = $id; 494ffb291f2SAdrian Lang $data[3] = $file['mime']; 49599c8d7f2Smichael $data[4] = $overwrite; 496ffb291f2SAdrian Lang $data[5] = $move; 49711d9dfa5SMichael Klier 49811d9dfa5SMichael Klier // trigger event 499cbb44eabSAndreas Gohr return Event::createAndTrigger('MEDIA_UPLOAD_FINISH', $data, '_media_upload_action', true); 50011d9dfa5SMichael Klier} 50111d9dfa5SMichael Klier 50211d9dfa5SMichael Klier/** 50342ea7f44SGerrit Uitslag * Callback adapter for media_upload_finish() triggered by MEDIA_UPLOAD_FINISH 50442ea7f44SGerrit Uitslag * 50511d9dfa5SMichael Klier * @author Michael Klier <chi@chimeric.de> 50642ea7f44SGerrit Uitslag * 50742ea7f44SGerrit Uitslag * @param array $data event data 50842ea7f44SGerrit Uitslag * @return false|array|string 50911d9dfa5SMichael Klier */ 510d868eb89SAndreas Gohrfunction _media_upload_action($data) 511d868eb89SAndreas Gohr{ 51211d9dfa5SMichael Klier // fixme do further sanity tests of given data? 513ffb291f2SAdrian Lang if (is_array($data) && count($data)===6) { 514ffb291f2SAdrian Lang return media_upload_finish($data[0], $data[1], $data[2], $data[3], $data[4], $data[5]); 51511d9dfa5SMichael Klier } else { 51611d9dfa5SMichael Klier return false; //callback error 51711d9dfa5SMichael Klier } 51811d9dfa5SMichael Klier} 51911d9dfa5SMichael Klier 52011d9dfa5SMichael Klier/** 52111d9dfa5SMichael Klier * Saves an uploaded media file 52211d9dfa5SMichael Klier * 52311d9dfa5SMichael Klier * @author Andreas Gohr <andi@splitbrain.org> 52411d9dfa5SMichael Klier * @author Michael Klier <chi@chimeric.de> 525cbe26ad6SKate Arzamastseva * @author Kate Arzamastseva <pshns@ukr.net> 52642ea7f44SGerrit Uitslag * 52742ea7f44SGerrit Uitslag * @param string $fn_tmp 52842ea7f44SGerrit Uitslag * @param string $fn 52942ea7f44SGerrit Uitslag * @param string $id media id 53042ea7f44SGerrit Uitslag * @param string $imime mime type 53142ea7f44SGerrit Uitslag * @param bool $overwrite overwrite existing? 53242ea7f44SGerrit Uitslag * @param string $move function name 53342ea7f44SGerrit Uitslag * @return array|string 53411d9dfa5SMichael Klier */ 535d868eb89SAndreas Gohrfunction media_upload_finish($fn_tmp, $fn, $id, $imime, $overwrite, $move = 'move_uploaded_file') 536d868eb89SAndreas Gohr{ 53711d9dfa5SMichael Klier global $conf; 53811d9dfa5SMichael Klier global $lang; 5399c1bd4bcSKate Arzamastseva global $REV; 54011d9dfa5SMichael Klier 541e4f389efSKate Arzamastseva $old = @filemtime($fn); 54279e79377SAndreas Gohr if (!file_exists(mediaFN($id, $old)) && file_exists($fn)) { 543e4f389efSKate Arzamastseva // add old revision to the attic if missing 544cbe26ad6SKate Arzamastseva media_saveOldRevision($id); 545e4f389efSKate Arzamastseva } 546e4f389efSKate Arzamastseva 5473df72098SAndreas Gohr // prepare directory 548cc7d0c94SBen Coburn io_createNamespace($id, 'media'); 54911d9dfa5SMichael Klier 550ac3ed4afSGerrit Uitslag $filesize_old = file_exists($fn) ? filesize($fn) : 0; 551ac3ed4afSGerrit Uitslag 552ffb291f2SAdrian Lang if ($move($fn_tmp, $fn)) { 55323846a98SKate Arzamastseva @clearstatcache(true, $fn); 554dad6764eSKate Arzamastseva $new = @filemtime($fn); 55574400ea5SBen Coburn // Set the correct permission here. 55674400ea5SBen Coburn // Always chmod media because they may be saved with different permissions than expected from the php umask. 55774400ea5SBen Coburn // (Should normally chmod to $conf['fperm'] only if $conf['fperm'] is set.) 55874400ea5SBen Coburn chmod($fn, $conf['fmode']); 5593df72098SAndreas Gohr msg($lang['uploadsucc'], 1); 56083734cddSPhy media_notify($id, $fn, $imime, $old, $new); 56199c8d7f2Smichael // add a log entry to the media changelog 562ac3ed4afSGerrit Uitslag $filesize_new = filesize($fn); 563ac3ed4afSGerrit Uitslag $sizechange = $filesize_new - $filesize_old; 5649c1bd4bcSKate Arzamastseva if ($REV) { 56564159a61SAndreas Gohr addMediaLogEntry( 56664159a61SAndreas Gohr $new, 56764159a61SAndreas Gohr $id, 56864159a61SAndreas Gohr DOKU_CHANGE_TYPE_REVERT, 56964159a61SAndreas Gohr sprintf($lang['restored'], dformat($REV)), 57064159a61SAndreas Gohr $REV, 57164159a61SAndreas Gohr null, 57264159a61SAndreas Gohr $sizechange 57364159a61SAndreas Gohr ); 5749c1bd4bcSKate Arzamastseva } elseif ($overwrite) { 575ac3ed4afSGerrit Uitslag addMediaLogEntry($new, $id, DOKU_CHANGE_TYPE_EDIT, '', '', null, $sizechange); 57699c8d7f2Smichael } else { 577ac3ed4afSGerrit Uitslag addMediaLogEntry($new, $id, DOKU_CHANGE_TYPE_CREATE, $lang['created'], '', null, $sizechange); 57899c8d7f2Smichael } 5793df72098SAndreas Gohr return $id; 5803df72098SAndreas Gohr } else { 58124870174SAndreas Gohr return [$lang['uploadfail'], -1]; 5823df72098SAndreas Gohr } 5833df72098SAndreas Gohr} 5843df72098SAndreas Gohr 5858cb1eb01SAndreas Gohr/** 586cbe26ad6SKate Arzamastseva * Moves the current version of media file to the media_attic 587cbe26ad6SKate Arzamastseva * directory 588cbe26ad6SKate Arzamastseva * 589cbe26ad6SKate Arzamastseva * @author Kate Arzamastseva <pshns@ukr.net> 59042ea7f44SGerrit Uitslag * 591cbe26ad6SKate Arzamastseva * @param string $id 592cbe26ad6SKate Arzamastseva * @return int - revision date 593e4f389efSKate Arzamastseva */ 594d868eb89SAndreas Gohrfunction media_saveOldRevision($id) 595d868eb89SAndreas Gohr{ 596dbf57c96SKate Arzamastseva global $conf, $lang; 597dbf57c96SKate Arzamastseva 598e4f389efSKate Arzamastseva $oldf = mediaFN($id); 59979e79377SAndreas Gohr if (!file_exists($oldf)) return ''; 600e4f389efSKate Arzamastseva $date = filemtime($oldf); 601e5d185e1SKate Arzamastseva if (!$conf['mediarevisions']) return $date; 602e5d185e1SKate Arzamastseva 603047bad06SGerrit Uitslag $medialog = new MediaChangeLog($id); 604047bad06SGerrit Uitslag if (!$medialog->getRevisionInfo($date)) { 605dbf57c96SKate Arzamastseva // there was an external edit, 606dbf57c96SKate Arzamastseva // there is no log entry for current version of file 607ac3ed4afSGerrit Uitslag $sizechange = filesize($oldf); 60879e79377SAndreas Gohr if (!file_exists(mediaMetaFN($id, '.changes'))) { 609ac3ed4afSGerrit Uitslag addMediaLogEntry($date, $id, DOKU_CHANGE_TYPE_CREATE, $lang['created'], '', null, $sizechange); 610dbf57c96SKate Arzamastseva } else { 611ac3ed4afSGerrit Uitslag $oldRev = $medialog->getRevisions(-1, 1); // from changelog 612ac3ed4afSGerrit Uitslag $oldRev = (int) (empty($oldRev) ? 0 : $oldRev[0]); 613ac3ed4afSGerrit Uitslag $filesize_old = filesize(mediaFN($id, $oldRev)); 61424870174SAndreas Gohr $sizechange -= $filesize_old; 615ac3ed4afSGerrit Uitslag 616ac3ed4afSGerrit Uitslag addMediaLogEntry($date, $id, DOKU_CHANGE_TYPE_EDIT, '', '', null, $sizechange); 617dbf57c96SKate Arzamastseva } 618dbf57c96SKate Arzamastseva } 619dbf57c96SKate Arzamastseva 620e4f389efSKate Arzamastseva $newf = mediaFN($id, $date); 621e4f389efSKate Arzamastseva io_makeFileDir($newf); 622cbe26ad6SKate Arzamastseva if (copy($oldf, $newf)) { 623e4f389efSKate Arzamastseva // Set the correct permission here. 624e4f389efSKate Arzamastseva // Always chmod media because they may be saved with different permissions than expected from the php umask. 625e4f389efSKate Arzamastseva // (Should normally chmod to $conf['fperm'] only if $conf['fperm'] is set.) 626e4f389efSKate Arzamastseva chmod($newf, $conf['fmode']); 627e4f389efSKate Arzamastseva } 628e4f389efSKate Arzamastseva return $date; 629e4f389efSKate Arzamastseva} 630e4f389efSKate Arzamastseva 631e4f389efSKate Arzamastseva/** 6328cb1eb01SAndreas Gohr * This function checks if the uploaded content is really what the 63326ceae18SAndreas Gohr * mimetype says it is. We also do spam checking for text types here. 6348cb1eb01SAndreas Gohr * 6358cb1eb01SAndreas Gohr * We need to do this stuff because we can not rely on the browser 6368cb1eb01SAndreas Gohr * to do this check correctly. Yes, IE is broken as usual. 6378cb1eb01SAndreas Gohr * 6388cb1eb01SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org> 63926ceae18SAndreas Gohr * @link http://www.splitbrain.org/blog/2007-02/12-internet_explorer_facilitates_cross_site_scripting 6408cb1eb01SAndreas Gohr * @fixme check all 26 magic IE filetypes here? 64142ea7f44SGerrit Uitslag * 64242ea7f44SGerrit Uitslag * @param string $file path to file 64342ea7f44SGerrit Uitslag * @param string $mime mimetype 64442ea7f44SGerrit Uitslag * @return int 6458cb1eb01SAndreas Gohr */ 646d868eb89SAndreas Gohrfunction media_contentcheck($file, $mime) 647d868eb89SAndreas Gohr{ 64826ceae18SAndreas Gohr global $conf; 64926ceae18SAndreas Gohr if ($conf['iexssprotect']) { 65026ceae18SAndreas Gohr $fh = @fopen($file, 'rb'); 65126ceae18SAndreas Gohr if ($fh) { 65226ceae18SAndreas Gohr $bytes = fread($fh, 256); 65326ceae18SAndreas Gohr fclose($fh); 65426ceae18SAndreas Gohr if (preg_match('/<(script|a|img|html|body|iframe)[\s>]/i', $bytes)) { 65542ea7f44SGerrit Uitslag return -3; //XSS: possibly malicious content 65626ceae18SAndreas Gohr } 65726ceae18SAndreas Gohr } 65826ceae18SAndreas Gohr } 6598cb1eb01SAndreas Gohr if (substr($mime, 0, 6) == 'image/') { 6608cb1eb01SAndreas Gohr $info = @getimagesize($file); 6618cb1eb01SAndreas Gohr if ($mime == 'image/gif' && $info[2] != 1) { 66242ea7f44SGerrit Uitslag return -1; // uploaded content did not match the file extension 6638cb1eb01SAndreas Gohr } elseif ($mime == 'image/jpeg' && $info[2] != 2) { 6648cb1eb01SAndreas Gohr return -1; 6658cb1eb01SAndreas Gohr } elseif ($mime == 'image/png' && $info[2] != 3) { 6668cb1eb01SAndreas Gohr return -1; 6678cb1eb01SAndreas Gohr } 6688cb1eb01SAndreas Gohr # fixme maybe check other images types as well 6698cb1eb01SAndreas Gohr } elseif (substr($mime, 0, 5) == 'text/') { 6708cb1eb01SAndreas Gohr global $TEXT; 6718cb1eb01SAndreas Gohr $TEXT = io_readFile($file); 6728cb1eb01SAndreas Gohr if (checkwordblock()) { 67342ea7f44SGerrit Uitslag return -2; //blocked by the spam blacklist 6748cb1eb01SAndreas Gohr } 6758cb1eb01SAndreas Gohr } 6768cb1eb01SAndreas Gohr return 0; 6778cb1eb01SAndreas Gohr} 6783df72098SAndreas Gohr 6793df72098SAndreas Gohr/** 68075030359SAndreas Gohr * Send a notify mail on uploads 68175030359SAndreas Gohr * 68275030359SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org> 68342ea7f44SGerrit Uitslag * 68442ea7f44SGerrit Uitslag * @param string $id media id 68542ea7f44SGerrit Uitslag * @param string $file path to file 68642ea7f44SGerrit Uitslag * @param string $mime mime type 68742ea7f44SGerrit Uitslag * @param bool|int $old_rev revision timestamp or false 68875030359SAndreas Gohr */ 689d868eb89SAndreas Gohrfunction media_notify($id, $file, $mime, $old_rev = false, $current_rev = false) 690d868eb89SAndreas Gohr{ 69175030359SAndreas Gohr global $conf; 69279a2d784SGerrit Uitslag if (empty($conf['notify'])) return; //notify enabled? 69375030359SAndreas Gohr 694704a815fSMichael Große $subscription = new MediaSubscriptionSender(); 69579a2d784SGerrit Uitslag $subscription->sendMediaDiff($conf['notify'], 'uploadmail', $id, $old_rev, $current_rev); 69675030359SAndreas Gohr} 69775030359SAndreas Gohr 69875030359SAndreas Gohr/** 6993df72098SAndreas Gohr * List all files in a given Media namespace 70042ea7f44SGerrit Uitslag * 70121d806cdSGerrit Uitslag * @param string $ns namespace 70242ea7f44SGerrit Uitslag * @param null|int $auth permission level 70342ea7f44SGerrit Uitslag * @param string $jump id 70442ea7f44SGerrit Uitslag * @param bool $fullscreenview 7058702de7fSGerrit Uitslag * @param bool|string $sort sorting order, false skips sorting 7063df72098SAndreas Gohr */ 707d868eb89SAndreas Gohrfunction media_filelist($ns, $auth = null, $jump = '', $fullscreenview = false, $sort = false) 708d868eb89SAndreas Gohr{ 7093df72098SAndreas Gohr global $conf; 7103df72098SAndreas Gohr global $lang; 7113df72098SAndreas Gohr $ns = cleanID($ns); 7123df72098SAndreas Gohr 7133df72098SAndreas Gohr // check auth our self if not given (needed for ajax calls) 7143df72098SAndreas Gohr if (is_null($auth)) $auth = auth_quickaclcheck("$ns:*"); 7153df72098SAndreas Gohr 716d9162c6cSKate Arzamastseva if (!$fullscreenview) echo '<h1 id="media__ns">:'.hsc($ns).'</h1>'.NL; 7173df72098SAndreas Gohr 7183df72098SAndreas Gohr if ($auth < AUTH_READ) { 7193df72098SAndreas Gohr // FIXME: print permission warning here instead? 7203df72098SAndreas Gohr echo '<div class="nothing">'.$lang['nothingfound'].'</div>'.NL; 72156fe6664SAndreas Gohr } else { 72289274c0dSChristopher Smith if (!$fullscreenview) { 72389274c0dSChristopher Smith media_uploadform($ns, $auth); 72489274c0dSChristopher Smith media_searchform($ns); 72589274c0dSChristopher Smith } 7263df72098SAndreas Gohr 7273df72098SAndreas Gohr $dir = utf8_encodeFN(str_replace(':', '/', $ns)); 72824870174SAndreas Gohr $data = []; 729dccd6b2bSAndreas Gohr search( 730dccd6b2bSAndreas Gohr $data, 731dccd6b2bSAndreas Gohr $conf['mediadir'], 732dccd6b2bSAndreas Gohr 'search_mediafiles', 733dccd6b2bSAndreas Gohr ['showmsg'=>true, 'depth'=>1], 734dccd6b2bSAndreas Gohr $dir, 735dccd6b2bSAndreas Gohr 1, 736dccd6b2bSAndreas Gohr $sort 737dccd6b2bSAndreas Gohr ); 7383df72098SAndreas Gohr 7393df72098SAndreas Gohr if (!count($data)) { 7403df72098SAndreas Gohr echo '<div class="nothing">'.$lang['nothingfound'].'</div>'.NL; 7415b9353faSKate Arzamastseva } else { 7425b9353faSKate Arzamastseva if ($fullscreenview) { 743554a8c9fSAdrian Lang echo '<ul class="' . _media_get_list_type() . '">'; 7445b9353faSKate Arzamastseva } 7455b9353faSKate Arzamastseva foreach ($data as $item) { 7465b9353faSKate Arzamastseva if (!$fullscreenview) { 7474f33babfSAndreas Gohr //FIXME old call: media_printfile($item,$auth,$jump); 74879a2d784SGerrit Uitslag $display = new DisplayRow($item); 7499453716dSAndreas Gohr $display->scrollIntoView($jump == $item->getID()); 7504f33babfSAndreas Gohr $display->show(); 7515b9353faSKate Arzamastseva } else { 7524f33babfSAndreas Gohr //FIXME old call: media_printfile_thumbs($item,$auth,$jump); 7534f33babfSAndreas Gohr echo '<li>'; 75479a2d784SGerrit Uitslag $display = new DisplayTile($item); 7559453716dSAndreas Gohr $display->scrollIntoView($jump == $item->getID()); 7564f33babfSAndreas Gohr $display->show(); 7574f33babfSAndreas Gohr echo '</li>'; 7585b9353faSKate Arzamastseva } 7595b9353faSKate Arzamastseva } 76094add303SAnika Henke if ($fullscreenview) echo '</ul>'.NL; 7613df72098SAndreas Gohr } 76256fe6664SAndreas Gohr } 763d9162c6cSKate Arzamastseva} 764d9162c6cSKate Arzamastseva 765d9162c6cSKate Arzamastseva/** 766d9162c6cSKate Arzamastseva * Prints tabs for files list actions 767d9162c6cSKate Arzamastseva * 768d9162c6cSKate Arzamastseva * @author Kate Arzamastseva <pshns@ukr.net> 76995b451bcSAdrian Lang * @author Adrian Lang <mail@adrianlang.de> 77095b451bcSAdrian Lang * 771035e07f1SKate Arzamastseva * @param string $selected_tab - opened tab 772d9162c6cSKate Arzamastseva */ 77395b451bcSAdrian Lang 774d868eb89SAndreas Gohrfunction media_tabs_files($selected_tab = '') 775d868eb89SAndreas Gohr{ 776d9162c6cSKate Arzamastseva global $lang; 77724870174SAndreas Gohr $tabs = []; 7787d34963bSAndreas Gohr foreach ( 7797d34963bSAndreas Gohr [ 78024870174SAndreas Gohr 'files' => 'mediaselect', 78195b451bcSAdrian Lang 'upload' => 'media_uploadtab', 78224870174SAndreas Gohr 'search' => 'media_searchtab' 7837d34963bSAndreas Gohr ] as $tab => $caption 7847d34963bSAndreas Gohr ) { 78524870174SAndreas Gohr $tabs[$tab] = [ 78624870174SAndreas Gohr 'href' => media_managerURL(['tab_files' => $tab], '&'), 78724870174SAndreas Gohr 'caption' => $lang[$caption] 78824870174SAndreas Gohr ]; 78995b451bcSAdrian Lang } 790d9162c6cSKate Arzamastseva 79195b451bcSAdrian Lang html_tabs($tabs, $selected_tab); 792d9162c6cSKate Arzamastseva} 793d9162c6cSKate Arzamastseva 794d9162c6cSKate Arzamastseva/** 795d9162c6cSKate Arzamastseva * Prints tabs for files details actions 796d9162c6cSKate Arzamastseva * 797d9162c6cSKate Arzamastseva * @author Kate Arzamastseva <pshns@ukr.net> 798ebc28e69SAndreas Gohr * @param string $image filename of the current image 799ebc28e69SAndreas Gohr * @param string $selected_tab opened tab 800d9162c6cSKate Arzamastseva */ 801d868eb89SAndreas Gohrfunction media_tabs_details($image, $selected_tab = '') 802d868eb89SAndreas Gohr{ 803e5d185e1SKate Arzamastseva global $lang, $conf; 804d9162c6cSKate Arzamastseva 80524870174SAndreas Gohr $tabs = []; 80624870174SAndreas Gohr $tabs['view'] = [ 80724870174SAndreas Gohr 'href' => media_managerURL(['tab_details' => 'view'], '&'), 80824870174SAndreas Gohr 'caption' => $lang['media_viewtab'] 80924870174SAndreas Gohr ]; 810d9162c6cSKate Arzamastseva 81124870174SAndreas Gohr [, $mime] = mimetype($image); 81279e79377SAndreas Gohr if ($mime == 'image/jpeg' && file_exists(mediaFN($image))) { 81324870174SAndreas Gohr $tabs['edit'] = [ 81424870174SAndreas Gohr 'href' => media_managerURL(['tab_details' => 'edit'], '&'), 81524870174SAndreas Gohr 'caption' => $lang['media_edittab'] 81624870174SAndreas Gohr ]; 817dd9ba38eSKate Arzamastseva } 818e5d185e1SKate Arzamastseva if ($conf['mediarevisions']) { 81924870174SAndreas Gohr $tabs['history'] = [ 82024870174SAndreas Gohr 'href' => media_managerURL(['tab_details' => 'history'], '&'), 82124870174SAndreas Gohr 'caption' => $lang['media_historytab'] 82224870174SAndreas Gohr ]; 823e5d185e1SKate Arzamastseva } 824d9162c6cSKate Arzamastseva 82595b451bcSAdrian Lang html_tabs($tabs, $selected_tab); 826d9162c6cSKate Arzamastseva} 827d9162c6cSKate Arzamastseva 828d9162c6cSKate Arzamastseva/** 829d9162c6cSKate Arzamastseva * Prints options for the tab that displays a list of all files 830d9162c6cSKate Arzamastseva * 831d9162c6cSKate Arzamastseva * @author Kate Arzamastseva <pshns@ukr.net> 832d9162c6cSKate Arzamastseva */ 833d868eb89SAndreas Gohrfunction media_tab_files_options() 834d868eb89SAndreas Gohr{ 8358108113cSTom N Harris global $lang; 8368108113cSTom N Harris global $INPUT; 837b7e5821dSAndreas Gohr global $ID; 838b960c74fSSatoshi Sahara 839b960c74fSSatoshi Sahara $form = new Form([ 840b960c74fSSatoshi Sahara 'method' => 'get', 841b960c74fSSatoshi Sahara 'action' => wl($ID), 842b960c74fSSatoshi Sahara 'class' => 'options' 843b960c74fSSatoshi Sahara ]); 844b960c74fSSatoshi Sahara $form->addTagOpen('div')->addClass('no'); 845b960c74fSSatoshi Sahara $form->setHiddenField('sectok', null); 846b960c74fSSatoshi Sahara $media_manager_params = media_managerURL([], '', false, true); 84738b2bf35SAnika Henke foreach ($media_manager_params as $pKey => $pVal) { 848b960c74fSSatoshi Sahara $form->setHiddenField($pKey, $pVal); 84938b2bf35SAnika Henke } 8508108113cSTom N Harris if ($INPUT->has('q')) { 851b960c74fSSatoshi Sahara $form->setHiddenField('q', $INPUT->str('q')); 852554a8c9fSAdrian Lang } 853b960c74fSSatoshi Sahara $form->addHTML('<ul>'.NL); 8547d34963bSAndreas Gohr foreach ( 8557d34963bSAndreas Gohr [ 85624870174SAndreas Gohr 'list' => ['listType', ['thumbs', 'rows']], 85724870174SAndreas Gohr 'sort' => ['sortBy', ['name', 'date']] 8587d34963bSAndreas Gohr ] as $group => $content 8597d34963bSAndreas Gohr ) { 860ec88e837SAndreas Gohr $checked = "_media_get_{$group}_type"; 861554a8c9fSAdrian Lang $checked = $checked(); 862d9162c6cSKate Arzamastseva 863b960c74fSSatoshi Sahara $form->addHTML('<li class="'. $content[0] .'">'); 86495b451bcSAdrian Lang foreach ($content[1] as $option) { 86524870174SAndreas Gohr $attrs = []; 866554a8c9fSAdrian Lang if ($checked == $option) { 86795b451bcSAdrian Lang $attrs['checked'] = 'checked'; 86895b451bcSAdrian Lang } 869b960c74fSSatoshi Sahara $radio = $form->addRadioButton( 870b960c74fSSatoshi Sahara $group.'_dwmedia', 871b960c74fSSatoshi Sahara $lang['media_'.$group.'_'.$option] 872b960c74fSSatoshi Sahara )->val($option)->id($content[0].'__'.$option)->addClass($option); 873b960c74fSSatoshi Sahara $radio->attrs($attrs); 87495b451bcSAdrian Lang } 875b960c74fSSatoshi Sahara $form->addHTML('</li>'.NL); 87695b451bcSAdrian Lang } 877b960c74fSSatoshi Sahara $form->addHTML('<li>'); 878b960c74fSSatoshi Sahara $form->addButton('', $lang['btn_apply'])->attr('type', 'submit'); 879b960c74fSSatoshi Sahara $form->addHTML('</li>'.NL); 880b960c74fSSatoshi Sahara $form->addHTML('</ul>'.NL); 881b960c74fSSatoshi Sahara $form->addTagClose('div'); 882*26dfc232SAndreas Gohr echo $form->toHTML(); 883d9162c6cSKate Arzamastseva} 884d9162c6cSKate Arzamastseva 885d9162c6cSKate Arzamastseva/** 88687deddfaSKate Arzamastseva * Returns type of sorting for the list of files in media manager 88787deddfaSKate Arzamastseva * 88887deddfaSKate Arzamastseva * @author Kate Arzamastseva <pshns@ukr.net> 88942ea7f44SGerrit Uitslag * 89087deddfaSKate Arzamastseva * @return string - sort type 89187deddfaSKate Arzamastseva */ 892d868eb89SAndreas Gohrfunction _media_get_sort_type() 893d868eb89SAndreas Gohr{ 89424870174SAndreas Gohr return _media_get_display_param('sort', ['default' => 'name', 'date']); 89587deddfaSKate Arzamastseva} 896554a8c9fSAdrian Lang 8976ffaeda9SGerrit Uitslag/** 8986ffaeda9SGerrit Uitslag * Returns type of listing for the list of files in media manager 8996ffaeda9SGerrit Uitslag * 9006ffaeda9SGerrit Uitslag * @author Kate Arzamastseva <pshns@ukr.net> 90142ea7f44SGerrit Uitslag * 9026ffaeda9SGerrit Uitslag * @return string - list type 9036ffaeda9SGerrit Uitslag */ 904d868eb89SAndreas Gohrfunction _media_get_list_type() 905d868eb89SAndreas Gohr{ 90624870174SAndreas Gohr return _media_get_display_param('list', ['default' => 'thumbs', 'rows']); 90787deddfaSKate Arzamastseva} 908554a8c9fSAdrian Lang 9096ffaeda9SGerrit Uitslag/** 9106ffaeda9SGerrit Uitslag * Get display parameters 9116ffaeda9SGerrit Uitslag * 9126ffaeda9SGerrit Uitslag * @param string $param name of parameter 9136ffaeda9SGerrit Uitslag * @param array $values allowed values, where default value has index key 'default' 9146ffaeda9SGerrit Uitslag * @return string the parameter value 9156ffaeda9SGerrit Uitslag */ 916d868eb89SAndreas Gohrfunction _media_get_display_param($param, $values) 917d868eb89SAndreas Gohr{ 9188108113cSTom N Harris global $INPUT; 9198108113cSTom N Harris if (in_array($INPUT->str($param), $values)) { 920554a8c9fSAdrian Lang // FIXME: Set cookie 9218108113cSTom N Harris return $INPUT->str($param); 922554a8c9fSAdrian Lang } else { 9233629bc8cSAdrian Lang $val = get_doku_pref($param, $values['default']); 9243629bc8cSAdrian Lang if (!in_array($val, $values)) { 9253629bc8cSAdrian Lang $val = $values['default']; 9263629bc8cSAdrian Lang } 9273629bc8cSAdrian Lang return $val; 928554a8c9fSAdrian Lang } 92987deddfaSKate Arzamastseva} 93087deddfaSKate Arzamastseva 93187deddfaSKate Arzamastseva/** 932d9162c6cSKate Arzamastseva * Prints tab that displays a list of all files 933d9162c6cSKate Arzamastseva * 934d9162c6cSKate Arzamastseva * @author Kate Arzamastseva <pshns@ukr.net> 93542ea7f44SGerrit Uitslag * 93642ea7f44SGerrit Uitslag * @param string $ns 93742ea7f44SGerrit Uitslag * @param null|int $auth permission level 93842ea7f44SGerrit Uitslag * @param string $jump item id 939d9162c6cSKate Arzamastseva */ 940d868eb89SAndreas Gohrfunction media_tab_files($ns, $auth = null, $jump = '') 941d868eb89SAndreas Gohr{ 942d9162c6cSKate Arzamastseva global $lang; 943d9162c6cSKate Arzamastseva if (is_null($auth)) $auth = auth_quickaclcheck("$ns:*"); 944d9162c6cSKate Arzamastseva 945d9162c6cSKate Arzamastseva if ($auth < AUTH_READ) { 94688a71175SKate Arzamastseva echo '<div class="nothing">'.$lang['media_perm_read'].'</div>'.NL; 947d9162c6cSKate Arzamastseva } else { 94895b451bcSAdrian Lang media_filelist($ns, $auth, $jump, true, _media_get_sort_type()); 949d9162c6cSKate Arzamastseva } 950d9162c6cSKate Arzamastseva} 951d9162c6cSKate Arzamastseva 952d9162c6cSKate Arzamastseva/** 953d9162c6cSKate Arzamastseva * Prints tab that displays uploading form 954d9162c6cSKate Arzamastseva * 955d9162c6cSKate Arzamastseva * @author Kate Arzamastseva <pshns@ukr.net> 95642ea7f44SGerrit Uitslag * 95742ea7f44SGerrit Uitslag * @param string $ns 95842ea7f44SGerrit Uitslag * @param null|int $auth permission level 95942ea7f44SGerrit Uitslag * @param string $jump item id 960d9162c6cSKate Arzamastseva */ 961d868eb89SAndreas Gohrfunction media_tab_upload($ns, $auth = null, $jump = '') 962d868eb89SAndreas Gohr{ 963d9162c6cSKate Arzamastseva global $lang; 964d9162c6cSKate Arzamastseva if (is_null($auth)) $auth = auth_quickaclcheck("$ns:*"); 965d9162c6cSKate Arzamastseva 96694add303SAnika Henke echo '<div class="upload">'.NL; 96795b451bcSAdrian Lang if ($auth >= AUTH_UPLOAD) { 96895b451bcSAdrian Lang echo '<p>' . $lang['mediaupload'] . '</p>'; 96995b451bcSAdrian Lang } 970d9162c6cSKate Arzamastseva media_uploadform($ns, $auth, true); 97194add303SAnika Henke echo '</div>'.NL; 972d9162c6cSKate Arzamastseva} 973d9162c6cSKate Arzamastseva 974d9162c6cSKate Arzamastseva/** 975d9162c6cSKate Arzamastseva * Prints tab that displays search form 976d9162c6cSKate Arzamastseva * 977d9162c6cSKate Arzamastseva * @author Kate Arzamastseva <pshns@ukr.net> 97842ea7f44SGerrit Uitslag * 97942ea7f44SGerrit Uitslag * @param string $ns 98042ea7f44SGerrit Uitslag * @param null|int $auth permission level 981d9162c6cSKate Arzamastseva */ 982d868eb89SAndreas Gohrfunction media_tab_search($ns, $auth = null) 983d868eb89SAndreas Gohr{ 9848108113cSTom N Harris global $INPUT; 985d9162c6cSKate Arzamastseva 9868108113cSTom N Harris $do = $INPUT->str('mediado'); 9878108113cSTom N Harris $query = $INPUT->str('q'); 98894add303SAnika Henke echo '<div class="search">'.NL; 989d9162c6cSKate Arzamastseva 990d9162c6cSKate Arzamastseva media_searchform($ns, $query, true); 9912dba8df4SAdrian Lang if ($do == 'searchlist' || $query) { 99295b451bcSAdrian Lang media_searchlist($query, $ns, $auth, true, _media_get_sort_type()); 99395b451bcSAdrian Lang } 99494add303SAnika Henke echo '</div>'.NL; 995d9162c6cSKate Arzamastseva} 996d9162c6cSKate Arzamastseva 997d9162c6cSKate Arzamastseva/** 998d9162c6cSKate Arzamastseva * Prints tab that displays mediafile details 999d9162c6cSKate Arzamastseva * 1000d9162c6cSKate Arzamastseva * @author Kate Arzamastseva <pshns@ukr.net> 1001e0c26282SGerrit Uitslag * 1002e0c26282SGerrit Uitslag * @param string $image media id 1003e0c26282SGerrit Uitslag * @param string $ns 1004e0c26282SGerrit Uitslag * @param null|int $auth permission level 100542ea7f44SGerrit Uitslag * @param string|int $rev revision timestamp or empty string 1006d9162c6cSKate Arzamastseva */ 1007d868eb89SAndreas Gohrfunction media_tab_view($image, $ns, $auth = null, $rev = '') 1008d868eb89SAndreas Gohr{ 1009ebc28e69SAndreas Gohr global $lang; 1010d9162c6cSKate Arzamastseva if (is_null($auth)) $auth = auth_quickaclcheck("$ns:*"); 1011d9162c6cSKate Arzamastseva 1012e136d6ccSKate Arzamastseva if ($image && $auth >= AUTH_READ) { 1013dd9ba38eSKate Arzamastseva $meta = new JpegMeta(mediaFN($image, $rev)); 1014dd9ba38eSKate Arzamastseva media_preview($image, $auth, $rev, $meta); 1015e136d6ccSKate Arzamastseva media_preview_buttons($image, $auth, $rev); 1016dd9ba38eSKate Arzamastseva media_details($image, $auth, $rev, $meta); 1017e136d6ccSKate Arzamastseva } else { 101894add303SAnika Henke echo '<div class="nothing">'.$lang['media_perm_read'].'</div>'.NL; 1019e136d6ccSKate Arzamastseva } 1020d9162c6cSKate Arzamastseva} 1021d9162c6cSKate Arzamastseva 1022d9162c6cSKate Arzamastseva/** 1023d9162c6cSKate Arzamastseva * Prints tab that displays form for editing mediafile metadata 1024d9162c6cSKate Arzamastseva * 1025d9162c6cSKate Arzamastseva * @author Kate Arzamastseva <pshns@ukr.net> 1026e0c26282SGerrit Uitslag * 1027e0c26282SGerrit Uitslag * @param string $image media id 1028e0c26282SGerrit Uitslag * @param string $ns 1029e0c26282SGerrit Uitslag * @param null|int $auth permission level 1030d9162c6cSKate Arzamastseva */ 1031d868eb89SAndreas Gohrfunction media_tab_edit($image, $ns, $auth = null) 1032d868eb89SAndreas Gohr{ 1033d9162c6cSKate Arzamastseva if (is_null($auth)) $auth = auth_quickaclcheck("$ns:*"); 1034d9162c6cSKate Arzamastseva 10351eeeced2SKate Arzamastseva if ($image) { 103624870174SAndreas Gohr [, $mime] = mimetype($image); 103730fd72fbSKate Arzamastseva if ($mime == 'image/jpeg') media_metaform($image, $auth); 10381eeeced2SKate Arzamastseva } 1039d9162c6cSKate Arzamastseva} 1040d9162c6cSKate Arzamastseva 1041d9162c6cSKate Arzamastseva/** 1042d9162c6cSKate Arzamastseva * Prints tab that displays mediafile revisions 1043d9162c6cSKate Arzamastseva * 1044d9162c6cSKate Arzamastseva * @author Kate Arzamastseva <pshns@ukr.net> 1045e0c26282SGerrit Uitslag * 1046e0c26282SGerrit Uitslag * @param string $image media id 1047e0c26282SGerrit Uitslag * @param string $ns 1048e0c26282SGerrit Uitslag * @param null|int $auth permission level 1049d9162c6cSKate Arzamastseva */ 1050d868eb89SAndreas Gohrfunction media_tab_history($image, $ns, $auth = null) 1051d868eb89SAndreas Gohr{ 1052d9162c6cSKate Arzamastseva global $lang; 10538108113cSTom N Harris global $INPUT; 10548108113cSTom N Harris 1055d9162c6cSKate Arzamastseva if (is_null($auth)) $auth = auth_quickaclcheck("$ns:*"); 10568108113cSTom N Harris $do = $INPUT->str('mediado'); 1057d9162c6cSKate Arzamastseva 10582e55802cSKate Arzamastseva if ($auth >= AUTH_READ && $image) { 10592e55802cSKate Arzamastseva if ($do == 'diff') { 106024870174SAndreas Gohr (new MediaDiff($image))->show(); //media_diff($image, $ns, $auth); 10612e55802cSKate Arzamastseva } else { 1062a46cc3dcSAndreas Gohr $first = $INPUT->int('first', -1); 106324870174SAndreas Gohr (new MediaRevisions($image))->show($first); 10642e55802cSKate Arzamastseva } 106588a71175SKate Arzamastseva } else { 106688a71175SKate Arzamastseva echo '<div class="nothing">'.$lang['media_perm_read'].'</div>'.NL; 10672e55802cSKate Arzamastseva } 10683df72098SAndreas Gohr} 10693df72098SAndreas Gohr 10703df72098SAndreas Gohr/** 10711eeeced2SKate Arzamastseva * Prints mediafile details 10721eeeced2SKate Arzamastseva * 10736ffaeda9SGerrit Uitslag * @param string $image media id 1074e0c26282SGerrit Uitslag * @param int $auth permission level 107559bc3b48SGerrit Uitslag * @param int|string $rev revision timestamp or empty string 10766ffaeda9SGerrit Uitslag * @param JpegMeta|bool $meta 107742ea7f44SGerrit Uitslag * 10781eeeced2SKate Arzamastseva * @author Kate Arzamastseva <pshns@ukr.net> 10791eeeced2SKate Arzamastseva */ 1080d868eb89SAndreas Gohrfunction media_preview($image, $auth, $rev = '', $meta = false) 1081d868eb89SAndreas Gohr{ 1082e136d6ccSKate Arzamastseva 1083e136d6ccSKate Arzamastseva $size = media_image_preview_size($image, $rev, $meta); 1084e136d6ccSKate Arzamastseva 1085e136d6ccSKate Arzamastseva if ($size) { 108659f3611bSAnika Henke global $lang; 108794add303SAnika Henke echo '<div class="image">'; 108894add303SAnika Henke 108924870174SAndreas Gohr $more = []; 1090e136d6ccSKate Arzamastseva if ($rev) { 10915b9353faSKate Arzamastseva $more['rev'] = $rev; 1092e136d6ccSKate Arzamastseva } else { 1093e136d6ccSKate Arzamastseva $t = @filemtime(mediaFN($image)); 10945b9353faSKate Arzamastseva $more['t'] = $t; 109588a71175SKate Arzamastseva } 109623786fd7SKate Arzamastseva 10975b9353faSKate Arzamastseva $more['w'] = $size[0]; 10985b9353faSKate Arzamastseva $more['h'] = $size[1]; 1099e136d6ccSKate Arzamastseva $src = ml($image, $more); 110059f3611bSAnika Henke 110159f3611bSAnika Henke echo '<a href="'.$src.'" target="_blank" title="'.$lang['mediaview'].'">'; 110295b451bcSAdrian Lang echo '<img src="'.$src.'" alt="" style="max-width: '.$size[0].'px;" />'; 110359f3611bSAnika Henke echo '</a>'; 1104e136d6ccSKate Arzamastseva 1105871895a7SSatoshi Sahara echo '</div>'; 110694add303SAnika Henke } 1107e136d6ccSKate Arzamastseva} 1108e136d6ccSKate Arzamastseva 1109e136d6ccSKate Arzamastseva/** 1110e136d6ccSKate Arzamastseva * Prints mediafile action buttons 1111e136d6ccSKate Arzamastseva * 1112e136d6ccSKate Arzamastseva * @author Kate Arzamastseva <pshns@ukr.net> 1113e0c26282SGerrit Uitslag * 1114e0c26282SGerrit Uitslag * @param string $image media id 1115e0c26282SGerrit Uitslag * @param int $auth permission level 1116871895a7SSatoshi Sahara * @param int|string $rev revision timestamp, or empty string 1117e136d6ccSKate Arzamastseva */ 1118d868eb89SAndreas Gohrfunction media_preview_buttons($image, $auth, $rev = '') 1119d868eb89SAndreas Gohr{ 1120e5d185e1SKate Arzamastseva global $lang, $conf; 1121e136d6ccSKate Arzamastseva 1122871895a7SSatoshi Sahara echo '<ul class="actions">'; 11231eeeced2SKate Arzamastseva 112479e79377SAndreas Gohr if ($auth >= AUTH_DELETE && !$rev && file_exists(mediaFN($image))) { 1125e136d6ccSKate Arzamastseva // delete button 1126b960c74fSSatoshi Sahara $form = new Form([ 1127b960c74fSSatoshi Sahara 'id' => 'mediamanager__btn_delete', 1128b960c74fSSatoshi Sahara 'action' => media_managerURL(['delete' => $image], '&'), 1129b960c74fSSatoshi Sahara ]); 1130b960c74fSSatoshi Sahara $form->addTagOpen('div')->addClass('no'); 1131b960c74fSSatoshi Sahara $form->addButton('', $lang['btn_delete'])->attr('type', 'submit'); 1132b960c74fSSatoshi Sahara $form->addTagClose('div'); 113395b451bcSAdrian Lang echo '<li>'; 1134b960c74fSSatoshi Sahara echo $form->toHTML(); 1135871895a7SSatoshi Sahara echo '</li>'; 1136e5d185e1SKate Arzamastseva } 1137e5d185e1SKate Arzamastseva 1138e5d185e1SKate Arzamastseva $auth_ow = (($conf['mediarevisions']) ? AUTH_UPLOAD : AUTH_DELETE); 1139e5d185e1SKate Arzamastseva if ($auth >= $auth_ow && !$rev) { 1140e136d6ccSKate Arzamastseva // upload new version button 1141b960c74fSSatoshi Sahara $form = new Form([ 1142b960c74fSSatoshi Sahara 'id' => 'mediamanager__btn_update', 1143b960c74fSSatoshi Sahara 'action' => media_managerURL(['image' => $image, 'mediado' => 'update'], '&'), 1144b960c74fSSatoshi Sahara ]); 1145b960c74fSSatoshi Sahara $form->addTagOpen('div')->addClass('no'); 1146b960c74fSSatoshi Sahara $form->addButton('', $lang['media_update'])->attr('type', 'submit'); 1147b960c74fSSatoshi Sahara $form->addTagClose('div'); 114895b451bcSAdrian Lang echo '<li>'; 1149b960c74fSSatoshi Sahara echo $form->toHTML(); 1150871895a7SSatoshi Sahara echo '</li>'; 115170c3cc9aSKate Arzamastseva } 1152e136d6ccSKate Arzamastseva 115379e79377SAndreas Gohr if ($auth >= AUTH_UPLOAD && $rev && $conf['mediarevisions'] && file_exists(mediaFN($image, $rev))) { 1154e136d6ccSKate Arzamastseva // restore button 1155b960c74fSSatoshi Sahara $form = new Form([ 1156b960c74fSSatoshi Sahara 'id' => 'mediamanager__btn_restore', 1157b960c74fSSatoshi Sahara 'action'=>media_managerURL(['image' => $image], '&'), 1158b960c74fSSatoshi Sahara ]); 1159b960c74fSSatoshi Sahara $form->addTagOpen('div')->addClass('no'); 1160b960c74fSSatoshi Sahara $form->setHiddenField('mediado', 'restore'); 1161b960c74fSSatoshi Sahara $form->setHiddenField('rev', $rev); 1162b960c74fSSatoshi Sahara $form->addButton('', $lang['media_restore'])->attr('type', 'submit'); 1163b960c74fSSatoshi Sahara $form->addTagClose('div'); 116495b451bcSAdrian Lang echo '<li>'; 1165b960c74fSSatoshi Sahara echo $form->toHTML(); 1166871895a7SSatoshi Sahara echo '</li>'; 11679c1bd4bcSKate Arzamastseva } 1168e136d6ccSKate Arzamastseva 1169871895a7SSatoshi Sahara echo '</ul>'; 11702e55802cSKate Arzamastseva} 11712e55802cSKate Arzamastseva 11722e55802cSKate Arzamastseva/** 1173fa8e5c77SKate Arzamastseva * Returns image width and height for mediamanager preview panel 1174fa8e5c77SKate Arzamastseva * 1175fa8e5c77SKate Arzamastseva * @author Kate Arzamastseva <pshns@ukr.net> 1176fa8e5c77SKate Arzamastseva * @param string $image 1177e0c26282SGerrit Uitslag * @param int|string $rev 117859bc3b48SGerrit Uitslag * @param JpegMeta|bool $meta 1179ebc28e69SAndreas Gohr * @param int $size 1180871895a7SSatoshi Sahara * @return array 1181fa8e5c77SKate Arzamastseva */ 1182d868eb89SAndreas Gohrfunction media_image_preview_size($image, $rev, $meta = false, $size = 500) 1183d868eb89SAndreas Gohr{ 11847d34963bSAndreas Gohr if ( 11857d34963bSAndreas Gohr !preg_match("/\.(jpe?g|gif|png)$/", $image) 1186871895a7SSatoshi Sahara || !file_exists($filename = mediaFN($image, $rev)) 118724870174SAndreas Gohr ) return []; 1188fa8e5c77SKate Arzamastseva 1189871895a7SSatoshi Sahara $info = getimagesize($filename); 119024870174SAndreas Gohr $w = $info[0]; 119124870174SAndreas Gohr $h = $info[1]; 1192fa8e5c77SKate Arzamastseva 1193fa8e5c77SKate Arzamastseva if ($meta && ($w > $size || $h > $size)) { 1194fa8e5c77SKate Arzamastseva $ratio = $meta->getResizeRatio($size, $size); 1195fa8e5c77SKate Arzamastseva $w = floor($w * $ratio); 1196fa8e5c77SKate Arzamastseva $h = floor($h * $ratio); 1197fa8e5c77SKate Arzamastseva } 119824870174SAndreas Gohr return [$w, $h]; 1199fa8e5c77SKate Arzamastseva} 1200fa8e5c77SKate Arzamastseva 1201fa8e5c77SKate Arzamastseva/** 1202fa8e5c77SKate Arzamastseva * Returns the requested EXIF/IPTC tag from the image meta 1203fa8e5c77SKate Arzamastseva * 1204fa8e5c77SKate Arzamastseva * @author Kate Arzamastseva <pshns@ukr.net> 1205e0c26282SGerrit Uitslag * 120642ea7f44SGerrit Uitslag * @param array $tags array with tags, first existing is returned 1207fa8e5c77SKate Arzamastseva * @param JpegMeta $meta 120842ea7f44SGerrit Uitslag * @param string $alt alternative value 1209fa8e5c77SKate Arzamastseva * @return string 1210fa8e5c77SKate Arzamastseva */ 1211d868eb89SAndreas Gohrfunction media_getTag($tags, $meta = false, $alt = '') 1212d868eb89SAndreas Gohr{ 1213871895a7SSatoshi Sahara if (!$meta) return $alt; 1214fa8e5c77SKate Arzamastseva $info = $meta->getField($tags); 1215871895a7SSatoshi Sahara if (!$info) return $alt; 1216fa8e5c77SKate Arzamastseva return $info; 1217fa8e5c77SKate Arzamastseva} 1218fa8e5c77SKate Arzamastseva 1219fa8e5c77SKate Arzamastseva/** 1220e136d6ccSKate Arzamastseva * Returns mediafile tags 12212e55802cSKate Arzamastseva * 12222e55802cSKate Arzamastseva * @author Kate Arzamastseva <pshns@ukr.net> 1223e0c26282SGerrit Uitslag * 1224e136d6ccSKate Arzamastseva * @param JpegMeta $meta 122542ea7f44SGerrit Uitslag * @return array list of tags of the mediafile 12262e55802cSKate Arzamastseva */ 1227d868eb89SAndreas Gohrfunction media_file_tags($meta) 1228d868eb89SAndreas Gohr{ 1229532850edSKate Arzamastseva // load the field descriptions 12303e98e685SKate Arzamastseva static $fields = null; 12313e98e685SKate Arzamastseva if (is_null($fields)) { 12323e98e685SKate Arzamastseva $config_files = getConfigFiles('mediameta'); 12333e98e685SKate Arzamastseva foreach ($config_files as $config_file) { 123479e79377SAndreas Gohr if (file_exists($config_file)) include($config_file); 1235532850edSKate Arzamastseva } 1236532850edSKate Arzamastseva } 12371eeeced2SKate Arzamastseva 123824870174SAndreas Gohr $tags = []; 1239dd9ba38eSKate Arzamastseva 124024870174SAndreas Gohr foreach ($fields as $tag) { 124124870174SAndreas Gohr $t = []; 124224870174SAndreas Gohr if (!empty($tag[0])) $t = [$tag[0]]; 12430e80bb5eSChristopher Smith if (isset($tag[3]) && is_array($tag[3])) $t = array_merge($t, $tag[3]); 1244e136d6ccSKate Arzamastseva $value = media_getTag($t, $meta); 124524870174SAndreas Gohr $tags[] = ['tag' => $tag, 'value' => $value]; 1246e136d6ccSKate Arzamastseva } 1247e136d6ccSKate Arzamastseva 1248e136d6ccSKate Arzamastseva return $tags; 1249e136d6ccSKate Arzamastseva} 1250e136d6ccSKate Arzamastseva 1251e136d6ccSKate Arzamastseva/** 1252e136d6ccSKate Arzamastseva * Prints mediafile tags 1253e136d6ccSKate Arzamastseva * 1254e136d6ccSKate Arzamastseva * @author Kate Arzamastseva <pshns@ukr.net> 125542ea7f44SGerrit Uitslag * 1256e0c26282SGerrit Uitslag * @param string $image image id 1257e0c26282SGerrit Uitslag * @param int $auth permission level 1258e0c26282SGerrit Uitslag * @param string|int $rev revision timestamp, or empty string 1259e0c26282SGerrit Uitslag * @param bool|JpegMeta $meta image object, or create one if false 1260e0c26282SGerrit Uitslag */ 1261d868eb89SAndreas Gohrfunction media_details($image, $auth, $rev = '', $meta = false) 1262d868eb89SAndreas Gohr{ 1263e136d6ccSKate Arzamastseva global $lang; 1264e136d6ccSKate Arzamastseva 1265e136d6ccSKate Arzamastseva if (!$meta) $meta = new JpegMeta(mediaFN($image, $rev)); 1266e136d6ccSKate Arzamastseva $tags = media_file_tags($meta); 1267e136d6ccSKate Arzamastseva 126894add303SAnika Henke echo '<dl>'.NL; 1269e136d6ccSKate Arzamastseva foreach ($tags as $tag) { 1270e136d6ccSKate Arzamastseva if ($tag['value']) { 1271e136d6ccSKate Arzamastseva $value = cleanText($tag['value']); 1272fde860beSGerrit Uitslag echo '<dt>'.$lang[$tag['tag'][1]].'</dt><dd>'; 1273e136d6ccSKate Arzamastseva if ($tag['tag'][2] == 'date') echo dformat($value); 12743e98e685SKate Arzamastseva else echo hsc($value); 127594add303SAnika Henke echo '</dd>'.NL; 12761eeeced2SKate Arzamastseva } 1277e136d6ccSKate Arzamastseva } 127894add303SAnika Henke echo '</dl>'.NL; 127922e68399Sflammy echo '<dl>'.NL; 128022e68399Sflammy echo '<dt>'.$lang['reference'].':</dt>'; 128122e68399Sflammy $media_usage = ft_mediause($image, true); 128224870174SAndreas Gohr if ($media_usage !== []) { 128322e68399Sflammy foreach ($media_usage as $path) { 128422e68399Sflammy echo '<dd>'.html_wikilink($path).'</dd>'; 128522e68399Sflammy } 128622e68399Sflammy } else { 128722e68399Sflammy echo '<dd>'.$lang['nothingfound'].'</dd>'; 128822e68399Sflammy } 128922e68399Sflammy echo '</dl>'.NL; 129022e68399Sflammy 12911eeeced2SKate Arzamastseva} 12922e55802cSKate Arzamastseva 12932e55802cSKate Arzamastseva/** 12942e55802cSKate Arzamastseva * Shows difference between two revisions of file 12952e55802cSKate Arzamastseva * 12962e55802cSKate Arzamastseva * @author Kate Arzamastseva <pshns@ukr.net> 129742ea7f44SGerrit Uitslag * 1298e0c26282SGerrit Uitslag * @param string $image image id 1299e0c26282SGerrit Uitslag * @param string $ns 1300e0c26282SGerrit Uitslag * @param int $auth permission level 1301e0c26282SGerrit Uitslag * @param bool $fromajax 130279a2d784SGerrit Uitslag * 1303297071beSSatoshi Sahara * @deprecated 2020-12-31 1304e0c26282SGerrit Uitslag */ 1305d868eb89SAndreas Gohrfunction media_diff($image, $ns, $auth, $fromajax = false) 1306d868eb89SAndreas Gohr{ 130779a2d784SGerrit Uitslag dbg_deprecated('see '. MediaDiff::class .'::show()'); 130859e81a43SKate Arzamastseva} 130959e81a43SKate Arzamastseva 13106ffaeda9SGerrit Uitslag/** 13116ffaeda9SGerrit Uitslag * Callback for media file diff 13126ffaeda9SGerrit Uitslag * 131342ea7f44SGerrit Uitslag * @param array $data event data 131479a2d784SGerrit Uitslag * 1315297071beSSatoshi Sahara * @deprecated 2020-12-31 13166ffaeda9SGerrit Uitslag */ 1317d868eb89SAndreas Gohrfunction _media_file_diff($data) 1318d868eb89SAndreas Gohr{ 131979a2d784SGerrit Uitslag dbg_deprecated('see '. MediaDiff::class .'::show()'); 132059e81a43SKate Arzamastseva} 132159e81a43SKate Arzamastseva 132259e81a43SKate Arzamastseva/** 132359e81a43SKate Arzamastseva * Shows difference between two revisions of image 132459e81a43SKate Arzamastseva * 132559e81a43SKate Arzamastseva * @author Kate Arzamastseva <pshns@ukr.net> 1326e0c26282SGerrit Uitslag * 1327e0c26282SGerrit Uitslag * @param string $image 1328e0c26282SGerrit Uitslag * @param string|int $l_rev revision timestamp, or empty string 1329e0c26282SGerrit Uitslag * @param string|int $r_rev revision timestamp, or empty string 1330e0c26282SGerrit Uitslag * @param string $ns 1331e0c26282SGerrit Uitslag * @param int $auth permission level 1332e0c26282SGerrit Uitslag * @param bool $fromajax 1333297071beSSatoshi Sahara * @deprecated 2020-12-31 133459e81a43SKate Arzamastseva */ 1335d868eb89SAndreas Gohrfunction media_file_diff($image, $l_rev, $r_rev, $ns, $auth, $fromajax) 1336d868eb89SAndreas Gohr{ 133779a2d784SGerrit Uitslag dbg_deprecated('see '. MediaDiff::class .'::showFileDiff()'); 1338e136d6ccSKate Arzamastseva} 1339e136d6ccSKate Arzamastseva 1340e136d6ccSKate Arzamastseva/** 1341e136d6ccSKate Arzamastseva * Prints two images side by side 1342fa8e5c77SKate Arzamastseva * and slider 1343e136d6ccSKate Arzamastseva * 1344e136d6ccSKate Arzamastseva * @author Kate Arzamastseva <pshns@ukr.net> 134542ea7f44SGerrit Uitslag * 134642ea7f44SGerrit Uitslag * @param string $image image id 1347e0c26282SGerrit Uitslag * @param int $l_rev revision timestamp, or empty string 1348e0c26282SGerrit Uitslag * @param int $r_rev revision timestamp, or empty string 134942ea7f44SGerrit Uitslag * @param array $l_size array with width and height 135042ea7f44SGerrit Uitslag * @param array $r_size array with width and height 135150fc55feSKate Arzamastseva * @param string $type 1352297071beSSatoshi Sahara * @deprecated 2020-12-31 1353e136d6ccSKate Arzamastseva */ 1354d868eb89SAndreas Gohrfunction media_image_diff($image, $l_rev, $r_rev, $l_size, $r_size, $type) 1355d868eb89SAndreas Gohr{ 135679a2d784SGerrit Uitslag dbg_deprecated('see '. MediaDiff::class .'::showImageDiff()'); 13571eeeced2SKate Arzamastseva} 13581eeeced2SKate Arzamastseva 13591eeeced2SKate Arzamastseva/** 13609c1bd4bcSKate Arzamastseva * Restores an old revision of a media file 13619c1bd4bcSKate Arzamastseva * 136242ea7f44SGerrit Uitslag * @param string $image media id 136342ea7f44SGerrit Uitslag * @param int $rev revision timestamp or empty string 13649c1bd4bcSKate Arzamastseva * @param int $auth 13659c1bd4bcSKate Arzamastseva * @return string - file's id 136642ea7f44SGerrit Uitslag * 13679c1bd4bcSKate Arzamastseva * @author Kate Arzamastseva <pshns@ukr.net> 13689c1bd4bcSKate Arzamastseva */ 1369d868eb89SAndreas Gohrfunction media_restore($image, $rev, $auth) 1370d868eb89SAndreas Gohr{ 1371e5d185e1SKate Arzamastseva global $conf; 137280525638SKate Arzamastseva if ($auth < AUTH_UPLOAD || !$conf['mediarevisions']) return false; 137392cac9a9SKate Arzamastseva $removed = (!file_exists(mediaFN($image)) && file_exists(mediaMetaFN($image, '.changes'))); 137492cac9a9SKate Arzamastseva if (!$image || (!file_exists(mediaFN($image)) && !$removed)) return false; 13759c1bd4bcSKate Arzamastseva if (!$rev || !file_exists(mediaFN($image, $rev))) return false; 137624870174SAndreas Gohr [, $imime, ] = mimetype($image); 1377dccd6b2bSAndreas Gohr $res = media_upload_finish( 1378dccd6b2bSAndreas Gohr mediaFN($image, $rev), 13799c1bd4bcSKate Arzamastseva mediaFN($image), 13809c1bd4bcSKate Arzamastseva $image, 13819c1bd4bcSKate Arzamastseva $imime, 13829c1bd4bcSKate Arzamastseva true, 1383dccd6b2bSAndreas Gohr 'copy' 1384dccd6b2bSAndreas Gohr ); 13859c1bd4bcSKate Arzamastseva if (is_array($res)) { 13869c1bd4bcSKate Arzamastseva msg($res[0], $res[1]); 13879c1bd4bcSKate Arzamastseva return false; 13889c1bd4bcSKate Arzamastseva } 13899c1bd4bcSKate Arzamastseva return $res; 13909c1bd4bcSKate Arzamastseva} 13919c1bd4bcSKate Arzamastseva 13929c1bd4bcSKate Arzamastseva/** 1393bf1f3ac4Ssarnowski * List all files found by the search request 1394c9f56829SAndreas Gohr * 1395c9f56829SAndreas Gohr * @author Tobias Sarnowski <sarnowski@cosmocode.de> 1396c9f56829SAndreas Gohr * @author Andreas Gohr <gohr@cosmocode.de> 1397d9162c6cSKate Arzamastseva * @author Kate Arzamastseva <pshns@ukr.net> 1398c9f56829SAndreas Gohr * @triggers MEDIA_SEARCH 1399e0c26282SGerrit Uitslag * 1400e0c26282SGerrit Uitslag * @param string $query 1401e0c26282SGerrit Uitslag * @param string $ns 1402e0c26282SGerrit Uitslag * @param null|int $auth 1403e0c26282SGerrit Uitslag * @param bool $fullscreen 1404e0c26282SGerrit Uitslag * @param string $sort 1405bf1f3ac4Ssarnowski */ 1406d868eb89SAndreas Gohrfunction media_searchlist($query, $ns, $auth = null, $fullscreen = false, $sort = 'natural') 1407d868eb89SAndreas Gohr{ 1408bf1f3ac4Ssarnowski global $conf; 1409bf1f3ac4Ssarnowski global $lang; 14108e69fd30SKate Arzamastseva 1411bf1f3ac4Ssarnowski $ns = cleanID($ns); 141224870174SAndreas Gohr $evdata = [ 141356fe6664SAndreas Gohr 'ns' => $ns, 141424870174SAndreas Gohr 'data' => [], 141556fe6664SAndreas Gohr 'query' => $query 141624870174SAndreas Gohr ]; 14177ed31746SSzymon Olewniczak if (!blank($query)) { 1418e1d9dcc8SAndreas Gohr $evt = new Event('MEDIA_SEARCH', $evdata); 1419bf1f3ac4Ssarnowski if ($evt->advise_before()) { 142056fe6664SAndreas Gohr $dir = utf8_encodeFN(str_replace(':', '/', $evdata['ns'])); 142176472096SSzymon Olewniczak $quoted = preg_quote($evdata['query'], '/'); 142276472096SSzymon Olewniczak //apply globbing 142324870174SAndreas Gohr $quoted = str_replace(['\*', '\?'], ['.*', '.'], $quoted, $count); 142423e31e76SSzymon Olewniczak 142523e31e76SSzymon Olewniczak //if we use globbing file name must match entirely but may be preceded by arbitrary namespace 142623e31e76SSzymon Olewniczak if ($count > 0) $quoted = '^([^:]*:)*'.$quoted.'$'; 142723e31e76SSzymon Olewniczak 142876472096SSzymon Olewniczak $pattern = '/'.$quoted.'/i'; 1429dccd6b2bSAndreas Gohr search( 1430dccd6b2bSAndreas Gohr $evdata['data'], 143156fe6664SAndreas Gohr $conf['mediadir'], 14324f33babfSAndreas Gohr 'search_mediafiles', 143324870174SAndreas Gohr ['showmsg'=>false, 'pattern'=>$pattern], 143400e3e394SChristopher Smith $dir, 143500e3e394SChristopher Smith 1, 1436dccd6b2bSAndreas Gohr $sort 1437dccd6b2bSAndreas Gohr ); 1438bf1f3ac4Ssarnowski } 1439bf1f3ac4Ssarnowski $evt->advise_after(); 1440bf1f3ac4Ssarnowski unset($evt); 144156fe6664SAndreas Gohr } 1442bf1f3ac4Ssarnowski 1443d9162c6cSKate Arzamastseva if (!$fullscreen) { 144456fe6664SAndreas Gohr echo '<h1 id="media__ns">'.sprintf($lang['searchmedia_in'], hsc($ns).':*').'</h1>'.NL; 144556fe6664SAndreas Gohr media_searchform($ns, $query); 1446d9162c6cSKate Arzamastseva } 144756fe6664SAndreas Gohr 144856fe6664SAndreas Gohr if (!count($evdata['data'])) { 1449bf1f3ac4Ssarnowski echo '<div class="nothing">'.$lang['nothingfound'].'</div>'.NL; 14505b9353faSKate Arzamastseva } else { 14515b9353faSKate Arzamastseva if ($fullscreen) { 1452554a8c9fSAdrian Lang echo '<ul class="' . _media_get_list_type() . '">'; 14535b9353faSKate Arzamastseva } 14545b9353faSKate Arzamastseva foreach ($evdata['data'] as $item) { 14554f33babfSAndreas Gohr if (!$fullscreen) { 14564f33babfSAndreas Gohr // FIXME old call: media_printfile($item,$item['perm'],'',true); 145779a2d784SGerrit Uitslag $display = new DisplayRow($item); 145879b00823SAndreas Gohr $display->relativeDisplay($ns); 14594f33babfSAndreas Gohr $display->show(); 14604f33babfSAndreas Gohr } else { 14614f33babfSAndreas Gohr // FIXME old call: media_printfile_thumbs($item,$item['perm'],false,true); 146279a2d784SGerrit Uitslag $display = new DisplayTile($item); 146379b00823SAndreas Gohr $display->relativeDisplay($ns); 14644f33babfSAndreas Gohr echo '<li>'; 14654f33babfSAndreas Gohr $display->show(); 14664f33babfSAndreas Gohr echo '</li>'; 14674f33babfSAndreas Gohr } 1468bf1f3ac4Ssarnowski } 146994add303SAnika Henke if ($fullscreen) echo '</ul>'.NL; 14705b9353faSKate Arzamastseva } 1471bf1f3ac4Ssarnowski} 1472bf1f3ac4Ssarnowski 1473bf1f3ac4Ssarnowski/** 1474c6571d58SAndreas Gohr * Display a media icon 1475c6571d58SAndreas Gohr * 147642ea7f44SGerrit Uitslag * @param string $filename media id 1477c6571d58SAndreas Gohr * @param string $size the size subfolder, if not specified 16x16 is used 147842ea7f44SGerrit Uitslag * @return string html 1479c6571d58SAndreas Gohr */ 1480d868eb89SAndreas Gohrfunction media_printicon($filename, $size = '') 1481d868eb89SAndreas Gohr{ 148224870174SAndreas Gohr [$ext] = mimetype(mediaFN($filename), false); 148323786fd7SKate Arzamastseva 148479e79377SAndreas Gohr if (file_exists(DOKU_INC.'lib/images/fileicons/'.$size.'/'.$ext.'.png')) { 1485c6571d58SAndreas Gohr $icon = DOKU_BASE.'lib/images/fileicons/'.$size.'/'.$ext.'.png'; 148623786fd7SKate Arzamastseva } else { 1487c6571d58SAndreas Gohr $icon = DOKU_BASE.'lib/images/fileicons/'.$size.'/file.png'; 148823786fd7SKate Arzamastseva } 148923786fd7SKate Arzamastseva 1490421ec38eSKate Arzamastseva return '<img src="'.$icon.'" alt="'.$filename.'" class="icon" />'; 149123786fd7SKate Arzamastseva} 149223786fd7SKate Arzamastseva 14933df72098SAndreas Gohr/** 149442ea7f44SGerrit Uitslag * Build link based on the current, adding/rewriting parameters 1495d9162c6cSKate Arzamastseva * 1496d9162c6cSKate Arzamastseva * @author Kate Arzamastseva <pshns@ukr.net> 149742ea7f44SGerrit Uitslag * 1498ebc28e69SAndreas Gohr * @param array|bool $params 149942ea7f44SGerrit Uitslag * @param string $amp separator 150042ea7f44SGerrit Uitslag * @param bool $abs absolute url? 150142ea7f44SGerrit Uitslag * @param bool $params_array return the parmeters array? 150242ea7f44SGerrit Uitslag * @return string|array - link or link parameters 1503d9162c6cSKate Arzamastseva */ 1504d868eb89SAndreas Gohrfunction media_managerURL($params = false, $amp = '&', $abs = false, $params_array = false) 1505d868eb89SAndreas Gohr{ 1506d9162c6cSKate Arzamastseva global $ID; 15078108113cSTom N Harris global $INPUT; 1508d9162c6cSKate Arzamastseva 150924870174SAndreas Gohr $gets = ['do' => 'media']; 151024870174SAndreas Gohr $media_manager_params = ['tab_files', 'tab_details', 'image', 'ns', 'list', 'sort']; 151123846a98SKate Arzamastseva foreach ($media_manager_params as $x) { 15128108113cSTom N Harris if ($INPUT->has($x)) $gets[$x] = $INPUT->str($x); 151323846a98SKate Arzamastseva } 1514d9162c6cSKate Arzamastseva 1515d9162c6cSKate Arzamastseva if ($params) { 1516554a8c9fSAdrian Lang $gets = $params + $gets; 1517d9162c6cSKate Arzamastseva } 1518d9162c6cSKate Arzamastseva unset($gets['id']); 1519554a8c9fSAdrian Lang if (isset($gets['delete'])) { 15201eeeced2SKate Arzamastseva unset($gets['image']); 15211eeeced2SKate Arzamastseva unset($gets['tab_details']); 15221eeeced2SKate Arzamastseva } 1523d9162c6cSKate Arzamastseva 1524035e07f1SKate Arzamastseva if ($params_array) return $gets; 1525035e07f1SKate Arzamastseva 15266dd095f5SKate Arzamastseva return wl($ID, $gets, $abs, $amp); 1527d9162c6cSKate Arzamastseva} 1528d9162c6cSKate Arzamastseva 1529d9162c6cSKate Arzamastseva/** 15303df72098SAndreas Gohr * Print the media upload form if permissions are correct 15313df72098SAndreas Gohr * 15323df72098SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org> 1533d9162c6cSKate Arzamastseva * @author Kate Arzamastseva <pshns@ukr.net> 1534e0c26282SGerrit Uitslag * 1535e0c26282SGerrit Uitslag * @param string $ns 1536e0c26282SGerrit Uitslag * @param int $auth permission level 1537e0c26282SGerrit Uitslag * @param bool $fullscreen 15383df72098SAndreas Gohr */ 1539d868eb89SAndreas Gohrfunction media_uploadform($ns, $auth, $fullscreen = false) 1540d868eb89SAndreas Gohr{ 15418108113cSTom N Harris global $lang; 15428108113cSTom N Harris global $conf; 15438108113cSTom N Harris global $INPUT; 15443df72098SAndreas Gohr 154588a71175SKate Arzamastseva if ($auth < AUTH_UPLOAD) { 154688a71175SKate Arzamastseva echo '<div class="nothing">'.$lang['media_perm_upload'].'</div>'.NL; 154788a71175SKate Arzamastseva return; 154888a71175SKate Arzamastseva } 154992cac9a9SKate Arzamastseva $auth_ow = (($conf['mediarevisions']) ? AUTH_UPLOAD : AUTH_DELETE); 15503df72098SAndreas Gohr 155170c3cc9aSKate Arzamastseva $update = false; 155270c3cc9aSKate Arzamastseva $id = ''; 15538108113cSTom N Harris if ($auth >= $auth_ow && $fullscreen && $INPUT->str('mediado') == 'update') { 155470c3cc9aSKate Arzamastseva $update = true; 15558108113cSTom N Harris $id = cleanID($INPUT->str('image')); 155670c3cc9aSKate Arzamastseva } 155770c3cc9aSKate Arzamastseva 1558d00ec455SAndreas Gohr // The default HTML upload form 1559b960c74fSSatoshi Sahara $form = new Form([ 1560b960c74fSSatoshi Sahara 'id' => 'dw__upload', 1561b960c74fSSatoshi Sahara 'enctype' => 'multipart/form-data', 1562b960c74fSSatoshi Sahara 'action' => ($fullscreen) 1563b960c74fSSatoshi Sahara ? media_managerURL(['tab_files' => 'files', 'tab_details' => 'view'], '&') 1564b960c74fSSatoshi Sahara : DOKU_BASE.'lib/exe/mediamanager.php', 1565b960c74fSSatoshi Sahara ]); 1566b960c74fSSatoshi Sahara $form->addTagOpen('div')->addClass('no'); 1567b960c74fSSatoshi Sahara $form->setHiddenField('ns', hsc($ns)); // FIXME hsc required? 1568b960c74fSSatoshi Sahara $form->addTagOpen('p'); 1569b960c74fSSatoshi Sahara $form->addTextInput('upload', $lang['txt_upload'])->id('upload__file') 1570b960c74fSSatoshi Sahara ->attrs(['type' => 'file']); 1571b960c74fSSatoshi Sahara $form->addTagClose('p'); 1572b960c74fSSatoshi Sahara $form->addTagOpen('p'); 1573b960c74fSSatoshi Sahara $form->addTextInput('mediaid', $lang['txt_filename'])->id('upload__name') 1574b960c74fSSatoshi Sahara ->val(noNS($id)); 1575b960c74fSSatoshi Sahara $form->addButton('', $lang['btn_upload'])->attr('type', 'submit'); 1576b960c74fSSatoshi Sahara $form->addTagClose('p'); 157792cac9a9SKate Arzamastseva if ($auth >= $auth_ow) { 1578b960c74fSSatoshi Sahara $form->addTagOpen('p'); 157924870174SAndreas Gohr $attrs = []; 158070c3cc9aSKate Arzamastseva if ($update) $attrs['checked'] = 'checked'; 1581b960c74fSSatoshi Sahara $form->addCheckbox('ow', $lang['txt_overwrt'])->id('dw__ow')->val('1') 1582b960c74fSSatoshi Sahara ->addClass('check')->attrs($attrs); 1583b960c74fSSatoshi Sahara $form->addTagClose('p'); 1584b960c74fSSatoshi Sahara } 1585b960c74fSSatoshi Sahara $form->addTagClose('div'); 1586b960c74fSSatoshi Sahara 1587b960c74fSSatoshi Sahara if (!$fullscreen) { 1588b960c74fSSatoshi Sahara echo '<div class="upload">'. $lang['mediaupload'] .'</div>'.DOKU_LF; 1589b960c74fSSatoshi Sahara } else { 1590b960c74fSSatoshi Sahara echo DOKU_LF; 15913df72098SAndreas Gohr } 1592c472bad9SKate Arzamastseva 1593b960c74fSSatoshi Sahara echo '<div id="mediamanager__uploader">'.DOKU_LF; 1594c6977b3aSSatoshi Sahara echo $form->toHTML('Upload'); 1595b960c74fSSatoshi Sahara echo '</div>'.DOKU_LF; 1596f940e4a0SAndreas Gohr 1597f940e4a0SAndreas Gohr echo '<p class="maxsize">'; 1598f940e4a0SAndreas Gohr printf($lang['maxuploadsize'], filesize_h(media_getuploadsize())); 159956fc6b15SAnna Dabrowska echo ' <a class="allowedmime" href="#">'. $lang['allowedmime'] .'</a>'; 1600499d9bcdSAndreas Gohr echo ' <span>'. implode(', ', array_keys(getMimeTypes())) .'</span>'; 1601b960c74fSSatoshi Sahara echo '</p>'.DOKU_LF; 1602f940e4a0SAndreas Gohr} 1603f940e4a0SAndreas Gohr 1604f940e4a0SAndreas Gohr/** 1605f940e4a0SAndreas Gohr * Returns the size uploaded files may have 1606f940e4a0SAndreas Gohr * 1607f940e4a0SAndreas Gohr * This uses a conservative approach using the lowest number found 1608f940e4a0SAndreas Gohr * in any of the limiting ini settings 1609f940e4a0SAndreas Gohr * 1610f940e4a0SAndreas Gohr * @returns int size in bytes 1611f940e4a0SAndreas Gohr */ 1612d868eb89SAndreas Gohrfunction media_getuploadsize() 1613d868eb89SAndreas Gohr{ 1614f940e4a0SAndreas Gohr $okay = 0; 1615f940e4a0SAndreas Gohr 161624870174SAndreas Gohr $post = php_to_byte(@ini_get('post_max_size')); 161724870174SAndreas Gohr $suho = php_to_byte(@ini_get('suhosin.post.max_value_length')); 161824870174SAndreas Gohr $upld = php_to_byte(@ini_get('upload_max_filesize')); 1619f940e4a0SAndreas Gohr 162024870174SAndreas Gohr if ($post && ($post < $okay || $okay === 0)) $okay = $post; 1621f940e4a0SAndreas Gohr if ($suho && ($suho < $okay || $okay == 0)) $okay = $suho; 1622f940e4a0SAndreas Gohr if ($upld && ($upld < $okay || $okay == 0)) $okay = $upld; 1623f940e4a0SAndreas Gohr 1624f940e4a0SAndreas Gohr return $okay; 16259f5dde7fSMichael Klier} 16263df72098SAndreas Gohr 16273df72098SAndreas Gohr/** 1628bf1f3ac4Ssarnowski * Print the search field form 1629bf1f3ac4Ssarnowski * 1630bf1f3ac4Ssarnowski * @author Tobias Sarnowski <sarnowski@cosmocode.de> 1631d9162c6cSKate Arzamastseva * @author Kate Arzamastseva <pshns@ukr.net> 1632e0c26282SGerrit Uitslag * 1633e0c26282SGerrit Uitslag * @param string $ns 1634e0c26282SGerrit Uitslag * @param string $query 1635e0c26282SGerrit Uitslag * @param bool $fullscreen 1636bf1f3ac4Ssarnowski */ 1637d868eb89SAndreas Gohrfunction media_searchform($ns, $query = '', $fullscreen = false) 1638d868eb89SAndreas Gohr{ 1639bf1f3ac4Ssarnowski global $lang; 1640bf1f3ac4Ssarnowski 1641bf1f3ac4Ssarnowski // The default HTML search form 1642b960c74fSSatoshi Sahara $form = new Form([ 1643b960c74fSSatoshi Sahara 'id' => 'dw__mediasearch', 1644b960c74fSSatoshi Sahara 'action' => ($fullscreen) 1645b960c74fSSatoshi Sahara ? media_managerURL([], '&') 1646b960c74fSSatoshi Sahara : DOKU_BASE.'lib/exe/mediamanager.php', 1647b960c74fSSatoshi Sahara ]); 1648b960c74fSSatoshi Sahara $form->addTagOpen('div')->addClass('no'); 1649b960c74fSSatoshi Sahara $form->setHiddenField('ns', $ns); 1650b960c74fSSatoshi Sahara $form->setHiddenField($fullscreen ? 'mediado' : 'do', 'searchlist'); 1651554a8c9fSAdrian Lang 1652b960c74fSSatoshi Sahara $form->addTagOpen('p'); 1653bde2a644SSatoshi Sahara $form->addTextInput('q', $lang['searchmedia']) 1654b960c74fSSatoshi Sahara ->attr('title', sprintf($lang['searchmedia_in'], hsc($ns) .':*')) 1655b960c74fSSatoshi Sahara ->val($query); 1656bde2a644SSatoshi Sahara $form->addHTML(' '); 1657b960c74fSSatoshi Sahara $form->addButton('', $lang['btn_search'])->attr('type', 'submit'); 1658b960c74fSSatoshi Sahara $form->addTagClose('p'); 1659b960c74fSSatoshi Sahara $form->addTagClose('div'); 1660*26dfc232SAndreas Gohr echo $form->toHTML('SearchMedia'); 1661bf1f3ac4Ssarnowski} 1662bf1f3ac4Ssarnowski 1663bf1f3ac4Ssarnowski/** 16643df72098SAndreas Gohr * Build a tree outline of available media namespaces 16653df72098SAndreas Gohr * 16663df72098SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org> 1667e0c26282SGerrit Uitslag * 1668e0c26282SGerrit Uitslag * @param string $ns 16693df72098SAndreas Gohr */ 1670d868eb89SAndreas Gohrfunction media_nstree($ns) 1671d868eb89SAndreas Gohr{ 16723df72098SAndreas Gohr global $conf; 1673256ca81eSAndreas Gohr global $lang; 16743df72098SAndreas Gohr 16753df72098SAndreas Gohr // currently selected namespace 16763df72098SAndreas Gohr $ns = cleanID($ns); 16773df72098SAndreas Gohr if (empty($ns)) { 1678de3eb1d7SAdrian Lang global $ID; 1679dd90013aSMichael Hamann $ns = (string)getNS($ID); 16803df72098SAndreas Gohr } 1681dd90013aSMichael Hamann 1682dd90013aSMichael Hamann $ns_dir = utf8_encodeFN(str_replace(':', '/', $ns)); 16833df72098SAndreas Gohr 168424870174SAndreas Gohr $data = []; 168524870174SAndreas Gohr search($data, $conf['mediadir'], 'search_index', ['ns' => $ns_dir, 'nofiles' => true]); 16863df72098SAndreas Gohr 16873df72098SAndreas Gohr // wrap a list with the root level around the other namespaces 168824870174SAndreas Gohr array_unshift($data, ['level' => 0, 'id' => '', 'open' =>'true', 'label' => '['.$lang['mediaroot'].']']); 16893df72098SAndreas Gohr 1690dd90013aSMichael Hamann // insert the current ns into the hierarchy if it isn't already part of it 1691dd90013aSMichael Hamann $ns_parts = explode(':', $ns); 1692dd90013aSMichael Hamann $tmp_ns = ''; 1693dd90013aSMichael Hamann $pos = 0; 1694dd90013aSMichael Hamann foreach ($ns_parts as $level => $part) { 1695dd90013aSMichael Hamann if ($tmp_ns) $tmp_ns .= ':'.$part; 1696dd90013aSMichael Hamann else $tmp_ns = $part; 1697dd90013aSMichael Hamann 1698dd90013aSMichael Hamann // find the namespace parts or insert them 1699dd90013aSMichael Hamann while ($data[$pos]['id'] != $tmp_ns) { 170064159a61SAndreas Gohr if ( 170164159a61SAndreas Gohr $pos >= count($data) || 17022d85e841SAndreas Gohr ($data[$pos]['level'] <= $level+1 && Sort::strcmp($data[$pos]['id'], $tmp_ns) > 0) 170364159a61SAndreas Gohr ) { 170424870174SAndreas Gohr array_splice($data, $pos, 0, [['level' => $level+1, 'id' => $tmp_ns, 'open' => 'true']]); 1705dd90013aSMichael Hamann break; 1706dd90013aSMichael Hamann } 1707dd90013aSMichael Hamann ++$pos; 1708dd90013aSMichael Hamann } 1709dd90013aSMichael Hamann } 1710dd90013aSMichael Hamann 17113df72098SAndreas Gohr echo html_buildlist($data, 'idx', 'media_nstree_item', 'media_nstree_li'); 17123df72098SAndreas Gohr} 17133df72098SAndreas Gohr 17143df72098SAndreas Gohr/** 17153df72098SAndreas Gohr * Userfunction for html_buildlist 17163df72098SAndreas Gohr * 17173df72098SAndreas Gohr * Prints a media namespace tree item 17183df72098SAndreas Gohr * 17193df72098SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org> 172042ea7f44SGerrit Uitslag * 172142ea7f44SGerrit Uitslag * @param array $item 172242ea7f44SGerrit Uitslag * @return string html 17233df72098SAndreas Gohr */ 1724d868eb89SAndreas Gohrfunction media_nstree_item($item) 1725d868eb89SAndreas Gohr{ 17268108113cSTom N Harris global $INPUT; 17273df72098SAndreas Gohr $pos = strrpos($item['id'], ':'); 17283df72098SAndreas Gohr $label = substr($item['id'], $pos > 0 ? $pos + 1 : 0); 17290e80bb5eSChristopher Smith if (empty($item['label'])) $item['label'] = $label; 17303df72098SAndreas Gohr 17313df72098SAndreas Gohr $ret = ''; 173224870174SAndreas Gohr if ($INPUT->str('do') != 'media') 17333df72098SAndreas Gohr $ret .= '<a href="'.DOKU_BASE.'lib/exe/mediamanager.php?ns='.idfilter($item['id']).'" class="idx_dir">'; 1734b960c74fSSatoshi Sahara else $ret .= '<a href="'.media_managerURL(['ns' => idfilter($item['id'], false), 'tab_files' => 'files']) 173523846a98SKate Arzamastseva .'" class="idx_dir">'; 17363df72098SAndreas Gohr $ret .= $item['label']; 17373df72098SAndreas Gohr $ret .= '</a>'; 17383df72098SAndreas Gohr return $ret; 17393df72098SAndreas Gohr} 17403df72098SAndreas Gohr 17413df72098SAndreas Gohr/** 17423df72098SAndreas Gohr * Userfunction for html_buildlist 17433df72098SAndreas Gohr * 17443df72098SAndreas Gohr * Prints a media namespace tree item opener 17453df72098SAndreas Gohr * 17463df72098SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org> 174742ea7f44SGerrit Uitslag * 174842ea7f44SGerrit Uitslag * @param array $item 174942ea7f44SGerrit Uitslag * @return string html 17503df72098SAndreas Gohr */ 1751d868eb89SAndreas Gohrfunction media_nstree_li($item) 1752d868eb89SAndreas Gohr{ 17533df72098SAndreas Gohr $class='media level'.$item['level']; 17543df72098SAndreas Gohr if ($item['open']) { 17553df72098SAndreas Gohr $class .= ' open'; 17563df72098SAndreas Gohr $img = DOKU_BASE.'lib/images/minus.gif'; 1757e260f93bSAnika Henke $alt = '−'; 17583df72098SAndreas Gohr } else { 17593df72098SAndreas Gohr $class .= ' closed'; 17603df72098SAndreas Gohr $img = DOKU_BASE.'lib/images/plus.gif'; 17617af1b404SAnika Henke $alt = '+'; 17623df72098SAndreas Gohr } 1763231b8c34SPierre Spring // TODO: only deliver an image if it actually has a subtree... 17643df72098SAndreas Gohr return '<li class="'.$class.'">'. 17657af1b404SAnika Henke '<img src="'.$img.'" alt="'.$alt.'" />'; 17663df72098SAndreas Gohr} 176713c08e2fSMichael Klier 176813c08e2fSMichael Klier/** 1769b021f0b4SAndreas Gohr * Resizes or crop the given image to the given size 1770b021f0b4SAndreas Gohr * 1771b021f0b4SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org> 1772b021f0b4SAndreas Gohr * 1773b021f0b4SAndreas Gohr * @param string $file filename, path to file 1774b021f0b4SAndreas Gohr * @param string $ext extension 1775b021f0b4SAndreas Gohr * @param int $w desired width 1776b021f0b4SAndreas Gohr * @param int $h desired height 1777b021f0b4SAndreas Gohr * @param bool $crop should a center crop be used? 1778b021f0b4SAndreas Gohr * @return string path to resized or original size if failed 1779b021f0b4SAndreas Gohr */ 1780b021f0b4SAndreas Gohrfunction media_mod_image($file, $ext, $w, $h = 0, $crop = false) 1781b021f0b4SAndreas Gohr{ 1782b021f0b4SAndreas Gohr global $conf; 1783d967ecd8SAndreas Gohr if (!$h) $h = 0; 1784b021f0b4SAndreas Gohr // we wont scale up to infinity 1785b021f0b4SAndreas Gohr if ($w > 2000 || $h > 2000) return $file; 1786b021f0b4SAndreas Gohr 1787b021f0b4SAndreas Gohr $operation = $crop ? 'crop' : 'resize'; 1788b021f0b4SAndreas Gohr 1789b021f0b4SAndreas Gohr $options = [ 1790b021f0b4SAndreas Gohr 'quality' => $conf['jpg_quality'], 1791b021f0b4SAndreas Gohr 'imconvert' => $conf['im_convert'], 1792b021f0b4SAndreas Gohr ]; 1793b021f0b4SAndreas Gohr 179424870174SAndreas Gohr $cache = new CacheImageMod($file, $w, $h, $ext, $crop); 1795b021f0b4SAndreas Gohr if (!$cache->useCache()) { 1796b021f0b4SAndreas Gohr try { 1797b021f0b4SAndreas Gohr Slika::run($file, $options) 1798b021f0b4SAndreas Gohr ->autorotate() 1799b021f0b4SAndreas Gohr ->$operation($w, $h) 1800b021f0b4SAndreas Gohr ->save($cache->cache, $ext); 1801b021f0b4SAndreas Gohr if ($conf['fperm']) @chmod($cache->cache, $conf['fperm']); 180224870174SAndreas Gohr } catch (Exception $e) { 1803b021f0b4SAndreas Gohr Logger::debug($e->getMessage()); 1804b021f0b4SAndreas Gohr return $file; 1805b021f0b4SAndreas Gohr } 1806b021f0b4SAndreas Gohr } 1807b021f0b4SAndreas Gohr 1808b021f0b4SAndreas Gohr return $cache->cache; 1809b021f0b4SAndreas Gohr} 1810b021f0b4SAndreas Gohr 1811b021f0b4SAndreas Gohr/** 181213c08e2fSMichael Klier * Resizes the given image to the given size 181313c08e2fSMichael Klier * 181413c08e2fSMichael Klier * @author Andreas Gohr <andi@splitbrain.org> 181542ea7f44SGerrit Uitslag * 181642ea7f44SGerrit Uitslag * @param string $file filename, path to file 181742ea7f44SGerrit Uitslag * @param string $ext extension 181842ea7f44SGerrit Uitslag * @param int $w desired width 181942ea7f44SGerrit Uitslag * @param int $h desired height 182042ea7f44SGerrit Uitslag * @return string path to resized or original size if failed 182113c08e2fSMichael Klier */ 1822b021f0b4SAndreas Gohrfunction media_resize_image($file, $ext, $w, $h = 0) 1823b021f0b4SAndreas Gohr{ 1824b021f0b4SAndreas Gohr return media_mod_image($file, $ext, $w, $h, false); 1825d2bd34a5SAndreas Gohr} 182613c08e2fSMichael Klier 182713c08e2fSMichael Klier/** 1828d2bd34a5SAndreas Gohr * Center crops the given image to the wanted size 182913c08e2fSMichael Klier * 183013c08e2fSMichael Klier * @author Andreas Gohr <andi@splitbrain.org> 183142ea7f44SGerrit Uitslag * 183242ea7f44SGerrit Uitslag * @param string $file filename, path to file 183342ea7f44SGerrit Uitslag * @param string $ext extension 183442ea7f44SGerrit Uitslag * @param int $w desired width 183542ea7f44SGerrit Uitslag * @param int $h desired height 183642ea7f44SGerrit Uitslag * @return string path to resized or original size if failed 183713c08e2fSMichael Klier */ 1838b021f0b4SAndreas Gohrfunction media_crop_image($file, $ext, $w, $h = 0) 1839b021f0b4SAndreas Gohr{ 1840b021f0b4SAndreas Gohr return media_mod_image($file, $ext, $w, $h, true); 184113c08e2fSMichael Klier} 184213c08e2fSMichael Klier 184313c08e2fSMichael Klier/** 18440f4e0092SChristopher Smith * Calculate a token to be used to verify fetch requests for resized or 18450f4e0092SChristopher Smith * cropped images have been internally generated - and prevent external 18460f4e0092SChristopher Smith * DDOS attacks via fetch 18470f4e0092SChristopher Smith * 18483c124064SAndreas Gohr * @author Christopher Smith <chris@jalakai.co.uk> 18493c124064SAndreas Gohr * 18500f4e0092SChristopher Smith * @param string $id id of the image 18510f4e0092SChristopher Smith * @param int $w resize/crop width 18520f4e0092SChristopher Smith * @param int $h resize/crop height 185342ea7f44SGerrit Uitslag * @return string token or empty string if no token required 18540f4e0092SChristopher Smith */ 1855d868eb89SAndreas Gohrfunction media_get_token($id, $w, $h) 1856d868eb89SAndreas Gohr{ 18570f4e0092SChristopher Smith // token is only required for modified images 1858cc036f74SKlap-in if ($w || $h || media_isexternal($id)) { 18593c124064SAndreas Gohr $token = $id; 18600f4e0092SChristopher Smith if ($w) $token .= '.'.$w; 18610f4e0092SChristopher Smith if ($h) $token .= '.'.$h; 18620f4e0092SChristopher Smith 186324870174SAndreas Gohr return substr(PassHash::hmac('md5', $token, auth_cookiesalt()), 0, 6); 18640f4e0092SChristopher Smith } 18650f4e0092SChristopher Smith 18660f4e0092SChristopher Smith return ''; 18670f4e0092SChristopher Smith} 18680f4e0092SChristopher Smith 18690f4e0092SChristopher Smith/** 187013c08e2fSMichael Klier * Download a remote file and return local filename 187113c08e2fSMichael Klier * 187213c08e2fSMichael Klier * returns false if download fails. Uses cached file if available and 187313c08e2fSMichael Klier * wanted 187413c08e2fSMichael Klier * 187513c08e2fSMichael Klier * @author Andreas Gohr <andi@splitbrain.org> 187613c08e2fSMichael Klier * @author Pavel Vitis <Pavel.Vitis@seznam.cz> 187742ea7f44SGerrit Uitslag * 187842ea7f44SGerrit Uitslag * @param string $url 187942ea7f44SGerrit Uitslag * @param string $ext extension 188042ea7f44SGerrit Uitslag * @param int $cache cachetime in seconds 188142ea7f44SGerrit Uitslag * @return false|string path to cached file 188213c08e2fSMichael Klier */ 1883d868eb89SAndreas Gohrfunction media_get_from_URL($url, $ext, $cache) 1884d868eb89SAndreas Gohr{ 188513c08e2fSMichael Klier global $conf; 188613c08e2fSMichael Klier 188713c08e2fSMichael Klier // if no cache or fetchsize just redirect 188813c08e2fSMichael Klier if ($cache==0) return false; 188913c08e2fSMichael Klier if (!$conf['fetchsize']) return false; 189013c08e2fSMichael Klier 189113c08e2fSMichael Klier $local = getCacheName(strtolower($url), ".media.$ext"); 189213c08e2fSMichael Klier $mtime = @filemtime($local); // 0 if not exists 189313c08e2fSMichael Klier 189413c08e2fSMichael Klier //decide if download needed: 18957d34963bSAndreas Gohr if ( 18967d34963bSAndreas Gohr ($mtime == 0) || // cache does not exist 189713c08e2fSMichael Klier ($cache != -1 && $mtime < time() - $cache) // 'recache' and cache has expired 189813c08e2fSMichael Klier ) { 189913c08e2fSMichael Klier if (media_image_download($url, $local)) { 190013c08e2fSMichael Klier return $local; 190113c08e2fSMichael Klier } else { 190213c08e2fSMichael Klier return false; 190313c08e2fSMichael Klier } 190413c08e2fSMichael Klier } 190513c08e2fSMichael Klier 190613c08e2fSMichael Klier //if cache exists use it else 190713c08e2fSMichael Klier if ($mtime) return $local; 190813c08e2fSMichael Klier 190913c08e2fSMichael Klier //else return false 191013c08e2fSMichael Klier return false; 191113c08e2fSMichael Klier} 191213c08e2fSMichael Klier 191313c08e2fSMichael Klier/** 191413c08e2fSMichael Klier * Download image files 191513c08e2fSMichael Klier * 191613c08e2fSMichael Klier * @author Andreas Gohr <andi@splitbrain.org> 191742ea7f44SGerrit Uitslag * 191842ea7f44SGerrit Uitslag * @param string $url 191942ea7f44SGerrit Uitslag * @param string $file path to file in which to put the downloaded content 192042ea7f44SGerrit Uitslag * @return bool 192113c08e2fSMichael Klier */ 1922d868eb89SAndreas Gohrfunction media_image_download($url, $file) 1923d868eb89SAndreas Gohr{ 192413c08e2fSMichael Klier global $conf; 192513c08e2fSMichael Klier $http = new DokuHTTPClient(); 1926a5951419SAndreas Gohr $http->keep_alive = false; // we do single ops here, no need for keep-alive 1927a5951419SAndreas Gohr 192813c08e2fSMichael Klier $http->max_bodysize = $conf['fetchsize']; 192913c08e2fSMichael Klier $http->timeout = 25; //max. 25 sec 193013c08e2fSMichael Klier $http->header_regexp = '!\r\nContent-Type: image/(jpe?g|gif|png)!i'; 193113c08e2fSMichael Klier 193213c08e2fSMichael Klier $data = $http->get($url); 193313c08e2fSMichael Klier if (!$data) return false; 193413c08e2fSMichael Klier 193579e79377SAndreas Gohr $fileexists = file_exists($file); 193613c08e2fSMichael Klier $fp = @fopen($file, "w"); 193713c08e2fSMichael Klier if (!$fp) return false; 193813c08e2fSMichael Klier fwrite($fp, $data); 193913c08e2fSMichael Klier fclose($fp); 194024870174SAndreas Gohr if (!$fileexists && $conf['fperm']) chmod($file, $conf['fperm']); 194113c08e2fSMichael Klier 194213c08e2fSMichael Klier // check if it is really an image 194313c08e2fSMichael Klier $info = @getimagesize($file); 194413c08e2fSMichael Klier if (!$info) { 194513c08e2fSMichael Klier @unlink($file); 194613c08e2fSMichael Klier return false; 194713c08e2fSMichael Klier } 194813c08e2fSMichael Klier 194913c08e2fSMichael Klier return true; 195013c08e2fSMichael Klier} 195113c08e2fSMichael Klier 195213c08e2fSMichael Klier/** 195313c08e2fSMichael Klier * resize images using external ImageMagick convert program 195413c08e2fSMichael Klier * 195513c08e2fSMichael Klier * @author Pavel Vitis <Pavel.Vitis@seznam.cz> 195613c08e2fSMichael Klier * @author Andreas Gohr <andi@splitbrain.org> 195742ea7f44SGerrit Uitslag * 195842ea7f44SGerrit Uitslag * @param string $ext extension 195942ea7f44SGerrit Uitslag * @param string $from filename path to file 196042ea7f44SGerrit Uitslag * @param int $from_w original width 196142ea7f44SGerrit Uitslag * @param int $from_h original height 196242ea7f44SGerrit Uitslag * @param string $to path to resized file 196342ea7f44SGerrit Uitslag * @param int $to_w desired width 196442ea7f44SGerrit Uitslag * @param int $to_h desired height 196542ea7f44SGerrit Uitslag * @return bool 196613c08e2fSMichael Klier */ 1967d868eb89SAndreas Gohrfunction media_resize_imageIM($ext, $from, $from_w, $from_h, $to, $to_w, $to_h) 1968d868eb89SAndreas Gohr{ 196913c08e2fSMichael Klier global $conf; 197013c08e2fSMichael Klier 197113c08e2fSMichael Klier // check if convert is configured 197213c08e2fSMichael Klier if (!$conf['im_convert']) return false; 197313c08e2fSMichael Klier 197413c08e2fSMichael Klier // prepare command 197513c08e2fSMichael Klier $cmd = $conf['im_convert']; 197613c08e2fSMichael Klier $cmd .= ' -resize '.$to_w.'x'.$to_h.'!'; 197713c08e2fSMichael Klier if ($ext == 'jpg' || $ext == 'jpeg') { 197813c08e2fSMichael Klier $cmd .= ' -quality '.$conf['jpg_quality']; 197913c08e2fSMichael Klier } 198013c08e2fSMichael Klier $cmd .= " $from $to"; 198113c08e2fSMichael Klier 198213c08e2fSMichael Klier @exec($cmd, $out, $retval); 198313c08e2fSMichael Klier if ($retval == 0) return true; 198413c08e2fSMichael Klier return false; 198513c08e2fSMichael Klier} 198613c08e2fSMichael Klier 198713c08e2fSMichael Klier/** 198813c08e2fSMichael Klier * crop images using external ImageMagick convert program 198913c08e2fSMichael Klier * 199013c08e2fSMichael Klier * @author Andreas Gohr <andi@splitbrain.org> 199142ea7f44SGerrit Uitslag * 199242ea7f44SGerrit Uitslag * @param string $ext extension 199342ea7f44SGerrit Uitslag * @param string $from filename path to file 199442ea7f44SGerrit Uitslag * @param int $from_w original width 199542ea7f44SGerrit Uitslag * @param int $from_h original height 199642ea7f44SGerrit Uitslag * @param string $to path to resized file 199742ea7f44SGerrit Uitslag * @param int $to_w desired width 199842ea7f44SGerrit Uitslag * @param int $to_h desired height 199942ea7f44SGerrit Uitslag * @param int $ofs_x offset of crop centre 200042ea7f44SGerrit Uitslag * @param int $ofs_y offset of crop centre 200142ea7f44SGerrit Uitslag * @return bool 2002d2bd34a5SAndreas Gohr * @deprecated 2020-09-01 200313c08e2fSMichael Klier */ 2004d868eb89SAndreas Gohrfunction media_crop_imageIM($ext, $from, $from_w, $from_h, $to, $to_w, $to_h, $ofs_x, $ofs_y) 2005d868eb89SAndreas Gohr{ 200613c08e2fSMichael Klier global $conf; 2007d2bd34a5SAndreas Gohr dbg_deprecated('splitbrain\\Slika'); 200813c08e2fSMichael Klier 200913c08e2fSMichael Klier // check if convert is configured 201013c08e2fSMichael Klier if (!$conf['im_convert']) return false; 201113c08e2fSMichael Klier 201213c08e2fSMichael Klier // prepare command 201313c08e2fSMichael Klier $cmd = $conf['im_convert']; 201413c08e2fSMichael Klier $cmd .= ' -crop '.$to_w.'x'.$to_h.'+'.$ofs_x.'+'.$ofs_y; 201513c08e2fSMichael Klier if ($ext == 'jpg' || $ext == 'jpeg') { 201613c08e2fSMichael Klier $cmd .= ' -quality '.$conf['jpg_quality']; 201713c08e2fSMichael Klier } 201813c08e2fSMichael Klier $cmd .= " $from $to"; 201913c08e2fSMichael Klier 202013c08e2fSMichael Klier @exec($cmd, $out, $retval); 202113c08e2fSMichael Klier if ($retval == 0) return true; 202213c08e2fSMichael Klier return false; 202313c08e2fSMichael Klier} 202413c08e2fSMichael Klier 202513c08e2fSMichael Klier/** 202613c08e2fSMichael Klier * resize or crop images using PHP's libGD support 202713c08e2fSMichael Klier * 202813c08e2fSMichael Klier * @author Andreas Gohr <andi@splitbrain.org> 202913c08e2fSMichael Klier * @author Sebastian Wienecke <s_wienecke@web.de> 203042ea7f44SGerrit Uitslag * 203142ea7f44SGerrit Uitslag * @param string $ext extension 203242ea7f44SGerrit Uitslag * @param string $from filename path to file 203342ea7f44SGerrit Uitslag * @param int $from_w original width 203442ea7f44SGerrit Uitslag * @param int $from_h original height 203542ea7f44SGerrit Uitslag * @param string $to path to resized file 203642ea7f44SGerrit Uitslag * @param int $to_w desired width 203742ea7f44SGerrit Uitslag * @param int $to_h desired height 203842ea7f44SGerrit Uitslag * @param int $ofs_x offset of crop centre 203942ea7f44SGerrit Uitslag * @param int $ofs_y offset of crop centre 204042ea7f44SGerrit Uitslag * @return bool 2041d2bd34a5SAndreas Gohr * @deprecated 2020-09-01 204213c08e2fSMichael Klier */ 2043d868eb89SAndreas Gohrfunction media_resize_imageGD($ext, $from, $from_w, $from_h, $to, $to_w, $to_h, $ofs_x = 0, $ofs_y = 0) 2044d868eb89SAndreas Gohr{ 204513c08e2fSMichael Klier global $conf; 2046d2bd34a5SAndreas Gohr dbg_deprecated('splitbrain\\Slika'); 204713c08e2fSMichael Klier 204813c08e2fSMichael Klier if ($conf['gdlib'] < 1) return false; //no GDlib available or wanted 204913c08e2fSMichael Klier 205013c08e2fSMichael Klier // check available memory 205113c08e2fSMichael Klier if (!is_mem_available(($from_w * $from_h * 4) + ($to_w * $to_h * 4))) { 205213c08e2fSMichael Klier return false; 205313c08e2fSMichael Klier } 205413c08e2fSMichael Klier 205513c08e2fSMichael Klier // create an image of the given filetype 205659bc3b48SGerrit Uitslag $image = false; 205713c08e2fSMichael Klier if ($ext == 'jpg' || $ext == 'jpeg') { 205813c08e2fSMichael Klier if (!function_exists("imagecreatefromjpeg")) return false; 205913c08e2fSMichael Klier $image = @imagecreatefromjpeg($from); 206013c08e2fSMichael Klier } elseif ($ext == 'png') { 206113c08e2fSMichael Klier if (!function_exists("imagecreatefrompng")) return false; 206213c08e2fSMichael Klier $image = @imagecreatefrompng($from); 206313c08e2fSMichael Klier } elseif ($ext == 'gif') { 206413c08e2fSMichael Klier if (!function_exists("imagecreatefromgif")) return false; 206513c08e2fSMichael Klier $image = @imagecreatefromgif($from); 206613c08e2fSMichael Klier } 206713c08e2fSMichael Klier if (!$image) return false; 206813c08e2fSMichael Klier 206959bc3b48SGerrit Uitslag $newimg = false; 207013c08e2fSMichael Klier if (($conf['gdlib']>1) && function_exists("imagecreatetruecolor") && $ext != 'gif') { 207113c08e2fSMichael Klier $newimg = @imagecreatetruecolor($to_w, $to_h); 207213c08e2fSMichael Klier } 207313c08e2fSMichael Klier if (!$newimg) $newimg = @imagecreate($to_w, $to_h); 207413c08e2fSMichael Klier if (!$newimg) { 207513c08e2fSMichael Klier imagedestroy($image); 207613c08e2fSMichael Klier return false; 207713c08e2fSMichael Klier } 207813c08e2fSMichael Klier 207913c08e2fSMichael Klier //keep png alpha channel if possible 208013c08e2fSMichael Klier if ($ext == 'png' && $conf['gdlib']>1 && function_exists('imagesavealpha')) { 208113c08e2fSMichael Klier imagealphablending($newimg, false); 208213c08e2fSMichael Klier imagesavealpha($newimg, true); 208313c08e2fSMichael Klier } 208413c08e2fSMichael Klier 208513c08e2fSMichael Klier //keep gif transparent color if possible 208613c08e2fSMichael Klier if ($ext == 'gif' && function_exists('imagefill') && function_exists('imagecolorallocate')) { 208713c08e2fSMichael Klier if (function_exists('imagecolorsforindex') && function_exists('imagecolortransparent')) { 208813c08e2fSMichael Klier $transcolorindex = @imagecolortransparent($image); 208913c08e2fSMichael Klier if ($transcolorindex >= 0) { //transparent color exists 209013c08e2fSMichael Klier $transcolor = @imagecolorsforindex($image, $transcolorindex); 209164159a61SAndreas Gohr $transcolorindex = @imagecolorallocate( 209264159a61SAndreas Gohr $newimg, 209364159a61SAndreas Gohr $transcolor['red'], 209464159a61SAndreas Gohr $transcolor['green'], 209564159a61SAndreas Gohr $transcolor['blue'] 209664159a61SAndreas Gohr ); 209713c08e2fSMichael Klier @imagefill($newimg, 0, 0, $transcolorindex); 209813c08e2fSMichael Klier @imagecolortransparent($newimg, $transcolorindex); 209913c08e2fSMichael Klier } else { //filling with white 210013c08e2fSMichael Klier $whitecolorindex = @imagecolorallocate($newimg, 255, 255, 255); 210113c08e2fSMichael Klier @imagefill($newimg, 0, 0, $whitecolorindex); 210213c08e2fSMichael Klier } 210313c08e2fSMichael Klier } else { //filling with white 210413c08e2fSMichael Klier $whitecolorindex = @imagecolorallocate($newimg, 255, 255, 255); 210513c08e2fSMichael Klier @imagefill($newimg, 0, 0, $whitecolorindex); 210613c08e2fSMichael Klier } 210713c08e2fSMichael Klier } 210813c08e2fSMichael Klier 210913c08e2fSMichael Klier //try resampling first 211013c08e2fSMichael Klier if (function_exists("imagecopyresampled")) { 211113c08e2fSMichael Klier if (!@imagecopyresampled($newimg, $image, 0, 0, $ofs_x, $ofs_y, $to_w, $to_h, $from_w, $from_h)) { 211213c08e2fSMichael Klier imagecopyresized($newimg, $image, 0, 0, $ofs_x, $ofs_y, $to_w, $to_h, $from_w, $from_h); 211313c08e2fSMichael Klier } 211413c08e2fSMichael Klier } else { 211513c08e2fSMichael Klier imagecopyresized($newimg, $image, 0, 0, $ofs_x, $ofs_y, $to_w, $to_h, $from_w, $from_h); 211613c08e2fSMichael Klier } 211713c08e2fSMichael Klier 211813c08e2fSMichael Klier $okay = false; 211913c08e2fSMichael Klier if ($ext == 'jpg' || $ext == 'jpeg') { 212013c08e2fSMichael Klier if (!function_exists('imagejpeg')) { 212113c08e2fSMichael Klier $okay = false; 212213c08e2fSMichael Klier } else { 212313c08e2fSMichael Klier $okay = imagejpeg($newimg, $to, $conf['jpg_quality']); 212413c08e2fSMichael Klier } 212513c08e2fSMichael Klier } elseif ($ext == 'png') { 212613c08e2fSMichael Klier if (!function_exists('imagepng')) { 212713c08e2fSMichael Klier $okay = false; 212813c08e2fSMichael Klier } else { 212913c08e2fSMichael Klier $okay = imagepng($newimg, $to); 213013c08e2fSMichael Klier } 213113c08e2fSMichael Klier } elseif ($ext == 'gif') { 213213c08e2fSMichael Klier if (!function_exists('imagegif')) { 213313c08e2fSMichael Klier $okay = false; 213413c08e2fSMichael Klier } else { 213513c08e2fSMichael Klier $okay = imagegif($newimg, $to); 213613c08e2fSMichael Klier } 213713c08e2fSMichael Klier } 213813c08e2fSMichael Klier 213979a2d784SGerrit Uitslag // destroy GD image resources 214079a2d784SGerrit Uitslag imagedestroy($image); 214179a2d784SGerrit Uitslag imagedestroy($newimg); 214213c08e2fSMichael Klier 214313c08e2fSMichael Klier return $okay; 214413c08e2fSMichael Klier} 214513c08e2fSMichael Klier 21463d7a9e0aSAnika Henke/** 21473d7a9e0aSAnika Henke * Return other media files with the same base name 214879e53fe5SAnika Henke * but different extensions. 21493d7a9e0aSAnika Henke * 21503d7a9e0aSAnika Henke * @param string $src - ID of media file 215142ea7f44SGerrit Uitslag * @param string[] $exts - alternative extensions to find other files for 215242ea7f44SGerrit Uitslag * @return array - array(mime type => file ID) 21533d7a9e0aSAnika Henke * 21543d7a9e0aSAnika Henke * @author Anika Henke <anika@selfthinker.org> 21553d7a9e0aSAnika Henke */ 2156d868eb89SAndreas Gohrfunction media_alternativefiles($src, $exts) 2157d868eb89SAndreas Gohr{ 21583d7a9e0aSAnika Henke 215924870174SAndreas Gohr $files = []; 2160a19c9aa0SGerrit Uitslag [$srcExt, /* srcMime */] = mimetype($src); 21613d7a9e0aSAnika Henke $filebase = substr($src, 0, -1 * (strlen($srcExt)+1)); 21623d7a9e0aSAnika Henke 21633d7a9e0aSAnika Henke foreach ($exts as $ext) { 21643d7a9e0aSAnika Henke $fileid = $filebase.'.'.$ext; 21653d7a9e0aSAnika Henke $file = mediaFN($fileid); 21663d7a9e0aSAnika Henke if (file_exists($file)) { 2167a19c9aa0SGerrit Uitslag [/* fileExt */, $fileMime] = mimetype($file); 216879e53fe5SAnika Henke $files[$fileMime] = $fileid; 216999f943f6SAnika Henke } 21703d7a9e0aSAnika Henke } 21713d7a9e0aSAnika Henke return $files; 21723d7a9e0aSAnika Henke} 21733d7a9e0aSAnika Henke 2174f50634f0SAnika Henke/** 2175f50634f0SAnika Henke * Check if video/audio is supported to be embedded. 2176f50634f0SAnika Henke * 2177b83a74f1SAnika Henke * @param string $mime - mimetype of media file 217842ea7f44SGerrit Uitslag * @param string $type - type of media files to check ('video', 'audio', or null for all) 2179f50634f0SAnika Henke * @return boolean 2180f50634f0SAnika Henke * 2181f50634f0SAnika Henke * @author Anika Henke <anika@selfthinker.org> 2182f50634f0SAnika Henke */ 2183ed823bcdSAndreas Gohrfunction media_supportedav($mime, $type = null) 2184d868eb89SAndreas Gohr{ 218524870174SAndreas Gohr $supportedAudio = [ 2186f50634f0SAnika Henke 'ogg' => 'audio/ogg', 2187f50634f0SAnika Henke 'mp3' => 'audio/mpeg', 218824870174SAndreas Gohr 'wav' => 'audio/wav' 218924870174SAndreas Gohr ]; 219024870174SAndreas Gohr $supportedVideo = [ 2191f50634f0SAnika Henke 'webm' => 'video/webm', 2192f50634f0SAnika Henke 'ogv' => 'video/ogg', 219324870174SAndreas Gohr 'mp4' => 'video/mp4' 219424870174SAndreas Gohr ]; 2195f50634f0SAnika Henke if ($type == 'audio') { 2196f50634f0SAnika Henke $supportedAv = $supportedAudio; 2197f50634f0SAnika Henke } elseif ($type == 'video') { 2198f50634f0SAnika Henke $supportedAv = $supportedVideo; 2199f50634f0SAnika Henke } else { 2200f50634f0SAnika Henke $supportedAv = array_merge($supportedAudio, $supportedVideo); 2201f50634f0SAnika Henke } 2202f50634f0SAnika Henke return in_array($mime, $supportedAv); 2203f50634f0SAnika Henke} 2204f50634f0SAnika Henke 22050877a1f1SSchplurtz le Déboulonné/** 22060877a1f1SSchplurtz le Déboulonné * Return track media files with the same base name 22070877a1f1SSchplurtz le Déboulonné * but extensions that indicate kind and lang. 22080877a1f1SSchplurtz le Déboulonné * ie for foo.webm search foo.sub.lang.vtt, foo.cap.lang.vtt... 22090877a1f1SSchplurtz le Déboulonné * 22100877a1f1SSchplurtz le Déboulonné * @param string $src - ID of media file 221101299338SSchplurtz le Déboulonné * @return array - array(mediaID => array( kind, srclang )) 22120877a1f1SSchplurtz le Déboulonné * 22130877a1f1SSchplurtz le Déboulonné * @author Schplurtz le Déboulonné <Schplurtz@laposte.net> 22140877a1f1SSchplurtz le Déboulonné */ 2215d868eb89SAndreas Gohrfunction media_trackfiles($src) 2216d868eb89SAndreas Gohr{ 221724870174SAndreas Gohr $kinds=[ 22180877a1f1SSchplurtz le Déboulonné 'sub' => 'subtitles', 22190877a1f1SSchplurtz le Déboulonné 'cap' => 'captions', 22200877a1f1SSchplurtz le Déboulonné 'des' => 'descriptions', 22210877a1f1SSchplurtz le Déboulonné 'cha' => 'chapters', 22220877a1f1SSchplurtz le Déboulonné 'met' => 'metadata' 222324870174SAndreas Gohr ]; 22240877a1f1SSchplurtz le Déboulonné 222524870174SAndreas Gohr $files = []; 22260877a1f1SSchplurtz le Déboulonné $re='/\\.(sub|cap|des|cha|met)\\.([^.]+)\\.vtt$/'; 22270877a1f1SSchplurtz le Déboulonné $baseid=pathinfo($src, PATHINFO_FILENAME); 22280877a1f1SSchplurtz le Déboulonné $pattern=mediaFN($baseid).'.*.*.vtt'; 22290877a1f1SSchplurtz le Déboulonné $list=glob($pattern); 22300877a1f1SSchplurtz le Déboulonné foreach ($list as $track) { 22310877a1f1SSchplurtz le Déboulonné if (preg_match($re, $track, $matches)) { 223224870174SAndreas Gohr $files[$baseid.'.'.$matches[1].'.'.$matches[2].'.vtt']=[$kinds[$matches[1]], $matches[2]]; 22330877a1f1SSchplurtz le Déboulonné } 22340877a1f1SSchplurtz le Déboulonné } 22350877a1f1SSchplurtz le Déboulonné return $files; 22360877a1f1SSchplurtz le Déboulonné} 22370877a1f1SSchplurtz le Déboulonné 2238365be586SAndreas Gohr/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ 2239