3 * All output and handler function needed for the media management popup
5 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
6 * @author Andreas Gohr <andi@splitbrain.org>
9 use dokuwiki\ChangeLog\MediaChangeLog
;
10 use dokuwiki\HTTP\DokuHTTPClient
;
11 use dokuwiki\Subscriptions\MediaSubscriptionSender
;
12 use dokuwiki\Extension\Event
;
15 * Lists pages which currently use a media file selected for deletion
17 * References uses the same visual as search results and share
18 * their CSS tags except pagenames won't be links.
20 * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net>
25 function media_filesinuse($data,$id){
27 echo '<h1>'.$lang['reference'].' <code>'.hsc(noNS($id)).'</code></h1>';
28 echo '<p>'.hsc($lang['ref_inuse']).'</p>';
30 $hidden=0; //count of hits without read permission
31 foreach($data as $row){
32 if(auth_quickaclcheck($row) >= AUTH_READ
&& isVisiblePage($row)){
33 echo '<div class="search_result">';
34 echo '<span class="mediaref_ref">'.hsc($row).'</span>';
40 print '<div class="mediaref_hidden">'.$lang['ref_hidden'].'</div>';
45 * Handles the saving of image meta data
47 * @author Andreas Gohr <andi@splitbrain.org>
48 * @author Kate Arzamastseva <pshns@ukr.net>
50 * @param string $id media id
51 * @param int $auth permission level
53 * @return false|string
55 function media_metasave($id,$auth,$data){
56 if($auth < AUTH_UPLOAD
) return false;
57 if(!checkSecurityToken()) return false;
62 $meta = new JpegMeta($src);
65 foreach($data as $key => $val){
68 $meta->deleteField($key);
70 $meta->setField($key,$val);
74 $old = @filemtime
($src);
75 if(!file_exists(mediaFN($id, $old)) && file_exists($src)) {
76 // add old revision to the attic
77 media_saveOldRevision($id);
79 $filesize_old = filesize($src);
81 if($conf['fperm']) chmod($src, $conf['fperm']);
82 @clearstatcache
(true, $src);
83 $new = @filemtime
($src);
84 $filesize_new = filesize($src);
85 $sizechange = $filesize_new - $filesize_old;
87 // add a log entry to the media changelog
88 addMediaLogEntry($new, $id, DOKU_CHANGE_TYPE_EDIT
, $lang['media_meta_edited'], '', null, $sizechange);
90 msg($lang['metasaveok'],1);
93 msg($lang['metasaveerr'],-1);
99 * check if a media is external source
101 * @author Gerrit Uitslag <klapinklapin@gmail.com>
103 * @param string $id the media ID or URL
106 function media_isexternal($id){
107 if (preg_match('#^(?:https?|ftp)://#i', $id)) return true;
112 * Check if a media item is public (eg, external URL or readable by @ALL)
114 * @author Andreas Gohr <andi@splitbrain.org>
116 * @param string $id the media ID or URL
119 function media_ispublic($id){
120 if(media_isexternal($id)) return true;
122 if(auth_aclcheck(getNS($id).':*', '', array()) >= AUTH_READ
) return true;
127 * Display the form to edit image meta data
129 * @author Andreas Gohr <andi@splitbrain.org>
130 * @author Kate Arzamastseva <pshns@ukr.net>
132 * @param string $id media id
133 * @param int $auth permission level
136 function media_metaform($id,$auth){
139 if($auth < AUTH_UPLOAD
) {
140 echo '<div class="nothing">'.$lang['media_perm_upload'].'</div>'.NL
;
144 // load the field descriptions
145 static $fields = null;
146 if(is_null($fields)){
147 $config_files = getConfigFiles('mediameta');
148 foreach ($config_files as $config_file) {
149 if(file_exists($config_file)) include($config_file);
156 $form = new Doku_Form(array('action' => media_managerURL(array('tab_details' => 'view'), '&'),
158 $form->addHidden('img', $id);
159 $form->addHidden('mediado', 'save');
160 foreach($fields as $key => $field){
162 if (empty($field[0])) continue;
163 $tags = array($field[0]);
164 if(is_array($field[3])) $tags = array_merge($tags,$field[3]);
165 $value = tpl_img_getTag($tags,'',$src);
166 $value = cleanText($value);
168 // prepare attributes
170 $p['class'] = 'edit';
171 $p['id'] = 'meta__'.$key;
172 $p['name'] = 'meta['.$field[0].']';
173 $p_attrs = array('class' => 'edit');
175 $form->addElement('<div class="row">');
176 if($field[2] == 'text'){
182 ($lang[$field[1]]) ?
$lang[$field[1]] : $field[1] . ':',
189 $att = buildAttributes($p);
190 $form->addElement('<label for="meta__'.$key.'">'.$lang[$field[1]].'</label>');
191 $form->addElement("<textarea $att rows=\"6\" cols=\"50\">".formText($value).'</textarea>');
193 $form->addElement('</div>'.NL
);
195 $form->addElement('<div class="buttons">');
201 array('accesskey' => 's', 'name' => 'mediado[save]')
204 $form->addElement('</div>'.NL
);
211 * Convenience function to check if a media file is still in use
213 * @author Michael Klier <chi@chimeric.de>
215 * @param string $id media id
218 function media_inuse($id) {
221 if($conf['refcheck']){
222 $mediareferences = ft_mediause($id,true);
223 if(!count($mediareferences)) {
226 return $mediareferences;
233 define('DOKU_MEDIA_DELETED', 1);
234 define('DOKU_MEDIA_NOT_AUTH', 2);
235 define('DOKU_MEDIA_INUSE', 4);
236 define('DOKU_MEDIA_EMPTY_NS', 8);
239 * Handles media file deletions
241 * If configured, checks for media references before deletion
243 * @author Andreas Gohr <andi@splitbrain.org>
245 * @param string $id media id
246 * @param int $auth no longer used
247 * @return int One of: 0,
248 * DOKU_MEDIA_DELETED,
249 * DOKU_MEDIA_DELETED | DOKU_MEDIA_EMPTY_NS,
250 * DOKU_MEDIA_NOT_AUTH,
253 function media_delete($id,$auth){
255 $auth = auth_quickaclcheck(ltrim(getNS($id).':*', ':'));
256 if($auth < AUTH_DELETE
) return DOKU_MEDIA_NOT_AUTH
;
257 if(media_inuse($id)) return DOKU_MEDIA_INUSE
;
259 $file = mediaFN($id);
261 // trigger an event - MEDIA_DELETE_FILE
264 $data['name'] = \dokuwiki\Utf8\PhpString
::basename($file);
265 $data['path'] = $file;
266 $data['size'] = (file_exists($file)) ?
filesize($file) : 0;
268 $data['unl'] = false;
269 $data['del'] = false;
270 $evt = new Event('MEDIA_DELETE_FILE',$data);
271 if ($evt->advise_before()) {
272 $old = @filemtime
($file);
273 if(!file_exists(mediaFN($id, $old)) && file_exists($file)) {
274 // add old revision to the attic
275 media_saveOldRevision($id);
278 $data['unl'] = @unlink
($file);
280 $sizechange = 0 - $data['size'];
281 addMediaLogEntry(time(), $id, DOKU_CHANGE_TYPE_DELETE
, $lang['deleted'], '', null, $sizechange);
283 $data['del'] = io_sweepNS($id, 'mediadir');
286 $evt->advise_after();
289 if($data['unl'] && $data['del']){
290 return DOKU_MEDIA_DELETED | DOKU_MEDIA_EMPTY_NS
;
293 return $data['unl'] ? DOKU_MEDIA_DELETED
: 0;
297 * Handle file uploads via XMLHttpRequest
299 * @param string $ns target namespace
300 * @param int $auth current auth check result
301 * @return false|string false on error, id of the new file on success
303 function media_upload_xhr($ns,$auth){
304 if(!checkSecurityToken()) return false;
307 $id = $INPUT->get
->str('qqfile');
308 list($ext,$mime) = mimetype($id);
309 $input = fopen("php://input", "r");
310 if (!($tmp = io_mktmpdir())) return false;
311 $path = $tmp.'/'.md5($id);
312 $target = fopen($path, "w");
313 $realSize = stream_copy_to_stream($input, $target);
316 if (isset($_SERVER["CONTENT_LENGTH"]) && ($realSize != (int)$_SERVER["CONTENT_LENGTH"])){
322 array('name' => $path,
326 (($INPUT->get
->str('ow') == 'true') ?
true : false),
331 if ($tmp) io_rmdir($tmp, true);
332 if (is_array($res)) {
333 msg($res[0], $res[1]);
340 * Handles media file uploads
342 * @author Andreas Gohr <andi@splitbrain.org>
343 * @author Michael Klier <chi@chimeric.de>
345 * @param string $ns target namespace
346 * @param int $auth current auth check result
347 * @param bool|array $file $_FILES member, $_FILES['upload'] if false
348 * @return false|string false on error, id of the new file on success
350 function media_upload($ns,$auth,$file=false){
351 if(!checkSecurityToken()) return false;
356 $id = $INPUT->post
->str('mediaid');
357 if (!$file) $file = $_FILES['upload'];
358 if(empty($id)) $id = $file['name'];
360 // check for errors (messages are done in lib/exe/mediamanager.php)
361 if($file['error']) return false;
364 list($fext,$fmime) = mimetype($file['name']);
365 list($iext,$imime) = mimetype($id);
367 // no extension specified in id - read original one
370 }elseif($fext && $fext != $iext){
371 // extension was changed, print warning
372 msg(sprintf($lang['mediaextchange'],$fext,$iext));
375 $res = media_save(array('name' => $file['tmp_name'],
377 'ext' => $iext), $ns.':'.$id,
378 $INPUT->post
->bool('ow'), $auth, 'copy_uploaded_file');
379 if (is_array($res)) {
380 msg($res[0], $res[1]);
387 * An alternative to move_uploaded_file that copies
389 * Using copy, makes sure any setgid bits on the media directory are honored
391 * @see move_uploaded_file()
393 * @param string $from
397 function copy_uploaded_file($from, $to){
398 if(!is_uploaded_file($from)) return false;
399 $ok = copy($from, $to);
405 * This generates an action event and delegates to _media_upload_action().
406 * Action plugins are allowed to pre/postprocess the uploaded file.
407 * (The triggered event is preventable.)
410 * $data[0] fn_tmp: the temporary file name (read from $_FILES)
411 * $data[1] fn: the file name of the uploaded file
412 * $data[2] id: the future directory id of the uploaded file
413 * $data[3] imime: the mimetype of the uploaded file
414 * $data[4] overwrite: if an existing file is going to be overwritten
415 * $data[5] move: name of function that performs move/copy/..
417 * @triggers MEDIA_UPLOAD_FINISH
420 * @param string $id media id
421 * @param bool $ow overwrite?
422 * @param int $auth permission level
423 * @param string $move name of functions that performs move/copy/..
424 * @return false|array|string
426 function media_save($file, $id, $ow, $auth, $move) {
427 if($auth < AUTH_UPLOAD
) {
428 return array("You don't have permissions to upload files.", -1);
431 if (!isset($file['mime']) ||
!isset($file['ext'])) {
432 list($ext, $mime) = mimetype($id);
433 if (!isset($file['mime'])) {
434 $file['mime'] = $mime;
436 if (!isset($file['ext'])) {
447 // get filetype regexp
448 $types = array_keys(getMimeTypes());
451 return preg_quote($q, "/");
455 $regex = join('|',$types);
457 // because a temp file was created already
458 if(!preg_match('/\.('.$regex.')$/i',$fn)) {
459 return array($lang['uploadwrong'],-1);
462 //check for overwrite
463 $overwrite = file_exists($fn);
464 $auth_ow = (($conf['mediarevisions']) ? AUTH_UPLOAD
: AUTH_DELETE
);
465 if($overwrite && (!$ow ||
$auth < $auth_ow)) {
466 return array($lang['uploadexist'], 0);
468 // check for valid content
469 $ok = media_contentcheck($file['name'], $file['mime']);
471 return array(sprintf($lang['uploadbadcontent'],'.' . $file['ext']),-1);
473 return array($lang['uploadspam'],-1);
475 return array($lang['uploadxss'],-1);
478 // prepare event data
480 $data[0] = $file['name'];
483 $data[3] = $file['mime'];
484 $data[4] = $overwrite;
488 return Event
::createAndTrigger('MEDIA_UPLOAD_FINISH', $data, '_media_upload_action', true);
492 * Callback adapter for media_upload_finish() triggered by MEDIA_UPLOAD_FINISH
494 * @author Michael Klier <chi@chimeric.de>
496 * @param array $data event data
497 * @return false|array|string
499 function _media_upload_action($data) {
500 // fixme do further sanity tests of given data?
501 if(is_array($data) && count($data)===6) {
502 return media_upload_finish($data[0], $data[1], $data[2], $data[3], $data[4], $data[5]);
504 return false; //callback error
509 * Saves an uploaded media file
511 * @author Andreas Gohr <andi@splitbrain.org>
512 * @author Michael Klier <chi@chimeric.de>
513 * @author Kate Arzamastseva <pshns@ukr.net>
515 * @param string $fn_tmp
517 * @param string $id media id
518 * @param string $imime mime type
519 * @param bool $overwrite overwrite existing?
520 * @param string $move function name
521 * @return array|string
523 function media_upload_finish($fn_tmp, $fn, $id, $imime, $overwrite, $move = 'move_uploaded_file') {
528 $old = @filemtime
($fn);
529 if(!file_exists(mediaFN($id, $old)) && file_exists($fn)) {
530 // add old revision to the attic if missing
531 media_saveOldRevision($id);
535 io_createNamespace($id, 'media');
537 $filesize_old = file_exists($fn) ?
filesize($fn) : 0;
539 if($move($fn_tmp, $fn)) {
540 @clearstatcache
(true,$fn);
541 $new = @filemtime
($fn);
542 // Set the correct permission here.
543 // Always chmod media because they may be saved with different permissions than expected from the php umask.
544 // (Should normally chmod to $conf['fperm'] only if $conf['fperm'] is set.)
545 chmod($fn, $conf['fmode']);
546 msg($lang['uploadsucc'],1);
547 media_notify($id,$fn,$imime,$old);
548 // add a log entry to the media changelog
549 $filesize_new = filesize($fn);
550 $sizechange = $filesize_new - $filesize_old;
555 DOKU_CHANGE_TYPE_REVERT
,
556 sprintf($lang['restored'], dformat($REV)),
561 } elseif($overwrite) {
562 addMediaLogEntry($new, $id, DOKU_CHANGE_TYPE_EDIT
, '', '', null, $sizechange);
564 addMediaLogEntry($new, $id, DOKU_CHANGE_TYPE_CREATE
, $lang['created'], '', null, $sizechange);
568 return array($lang['uploadfail'],-1);
573 * Moves the current version of media file to the media_attic
576 * @author Kate Arzamastseva <pshns@ukr.net>
579 * @return int - revision date
581 function media_saveOldRevision($id){
584 $oldf = mediaFN($id);
585 if(!file_exists($oldf)) return '';
586 $date = filemtime($oldf);
587 if (!$conf['mediarevisions']) return $date;
589 $medialog = new MediaChangeLog($id);
590 if (!$medialog->getRevisionInfo($date)) {
591 // there was an external edit,
592 // there is no log entry for current version of file
593 $sizechange = filesize($oldf);
594 if(!file_exists(mediaMetaFN($id, '.changes'))) {
595 addMediaLogEntry($date, $id, DOKU_CHANGE_TYPE_CREATE
, $lang['created'], '', null, $sizechange);
597 $oldRev = $medialog->getRevisions(-1, 1); // from changelog
598 $oldRev = (int) (empty($oldRev) ?
0 : $oldRev[0]);
599 $filesize_old = filesize(mediaFN($id, $oldRev));
600 $sizechange = $sizechange - $filesize_old;
602 addMediaLogEntry($date, $id, DOKU_CHANGE_TYPE_EDIT
, '', '', null, $sizechange);
606 $newf = mediaFN($id,$date);
607 io_makeFileDir($newf);
608 if(copy($oldf, $newf)) {
609 // Set the correct permission here.
610 // Always chmod media because they may be saved with different permissions than expected from the php umask.
611 // (Should normally chmod to $conf['fperm'] only if $conf['fperm'] is set.)
612 chmod($newf, $conf['fmode']);
618 * This function checks if the uploaded content is really what the
619 * mimetype says it is. We also do spam checking for text types here.
621 * We need to do this stuff because we can not rely on the browser
622 * to do this check correctly. Yes, IE is broken as usual.
624 * @author Andreas Gohr <andi@splitbrain.org>
625 * @link http://www.splitbrain.org/blog/2007-02/12-internet_explorer_facilitates_cross_site_scripting
626 * @fixme check all 26 magic IE filetypes here?
628 * @param string $file path to file
629 * @param string $mime mimetype
632 function media_contentcheck($file,$mime){
634 if($conf['iexssprotect']){
635 $fh = @fopen
($file, 'rb');
637 $bytes = fread($fh, 256);
639 if(preg_match('/<(script|a|img|html|body|iframe)[\s>]/i',$bytes)){
640 return -3; //XSS: possibly malicious content
644 if(substr($mime,0,6) == 'image/'){
645 $info = @getimagesize
($file);
646 if($mime == 'image/gif' && $info[2] != 1){
647 return -1; // uploaded content did not match the file extension
648 }elseif($mime == 'image/jpeg' && $info[2] != 2){
650 }elseif($mime == 'image/png' && $info[2] != 3){
653 # fixme maybe check other images types as well
654 }elseif(substr($mime,0,5) == 'text/'){
656 $TEXT = io_readFile($file);
657 if(checkwordblock()){
658 return -2; //blocked by the spam blacklist
665 * Send a notify mail on uploads
667 * @author Andreas Gohr <andi@splitbrain.org>
669 * @param string $id media id
670 * @param string $file path to file
671 * @param string $mime mime type
672 * @param bool|int $old_rev revision timestamp or false
675 function media_notify($id,$file,$mime,$old_rev=false){
677 if(empty($conf['notify'])) return false; //notify enabled?
679 $subscription = new MediaSubscriptionSender();
680 return $subscription->sendMediaDiff($conf['notify'], 'uploadmail', $id, $old_rev);
684 * List all files in a given Media namespace
686 * @param string $ns namespace
687 * @param null|int $auth permission level
688 * @param string $jump id
689 * @param bool $fullscreenview
690 * @param bool|string $sort sorting order, false skips sorting
692 function media_filelist($ns,$auth=null,$jump='',$fullscreenview=false,$sort=false){
697 // check auth our self if not given (needed for ajax calls)
698 if(is_null($auth)) $auth = auth_quickaclcheck("$ns:*");
700 if (!$fullscreenview) echo '<h1 id="media__ns">:'.hsc($ns).'</h1>'.NL
;
702 if($auth < AUTH_READ
){
703 // FIXME: print permission warning here instead?
704 echo '<div class="nothing">'.$lang['nothingfound'].'</div>'.NL
;
706 if (!$fullscreenview) {
707 media_uploadform($ns, $auth);
708 media_searchform($ns);
711 $dir = utf8_encodeFN(str_replace(':','/',$ns));
713 search($data,$conf['mediadir'],'search_media',
714 array('showmsg'=>true,'depth'=>1),$dir,1,$sort);
717 echo '<div class="nothing">'.$lang['nothingfound'].'</div>'.NL
;
719 if ($fullscreenview) {
720 echo '<ul class="' . _media_get_list_type() . '">';
722 foreach($data as $item){
723 if (!$fullscreenview) {
724 media_printfile($item,$auth,$jump);
726 media_printfile_thumbs($item,$auth,$jump);
729 if ($fullscreenview) echo '</ul>'.NL
;
735 * Prints tabs for files list actions
737 * @author Kate Arzamastseva <pshns@ukr.net>
738 * @author Adrian Lang <mail@adrianlang.de>
740 * @param string $selected_tab - opened tab
743 function media_tabs_files($selected_tab = ''){
746 foreach(array('files' => 'mediaselect',
747 'upload' => 'media_uploadtab',
748 'search' => 'media_searchtab') as $tab => $caption) {
749 $tabs[$tab] = array('href' => media_managerURL(array('tab_files' => $tab), '&'),
750 'caption' => $lang[$caption]);
753 html_tabs($tabs, $selected_tab);
757 * Prints tabs for files details actions
759 * @author Kate Arzamastseva <pshns@ukr.net>
760 * @param string $image filename of the current image
761 * @param string $selected_tab opened tab
763 function media_tabs_details($image, $selected_tab = ''){
767 $tabs['view'] = array('href' => media_managerURL(array('tab_details' => 'view'), '&'),
768 'caption' => $lang['media_viewtab']);
770 list(, $mime) = mimetype($image);
771 if ($mime == 'image/jpeg' && file_exists(mediaFN($image))) {
772 $tabs['edit'] = array('href' => media_managerURL(array('tab_details' => 'edit'), '&'),
773 'caption' => $lang['media_edittab']);
775 if ($conf['mediarevisions']) {
776 $tabs['history'] = array('href' => media_managerURL(array('tab_details' => 'history'), '&'),
777 'caption' => $lang['media_historytab']);
780 html_tabs($tabs, $selected_tab);
784 * Prints options for the tab that displays a list of all files
786 * @author Kate Arzamastseva <pshns@ukr.net>
788 function media_tab_files_options(){
792 $form = new Doku_Form(array('class' => 'options', 'method' => 'get',
793 'action' => wl($ID)));
794 $media_manager_params = media_managerURL(array(), '', false, true);
795 foreach($media_manager_params as $pKey => $pVal){
796 $form->addHidden($pKey, $pVal);
798 $form->addHidden('sectok', null);
799 if ($INPUT->has('q')) {
800 $form->addHidden('q', $INPUT->str('q'));
802 $form->addElement('<ul>'.NL
);
803 foreach(array('list' => array('listType', array('thumbs', 'rows')),
804 'sort' => array('sortBy', array('name', 'date')))
805 as $group => $content) {
806 $checked = "_media_get_${group}_type";
807 $checked = $checked();
809 $form->addElement('<li class="' . $content[0] . '">');
810 foreach($content[1] as $option) {
812 if ($checked == $option) {
813 $attrs['checked'] = 'checked';
815 $form->addElement(form_makeRadioField($group . '_dwmedia', $option,
816 $lang['media_' . $group . '_' . $option],
817 $content[0] . '__' . $option,
820 $form->addElement('</li>'.NL
);
822 $form->addElement('<li>');
823 $form->addElement(form_makeButton('submit', '', $lang['btn_apply']));
824 $form->addElement('</li>'.NL
);
825 $form->addElement('</ul>'.NL
);
830 * Returns type of sorting for the list of files in media manager
832 * @author Kate Arzamastseva <pshns@ukr.net>
834 * @return string - sort type
836 function _media_get_sort_type() {
837 return _media_get_display_param('sort', array('default' => 'name', 'date'));
841 * Returns type of listing for the list of files in media manager
843 * @author Kate Arzamastseva <pshns@ukr.net>
845 * @return string - list type
847 function _media_get_list_type() {
848 return _media_get_display_param('list', array('default' => 'thumbs', 'rows'));
852 * Get display parameters
854 * @param string $param name of parameter
855 * @param array $values allowed values, where default value has index key 'default'
856 * @return string the parameter value
858 function _media_get_display_param($param, $values) {
860 if (in_array($INPUT->str($param), $values)) {
862 return $INPUT->str($param);
864 $val = get_doku_pref($param, $values['default']);
865 if (!in_array($val, $values)) {
866 $val = $values['default'];
873 * Prints tab that displays a list of all files
875 * @author Kate Arzamastseva <pshns@ukr.net>
878 * @param null|int $auth permission level
879 * @param string $jump item id
881 function media_tab_files($ns,$auth=null,$jump='') {
883 if(is_null($auth)) $auth = auth_quickaclcheck("$ns:*");
885 if($auth < AUTH_READ
){
886 echo '<div class="nothing">'.$lang['media_perm_read'].'</div>'.NL
;
888 media_filelist($ns,$auth,$jump,true,_media_get_sort_type());
893 * Prints tab that displays uploading form
895 * @author Kate Arzamastseva <pshns@ukr.net>
898 * @param null|int $auth permission level
899 * @param string $jump item id
901 function media_tab_upload($ns,$auth=null,$jump='') {
903 if(is_null($auth)) $auth = auth_quickaclcheck("$ns:*");
905 echo '<div class="upload">'.NL
;
906 if ($auth >= AUTH_UPLOAD
) {
907 echo '<p>' . $lang['mediaupload'] . '</p>';
909 media_uploadform($ns, $auth, true);
914 * Prints tab that displays search form
916 * @author Kate Arzamastseva <pshns@ukr.net>
919 * @param null|int $auth permission level
921 function media_tab_search($ns,$auth=null) {
924 $do = $INPUT->str('mediado');
925 $query = $INPUT->str('q');
926 echo '<div class="search">'.NL
;
928 media_searchform($ns, $query, true);
929 if ($do == 'searchlist' ||
$query) {
930 media_searchlist($query,$ns,$auth,true,_media_get_sort_type());
936 * Prints tab that displays mediafile details
938 * @author Kate Arzamastseva <pshns@ukr.net>
940 * @param string $image media id
942 * @param null|int $auth permission level
943 * @param string|int $rev revision timestamp or empty string
945 function media_tab_view($image, $ns, $auth=null, $rev='') {
947 if(is_null($auth)) $auth = auth_quickaclcheck("$ns:*");
949 if ($image && $auth >= AUTH_READ
) {
950 $meta = new JpegMeta(mediaFN($image, $rev));
951 media_preview($image, $auth, $rev, $meta);
952 media_preview_buttons($image, $auth, $rev);
953 media_details($image, $auth, $rev, $meta);
956 echo '<div class="nothing">'.$lang['media_perm_read'].'</div>'.NL
;
961 * Prints tab that displays form for editing mediafile metadata
963 * @author Kate Arzamastseva <pshns@ukr.net>
965 * @param string $image media id
967 * @param null|int $auth permission level
969 function media_tab_edit($image, $ns, $auth=null) {
970 if(is_null($auth)) $auth = auth_quickaclcheck("$ns:*");
973 list(, $mime) = mimetype($image);
974 if ($mime == 'image/jpeg') media_metaform($image,$auth);
979 * Prints tab that displays mediafile revisions
981 * @author Kate Arzamastseva <pshns@ukr.net>
983 * @param string $image media id
985 * @param null|int $auth permission level
987 function media_tab_history($image, $ns, $auth=null) {
991 if(is_null($auth)) $auth = auth_quickaclcheck("$ns:*");
992 $do = $INPUT->str('mediado');
994 if ($auth >= AUTH_READ
&& $image) {
996 media_diff($image, $ns, $auth);
998 $first = $INPUT->int('first');
999 html_revisions($first, $image);
1002 echo '<div class="nothing">'.$lang['media_perm_read'].'</div>'.NL
;
1007 * Prints mediafile details
1009 * @param string $image media id
1010 * @param int $auth permission level
1011 * @param int|string $rev revision timestamp or empty string
1012 * @param JpegMeta|bool $meta
1014 * @author Kate Arzamastseva <pshns@ukr.net>
1016 function media_preview($image, $auth, $rev='', $meta=false) {
1018 $size = media_image_preview_size($image, $rev, $meta);
1022 echo '<div class="image">';
1026 $more['rev'] = $rev;
1028 $t = @filemtime
(mediaFN($image));
1032 $more['w'] = $size[0];
1033 $more['h'] = $size[1];
1034 $src = ml($image, $more);
1036 echo '<a href="'.$src.'" target="_blank" title="'.$lang['mediaview'].'">';
1037 echo '<img src="'.$src.'" alt="" style="max-width: '.$size[0].'px;" />';
1045 * Prints mediafile action buttons
1047 * @author Kate Arzamastseva <pshns@ukr.net>
1049 * @param string $image media id
1050 * @param int $auth permission level
1051 * @param string|int $rev revision timestamp, or empty string
1053 function media_preview_buttons($image, $auth, $rev='') {
1054 global $lang, $conf;
1056 echo '<ul class="actions">'.NL
;
1058 if($auth >= AUTH_DELETE
&& !$rev && file_exists(mediaFN($image))){
1061 $form = new Doku_Form(array('id' => 'mediamanager__btn_delete',
1062 'action'=>media_managerURL(array('delete' => $image), '&')));
1063 $form->addElement(form_makeButton('submit','',$lang['btn_delete']));
1069 $auth_ow = (($conf['mediarevisions']) ? AUTH_UPLOAD
: AUTH_DELETE
);
1070 if($auth >= $auth_ow && !$rev){
1072 // upload new version button
1073 $form = new Doku_Form(array('id' => 'mediamanager__btn_update',
1074 'action'=>media_managerURL(array('image' => $image, 'mediado' => 'update'), '&')));
1075 $form->addElement(form_makeButton('submit','',$lang['media_update']));
1081 if($auth >= AUTH_UPLOAD
&& $rev && $conf['mediarevisions'] && file_exists(mediaFN($image, $rev))){
1084 $form = new Doku_Form(array('id' => 'mediamanager__btn_restore',
1085 'action'=>media_managerURL(array('image' => $image), '&')));
1086 $form->addHidden('mediado','restore');
1087 $form->addHidden('rev',$rev);
1088 $form->addElement(form_makeButton('submit','',$lang['media_restore']));
1098 * Returns image width and height for mediamanager preview panel
1100 * @author Kate Arzamastseva <pshns@ukr.net>
1101 * @param string $image
1102 * @param int|string $rev
1103 * @param JpegMeta|bool $meta
1105 * @return array|false
1107 function media_image_preview_size($image, $rev, $meta, $size = 500) {
1108 if (!preg_match("/\.(jpe?g|gif|png)$/", $image) ||
!file_exists(mediaFN($image, $rev))) return false;
1110 $info = getimagesize(mediaFN($image, $rev));
1111 $w = (int) $info[0];
1112 $h = (int) $info[1];
1114 if($meta && ($w > $size ||
$h > $size)){
1115 $ratio = $meta->getResizeRatio($size, $size);
1116 $w = floor($w * $ratio);
1117 $h = floor($h * $ratio);
1119 return array($w, $h);
1123 * Returns the requested EXIF/IPTC tag from the image meta
1125 * @author Kate Arzamastseva <pshns@ukr.net>
1127 * @param array $tags array with tags, first existing is returned
1128 * @param JpegMeta $meta
1129 * @param string $alt alternative value
1132 function media_getTag($tags,$meta,$alt=''){
1133 if($meta === false) return $alt;
1134 $info = $meta->getField($tags);
1135 if($info == false) return $alt;
1140 * Returns mediafile tags
1142 * @author Kate Arzamastseva <pshns@ukr.net>
1144 * @param JpegMeta $meta
1145 * @return array list of tags of the mediafile
1147 function media_file_tags($meta) {
1148 // load the field descriptions
1149 static $fields = null;
1150 if(is_null($fields)){
1151 $config_files = getConfigFiles('mediameta');
1152 foreach ($config_files as $config_file) {
1153 if(file_exists($config_file)) include($config_file);
1159 foreach($fields as $key => $tag){
1161 if (!empty($tag[0])) $t = array($tag[0]);
1162 if(isset($tag[3]) && is_array($tag[3])) $t = array_merge($t,$tag[3]);
1163 $value = media_getTag($t, $meta);
1164 $tags[] = array('tag' => $tag, 'value' => $value);
1171 * Prints mediafile tags
1173 * @author Kate Arzamastseva <pshns@ukr.net>
1175 * @param string $image image id
1176 * @param int $auth permission level
1177 * @param string|int $rev revision timestamp, or empty string
1178 * @param bool|JpegMeta $meta image object, or create one if false
1180 function media_details($image, $auth, $rev='', $meta=false) {
1183 if (!$meta) $meta = new JpegMeta(mediaFN($image, $rev));
1184 $tags = media_file_tags($meta);
1187 foreach($tags as $tag){
1188 if ($tag['value']) {
1189 $value = cleanText($tag['value']);
1190 echo '<dt>'.$lang[$tag['tag'][1]].'</dt><dd>';
1191 if ($tag['tag'][2] == 'date') echo dformat($value);
1192 else echo hsc($value);
1198 echo '<dt>'.$lang['reference'].':</dt>';
1199 $media_usage = ft_mediause($image,true);
1200 if(count($media_usage) > 0){
1201 foreach($media_usage as $path){
1202 echo '<dd>'.html_wikilink($path).'</dd>';
1205 echo '<dd>'.$lang['nothingfound'].'</dd>';
1212 * Shows difference between two revisions of file
1214 * @author Kate Arzamastseva <pshns@ukr.net>
1216 * @param string $image image id
1218 * @param int $auth permission level
1219 * @param bool $fromajax
1220 * @return false|null|string
1222 function media_diff($image, $ns, $auth, $fromajax = false) {
1226 if ($auth < AUTH_READ ||
!$image ||
!$conf['mediarevisions']) return '';
1228 $rev1 = $INPUT->int('rev');
1230 $rev2 = $INPUT->ref('rev2');
1231 if(is_array($rev2)){
1232 $rev1 = (int) $rev2[0];
1233 $rev2 = (int) $rev2[1];
1240 $rev2 = $INPUT->int('rev2');
1243 if ($rev1 && !file_exists(mediaFN($image, $rev1))) $rev1 = false;
1244 if ($rev2 && !file_exists(mediaFN($image, $rev2))) $rev2 = false;
1246 if($rev1 && $rev2){ // two specific revisions wanted
1247 // make sure order is correct (older on the left)
1255 }elseif($rev1){ // single revision given, compare to current
1258 }else{ // no revision was given, compare previous to current
1260 $medialog = new MediaChangeLog($image);
1261 $revs = $medialog->getRevisions(0, 1);
1262 if (file_exists(mediaFN($image, $revs[0]))) {
1269 // prepare event data
1276 $data[5] = $fromajax;
1279 return Event
::createAndTrigger('MEDIA_DIFF', $data, '_media_file_diff', true);
1283 * Callback for media file diff
1285 * @param array $data event data
1286 * @return false|null
1288 function _media_file_diff($data) {
1289 if(is_array($data) && count($data)===6) {
1290 media_file_diff($data[0], $data[1], $data[2], $data[3], $data[4], $data[5]);
1297 * Shows difference between two revisions of image
1299 * @author Kate Arzamastseva <pshns@ukr.net>
1301 * @param string $image
1302 * @param string|int $l_rev revision timestamp, or empty string
1303 * @param string|int $r_rev revision timestamp, or empty string
1305 * @param int $auth permission level
1306 * @param bool $fromajax
1308 function media_file_diff($image, $l_rev, $r_rev, $ns, $auth, $fromajax){
1312 $l_meta = new JpegMeta(mediaFN($image, $l_rev));
1313 $r_meta = new JpegMeta(mediaFN($image, $r_rev));
1315 $is_img = preg_match('/\.(jpe?g|gif|png)$/', $image);
1317 $l_size = media_image_preview_size($image, $l_rev, $l_meta);
1318 $r_size = media_image_preview_size($image, $r_rev, $r_meta);
1319 $is_img = ($l_size && $r_size && ($l_size[0] >= 30 ||
$r_size[0] >= 30));
1321 $difftype = $INPUT->str('difftype');
1324 $form = new Doku_Form(array(
1325 'action' => media_managerURL(array(), '&'),
1327 'id' => 'mediamanager__form_diffview',
1328 'class' => 'diffView'
1330 $form->addHidden('sectok', null);
1331 $form->addElement('<input type="hidden" name="rev2[]" value="'.$l_rev.'" ></input>');
1332 $form->addElement('<input type="hidden" name="rev2[]" value="'.$r_rev.'" ></input>');
1333 $form->addHidden('mediado', 'diff');
1336 echo NL
.'<div id="mediamanager__diff" >'.NL
;
1339 if ($difftype == 'opacity' ||
$difftype == 'portions') {
1340 media_image_diff($image, $l_rev, $r_rev, $l_size, $r_size, $difftype);
1341 if (!$fromajax) echo '</div>';
1346 list($l_head, $r_head) = html_diff_head($l_rev, $r_rev, $image, true);
1352 <th
><?php
echo $l_head; ?
></th
>
1353 <th
><?php
echo $r_head; ?
></th
>
1357 echo '<tr class="image">';
1359 media_preview($image, $auth, $l_rev, $l_meta);
1363 media_preview($image, $auth, $r_rev, $r_meta);
1367 echo '<tr class="actions">';
1369 media_preview_buttons($image, $auth, $l_rev);
1373 media_preview_buttons($image, $auth, $r_rev);
1377 $l_tags = media_file_tags($l_meta);
1378 $r_tags = media_file_tags($r_meta);
1379 // FIXME r_tags-only stuff
1380 foreach ($l_tags as $key => $l_tag) {
1381 if ($l_tag['value'] != $r_tags[$key]['value']) {
1382 $r_tags[$key]['highlighted'] = true;
1383 $l_tags[$key]['highlighted'] = true;
1384 } else if (!$l_tag['value'] ||
!$r_tags[$key]['value']) {
1385 unset($r_tags[$key]);
1386 unset($l_tags[$key]);
1391 foreach(array($l_tags,$r_tags) as $tags){
1394 echo '<dl class="img_tags">';
1395 foreach($tags as $tag){
1396 $value = cleanText($tag['value']);
1397 if (!$value) $value = '-';
1398 echo '<dt>'.$lang[$tag['tag'][1]].'</dt>';
1400 if ($tag['highlighted']) {
1403 if ($tag['tag'][2] == 'date') echo dformat($value);
1404 else echo hsc($value);
1405 if ($tag['highlighted']) {
1419 if ($is_img && !$fromajax) echo '</div>';
1423 * Prints two images side by side
1426 * @author Kate Arzamastseva <pshns@ukr.net>
1428 * @param string $image image id
1429 * @param int $l_rev revision timestamp, or empty string
1430 * @param int $r_rev revision timestamp, or empty string
1431 * @param array $l_size array with width and height
1432 * @param array $r_size array with width and height
1433 * @param string $type
1435 function media_image_diff($image, $l_rev, $r_rev, $l_size, $r_size, $type) {
1436 if ($l_size != $r_size) {
1437 if ($r_size[0] > $l_size[0]) {
1442 $l_more = array('rev' => $l_rev, 'h' => $l_size[1], 'w' => $l_size[0]);
1443 $r_more = array('rev' => $r_rev, 'h' => $l_size[1], 'w' => $l_size[0]);
1445 $l_src = ml($image, $l_more);
1446 $r_src = ml($image, $r_more);
1449 echo '<div class="slider" style="max-width: '.($l_size[0]-20).'px;" ></div>'.NL
;
1451 // two images in divs
1452 echo '<div class="imageDiff ' . $type . '">'.NL
;
1453 echo '<div class="image1" style="max-width: '.$l_size[0].'px;">';
1454 echo '<img src="'.$l_src.'" alt="" />';
1456 echo '<div class="image2" style="max-width: '.$l_size[0].'px;">';
1457 echo '<img src="'.$r_src.'" alt="" />';
1463 * Restores an old revision of a media file
1465 * @param string $image media id
1466 * @param int $rev revision timestamp or empty string
1468 * @return string - file's id
1470 * @author Kate Arzamastseva <pshns@ukr.net>
1472 function media_restore($image, $rev, $auth){
1474 if ($auth < AUTH_UPLOAD ||
!$conf['mediarevisions']) return false;
1475 $removed = (!file_exists(mediaFN($image)) && file_exists(mediaMetaFN($image, '.changes')));
1476 if (!$image ||
(!file_exists(mediaFN($image)) && !$removed)) return false;
1477 if (!$rev ||
!file_exists(mediaFN($image, $rev))) return false;
1478 list(,$imime,) = mimetype($image);
1479 $res = media_upload_finish(mediaFN($image, $rev),
1485 if (is_array($res)) {
1486 msg($res[0], $res[1]);
1493 * List all files found by the search request
1495 * @author Tobias Sarnowski <sarnowski@cosmocode.de>
1496 * @author Andreas Gohr <gohr@cosmocode.de>
1497 * @author Kate Arzamastseva <pshns@ukr.net>
1498 * @triggers MEDIA_SEARCH
1500 * @param string $query
1502 * @param null|int $auth
1503 * @param bool $fullscreen
1504 * @param string $sort
1506 function media_searchlist($query,$ns,$auth=null,$fullscreen=false,$sort='natural'){
1516 if (!blank($query)) {
1517 $evt = new Event('MEDIA_SEARCH', $evdata);
1518 if ($evt->advise_before()) {
1519 $dir = utf8_encodeFN(str_replace(':','/',$evdata['ns']));
1520 $quoted = preg_quote($evdata['query'],'/');
1522 $quoted = str_replace(array('\*', '\?'), array('.*', '.'), $quoted, $count);
1524 //if we use globbing file name must match entirely but may be preceded by arbitrary namespace
1525 if ($count > 0) $quoted = '^([^:]*:)*'.$quoted.'$';
1527 $pattern = '/'.$quoted.'/i';
1528 search($evdata['data'],
1531 array('showmsg'=>false,'pattern'=>$pattern),
1536 $evt->advise_after();
1541 echo '<h1 id="media__ns">'.sprintf($lang['searchmedia_in'],hsc($ns).':*').'</h1>'.NL
;
1542 media_searchform($ns,$query);
1545 if(!count($evdata['data'])){
1546 echo '<div class="nothing">'.$lang['nothingfound'].'</div>'.NL
;
1549 echo '<ul class="' . _media_get_list_type() . '">';
1551 foreach($evdata['data'] as $item){
1552 if (!$fullscreen) media_printfile($item,$item['perm'],'',true);
1553 else media_printfile_thumbs($item,$item['perm'],false,true);
1555 if ($fullscreen) echo '</ul>'.NL
;
1560 * Formats and prints one file in the list
1562 * @param array $item
1563 * @param int $auth permission level
1564 * @param string $jump item id
1565 * @param bool $display_namespace
1567 function media_printfile($item,$auth,$jump,$display_namespace=false){
1570 // Prepare zebra coloring
1571 // I always wanted to use this variable name :-D
1572 static $twibble = 1;
1574 $zebra = ($twibble == -1) ?
'odd' : 'even';
1576 // Automatically jump to recent action
1577 if($jump == $item['id']) {
1578 $jump = ' id="scroll__here" ';
1583 // Prepare fileicons
1584 list($ext) = mimetype($item['file'],false);
1585 $class = preg_replace('/[^_\-a-z0-9]+/i','_',$ext);
1586 $class = 'select mediafile mf_'.$class;
1589 $file = utf8_decodeFN($item['file']);
1594 $info .= (int) $item['meta']->getField('File.Width');
1596 $info .= (int) $item['meta']->getField('File.Height');
1599 $info .= '<i>'.dformat($item['mtime']).'</i>';
1601 $info .= filesize_h($item['size']);
1604 echo '<div class="'.$zebra.'"'.$jump.' title="'.hsc($item['id']).'">'.NL
;
1605 if (!$display_namespace) {
1606 echo '<a id="h_:'.$item['id'].'" class="'.$class.'">'.hsc($file).'</a> ';
1608 echo '<a id="h_:'.$item['id'].'" class="'.$class.'">'.hsc($item['id']).'</a><br/>';
1610 echo '<span class="info">('.$info.')</span>'.NL
;
1613 $link = ml($item['id'],'',true);
1614 echo ' <a href="'.$link.'" target="_blank"><img src="'.DOKU_BASE
.'lib/images/magnifier.png" '.
1615 'alt="'.$lang['mediaview'].'" title="'.$lang['mediaview'].'" class="btn" /></a>';
1617 // mediamanager button
1618 $link = wl('',array('do'=>'media','image'=>$item['id'],'ns'=>getNS($item['id'])));
1619 echo ' <a href="'.$link.'" target="_blank"><img src="'.DOKU_BASE
.'lib/images/mediamanager.png" '.
1620 'alt="'.$lang['btn_media'].'" title="'.$lang['btn_media'].'" class="btn" /></a>';
1623 if($item['writable'] && $auth >= AUTH_DELETE
){
1624 $link = DOKU_BASE
.'lib/exe/mediamanager.php?delete='.rawurlencode($item['id']).
1625 '&sectok='.getSecurityToken();
1626 echo ' <a href="'.$link.'" class="btn_media_delete" title="'.$item['id'].'">'.
1627 '<img src="'.DOKU_BASE
.'lib/images/trash.png" alt="'.$lang['btn_delete'].'" '.
1628 'title="'.$lang['btn_delete'].'" class="btn" /></a>';
1631 echo '<div class="example" id="ex_'.str_replace(':','_',$item['id']).'">';
1632 echo $lang['mediausage'].' <code>{{:'.$item['id'].'}}</code>';
1634 if($item['isimg']) media_printimgdetail($item);
1635 echo '<div class="clearer"></div>'.NL
;
1640 * Display a media icon
1642 * @param string $filename media id
1643 * @param string $size the size subfolder, if not specified 16x16 is used
1644 * @return string html
1646 function media_printicon($filename, $size=''){
1647 list($ext) = mimetype(mediaFN($filename),false);
1649 if (file_exists(DOKU_INC
.'lib/images/fileicons/'.$size.'/'.$ext.'.png')) {
1650 $icon = DOKU_BASE
.'lib/images/fileicons/'.$size.'/'.$ext.'.png';
1652 $icon = DOKU_BASE
.'lib/images/fileicons/'.$size.'/file.png';
1655 return '<img src="'.$icon.'" alt="'.$filename.'" class="icon" />';
1659 * Formats and prints one file in the list in the thumbnails view
1661 * @author Kate Arzamastseva <pshns@ukr.net>
1663 * @param array $item
1664 * @param int $auth permission level
1665 * @param bool|string $jump item id
1666 * @param bool $display_namespace
1668 function media_printfile_thumbs($item,$auth,$jump=false,$display_namespace=false){
1671 $file = utf8_decodeFN($item['file']);
1674 echo '<li><dl title="'.hsc($item['id']).'">'.NL
;
1677 if($item['isimg']) {
1678 media_printimgdetail($item, true);
1681 echo '<a id="d_:'.$item['id'].'" class="image" title="'.$item['id'].'" href="'.
1682 media_managerURL(array('image' => hsc($item['id']), 'ns' => getNS($item['id']),
1683 'tab_details' => 'view')).'">';
1684 echo media_printicon($item['id'], '32x32');
1688 if (!$display_namespace) {
1691 $name = hsc($item['id']);
1693 echo '<dd class="name"><a href="'.media_managerURL(array('image' => hsc($item['id']), 'ns' => getNS($item['id']),
1694 'tab_details' => 'view')).'" id="h_:'.$item['id'].'">'.$name.'</a></dd>'.NL
;
1698 $size .= (int) $item['meta']->getField('File.Width');
1700 $size .= (int) $item['meta']->getField('File.Height');
1701 echo '<dd class="size">'.$size.'</dd>'.NL
;
1703 echo '<dd class="size"> </dd>'.NL
;
1705 $date = dformat($item['mtime']);
1706 echo '<dd class="date">'.$date.'</dd>'.NL
;
1707 $filesize = filesize_h($item['size']);
1708 echo '<dd class="filesize">'.$filesize.'</dd>'.NL
;
1709 echo '</dl></li>'.NL
;
1713 * Prints a thumbnail and metainfo
1715 * @param array $item
1716 * @param bool $fullscreen
1718 function media_printimgdetail($item, $fullscreen=false){
1719 // prepare thumbnail
1720 $size = $fullscreen ?
90 : 120;
1722 $w = (int) $item['meta']->getField('File.Width');
1723 $h = (int) $item['meta']->getField('File.Height');
1724 if($w>$size ||
$h>$size){
1726 $ratio = $item['meta']->getResizeRatio($size);
1728 $ratio = $item['meta']->getResizeRatio($size,$size);
1730 $w = floor($w * $ratio);
1731 $h = floor($h * $ratio);
1733 $src = ml($item['id'],array('w'=>$w,'h'=>$h,'t'=>$item['mtime']));
1736 // In fullscreen mediamanager view, image resizing is done via CSS.
1740 $p['alt'] = $item['id'];
1741 $att = buildAttributes($p);
1745 echo '<a id="l_:'.$item['id'].'" class="image thumb" href="'.
1746 media_managerURL(['image' => hsc($item['id']), 'ns' => getNS($item['id']), 'tab_details' => 'view']).'">';
1747 echo '<img src="'.$src.'" '.$att.' />';
1751 if ($fullscreen) return;
1753 echo '<div class="detail">';
1754 echo '<div class="thumb">';
1755 echo '<a id="d_:'.$item['id'].'" class="select">';
1756 echo '<img src="'.$src.'" '.$att.' />';
1760 // read EXIF/IPTC data
1761 $t = $item['meta']->getField(array('IPTC.Headline','xmp.dc:title'));
1762 $d = $item['meta']->getField(array('IPTC.Caption','EXIF.UserComment',
1763 'EXIF.TIFFImageDescription',
1764 'EXIF.TIFFUserComment'));
1765 if(\dokuwiki\Utf8\PhpString
::strlen($d) > 250) $d = \dokuwiki\Utf8\PhpString
::substr($d,0,250).'...';
1766 $k = $item['meta']->getField(array('IPTC.Keywords','IPTC.Category','xmp.dc:subject'));
1768 // print EXIF/IPTC data
1769 if($t ||
$d ||
$k ){
1771 if($t) echo '<strong>'.hsc($t).'</strong><br />';
1772 if($d) echo hsc($d).'<br />';
1773 if($t) echo '<em>'.hsc($k).'</em>';
1780 * Build link based on the current, adding/rewriting parameters
1782 * @author Kate Arzamastseva <pshns@ukr.net>
1784 * @param array|bool $params
1785 * @param string $amp separator
1786 * @param bool $abs absolute url?
1787 * @param bool $params_array return the parmeters array?
1788 * @return string|array - link or link parameters
1790 function media_managerURL($params=false, $amp='&', $abs=false, $params_array=false) {
1794 $gets = array('do' => 'media');
1795 $media_manager_params = array('tab_files', 'tab_details', 'image', 'ns', 'list', 'sort');
1796 foreach ($media_manager_params as $x) {
1797 if ($INPUT->has($x)) $gets[$x] = $INPUT->str($x);
1801 $gets = $params +
$gets;
1804 if (isset($gets['delete'])) {
1805 unset($gets['image']);
1806 unset($gets['tab_details']);
1809 if ($params_array) return $gets;
1811 return wl($ID,$gets,$abs,$amp);
1815 * Print the media upload form if permissions are correct
1817 * @author Andreas Gohr <andi@splitbrain.org>
1818 * @author Kate Arzamastseva <pshns@ukr.net>
1821 * @param int $auth permission level
1822 * @param bool $fullscreen
1824 function media_uploadform($ns, $auth, $fullscreen = false){
1829 if($auth < AUTH_UPLOAD
) {
1830 echo '<div class="nothing">'.$lang['media_perm_upload'].'</div>'.NL
;
1833 $auth_ow = (($conf['mediarevisions']) ? AUTH_UPLOAD
: AUTH_DELETE
);
1837 if ($auth >= $auth_ow && $fullscreen && $INPUT->str('mediado') == 'update') {
1839 $id = cleanID($INPUT->str('image'));
1842 // The default HTML upload form
1843 $params = array('id' => 'dw__upload',
1844 'enctype' => 'multipart/form-data');
1846 $params['action'] = DOKU_BASE
.'lib/exe/mediamanager.php';
1848 $params['action'] = media_managerURL(array('tab_files' => 'files',
1849 'tab_details' => 'view'), '&');
1852 $form = new Doku_Form($params);
1853 if (!$fullscreen) echo '<div class="upload">' . $lang['mediaupload'] . '</div>';
1854 $form->addElement(formSecurityToken());
1855 $form->addHidden('ns', hsc($ns));
1856 $form->addElement(form_makeOpenTag('p'));
1857 $form->addElement(form_makeFileField('upload', $lang['txt_upload'], 'upload__file'));
1858 $form->addElement(form_makeCloseTag('p'));
1859 $form->addElement(form_makeOpenTag('p'));
1860 $form->addElement(form_makeTextField('mediaid', noNS($id), $lang['txt_filename'], 'upload__name'));
1861 $form->addElement(form_makeButton('submit', '', $lang['btn_upload']));
1862 $form->addElement(form_makeCloseTag('p'));
1864 if($auth >= $auth_ow){
1865 $form->addElement(form_makeOpenTag('p'));
1867 if ($update) $attrs['checked'] = 'checked';
1868 $form->addElement(form_makeCheckboxField('ow', 1, $lang['txt_overwrt'], 'dw__ow', 'check', $attrs));
1869 $form->addElement(form_makeCloseTag('p'));
1872 echo NL
.'<div id="mediamanager__uploader">'.NL
;
1873 html_form('upload', $form);
1877 echo '<p class="maxsize">';
1878 printf($lang['maxuploadsize'],filesize_h(media_getuploadsize()));
1884 * Returns the size uploaded files may have
1886 * This uses a conservative approach using the lowest number found
1887 * in any of the limiting ini settings
1889 * @returns int size in bytes
1891 function media_getuploadsize(){
1894 $post = (int) php_to_byte(@ini_get
('post_max_size'));
1895 $suho = (int) php_to_byte(@ini_get
('suhosin.post.max_value_length'));
1896 $upld = (int) php_to_byte(@ini_get
('upload_max_filesize'));
1898 if($post && ($post < $okay ||
$okay == 0)) $okay = $post;
1899 if($suho && ($suho < $okay ||
$okay == 0)) $okay = $suho;
1900 if($upld && ($upld < $okay ||
$okay == 0)) $okay = $upld;
1906 * Print the search field form
1908 * @author Tobias Sarnowski <sarnowski@cosmocode.de>
1909 * @author Kate Arzamastseva <pshns@ukr.net>
1912 * @param string $query
1913 * @param bool $fullscreen
1915 function media_searchform($ns,$query='',$fullscreen=false){
1918 // The default HTML search form
1919 $params = array('id' => 'dw__mediasearch');
1921 $params['action'] = DOKU_BASE
.'lib/exe/mediamanager.php';
1923 $params['action'] = media_managerURL(array(), '&');
1925 $form = new Doku_Form($params);
1926 $form->addHidden('ns', $ns);
1927 $form->addHidden($fullscreen ?
'mediado' : 'do', 'searchlist');
1929 $form->addElement(form_makeOpenTag('p'));
1934 $lang['searchmedia'],
1937 array('title' => sprintf($lang['searchmedia_in'], hsc($ns) . ':*'))
1940 $form->addElement(form_makeButton('submit', '', $lang['btn_search']));
1941 $form->addElement(form_makeCloseTag('p'));
1942 html_form('searchmedia', $form);
1946 * Build a tree outline of available media namespaces
1948 * @author Andreas Gohr <andi@splitbrain.org>
1952 function media_nstree($ns){
1956 // currently selected namespace
1960 $ns = (string)getNS($ID);
1963 $ns_dir = utf8_encodeFN(str_replace(':','/',$ns));
1966 search($data,$conf['mediadir'],'search_index',array('ns' => $ns_dir, 'nofiles' => true));
1968 // wrap a list with the root level around the other namespaces
1969 array_unshift($data, array('level' => 0, 'id' => '', 'open' =>'true',
1970 'label' => '['.$lang['mediaroot'].']'));
1972 // insert the current ns into the hierarchy if it isn't already part of it
1973 $ns_parts = explode(':', $ns);
1976 foreach ($ns_parts as $level => $part) {
1977 if ($tmp_ns) $tmp_ns .= ':'.$part;
1978 else $tmp_ns = $part;
1980 // find the namespace parts or insert them
1981 while ($data[$pos]['id'] != $tmp_ns) {
1983 $pos >= count($data) ||
1985 $data[$pos]['level'] <= $level+
1 &&
1986 strnatcmp(utf8_encodeFN($data[$pos]['id']), utf8_encodeFN($tmp_ns)) > 0
1989 array_splice($data, $pos, 0, array(array('level' => $level+
1, 'id' => $tmp_ns, 'open' => 'true')));
1996 echo html_buildlist($data,'idx','media_nstree_item','media_nstree_li');
2000 * Userfunction for html_buildlist
2002 * Prints a media namespace tree item
2004 * @author Andreas Gohr <andi@splitbrain.org>
2006 * @param array $item
2007 * @return string html
2009 function media_nstree_item($item){
2011 $pos = strrpos($item['id'], ':');
2012 $label = substr($item['id'], $pos > 0 ?
$pos +
1 : 0);
2013 if(empty($item['label'])) $item['label'] = $label;
2016 if (!($INPUT->str('do') == 'media'))
2017 $ret .= '<a href="'.DOKU_BASE
.'lib/exe/mediamanager.php?ns='.idfilter($item['id']).'" class="idx_dir">';
2018 else $ret .= '<a href="'.media_managerURL(array('ns' => idfilter($item['id'], false), 'tab_files' => 'files'))
2019 .'" class="idx_dir">';
2020 $ret .= $item['label'];
2026 * Userfunction for html_buildlist
2028 * Prints a media namespace tree item opener
2030 * @author Andreas Gohr <andi@splitbrain.org>
2032 * @param array $item
2033 * @return string html
2035 function media_nstree_li($item){
2036 $class='media level'.$item['level'];
2039 $img = DOKU_BASE
.'lib/images/minus.gif';
2042 $class .= ' closed';
2043 $img = DOKU_BASE
.'lib/images/plus.gif';
2046 // TODO: only deliver an image if it actually has a subtree...
2047 return '<li class="'.$class.'">'.
2048 '<img src="'.$img.'" alt="'.$alt.'" />';
2052 * Resizes the given image to the given size
2054 * @author Andreas Gohr <andi@splitbrain.org>
2056 * @param string $file filename, path to file
2057 * @param string $ext extension
2058 * @param int $w desired width
2059 * @param int $h desired height
2060 * @return string path to resized or original size if failed
2062 function media_resize_image($file, $ext, $w, $h=0){
2065 $info = @getimagesize
($file); //get original size
2066 if($info == false) return $file; // that's no image - it's a spaceship!
2068 if(!$h) $h = round(($w * $info[1]) / $info[0]);
2069 if(!$w) $w = round(($h * $info[0]) / $info[1]);
2071 // we wont scale up to infinity
2072 if($w > 2000 ||
$h > 2000) return $file;
2074 // resize necessary? - (w,h) = native dimensions
2075 if(($w == $info[0]) && ($h == $info[1])) return $file;
2078 $local = getCacheName($file,'.media.'.$w.'x'.$h.'.'.$ext);
2079 $mtime = @filemtime
($local); // 0 if not exists
2081 if($mtime > filemtime($file) ||
2082 media_resize_imageIM($ext, $file, $info[0], $info[1], $local, $w, $h) ||
2083 media_resize_imageGD($ext, $file, $info[0], $info[1], $local, $w, $h)
2085 if(!empty($conf['fperm'])) @chmod
($local, $conf['fperm']);
2088 //still here? resizing failed
2093 * Crops the given image to the wanted ratio, then calls media_resize_image to scale it
2094 * to the wanted size
2096 * Crops are centered horizontally but prefer the upper third of an vertical
2097 * image because most pics are more interesting in that area (rule of thirds)
2099 * @author Andreas Gohr <andi@splitbrain.org>
2101 * @param string $file filename, path to file
2102 * @param string $ext extension
2103 * @param int $w desired width
2104 * @param int $h desired height
2105 * @return string path to resized or original size if failed
2107 function media_crop_image($file, $ext, $w, $h=0){
2111 $info = @getimagesize
($file); //get original size
2112 if($info == false) return $file; // that's no image - it's a spaceship!
2114 // calculate crop size
2115 $fr = $info[0]/$info[1];
2118 // check if the crop can be handled completely by resize,
2119 // i.e. the specified width & height match the aspect ratio of the source image
2120 if ($w == round($h*$fr)) {
2121 return media_resize_image($file, $ext, $w);
2127 $ch = (int) ($info[0]/$tr);
2129 $cw = (int) ($info[1]*$tr);
2134 $cw = (int) ($info[1]*$tr);
2138 $ch = (int) ($info[0]/$tr);
2141 // calculate crop offset
2142 $cx = (int) (($info[0]-$cw)/2);
2143 $cy = (int) (($info[1]-$ch)/3);
2146 $local = getCacheName($file,'.media.'.$cw.'x'.$ch.'.crop.'.$ext);
2147 $mtime = @filemtime
($local); // 0 if not exists
2149 if( $mtime > @filemtime
($file) ||
2150 media_crop_imageIM($ext,$file,$info[0],$info[1],$local,$cw,$ch,$cx,$cy) ||
2151 media_resize_imageGD($ext,$file,$cw,$ch,$local,$cw,$ch,$cx,$cy) ){
2152 if(!empty($conf['fperm'])) @chmod
($local, $conf['fperm']);
2153 return media_resize_image($local,$ext, $w, $h);
2156 //still here? cropping failed
2157 return media_resize_image($file,$ext, $w, $h);
2161 * Calculate a token to be used to verify fetch requests for resized or
2162 * cropped images have been internally generated - and prevent external
2163 * DDOS attacks via fetch
2165 * @author Christopher Smith <chris@jalakai.co.uk>
2167 * @param string $id id of the image
2168 * @param int $w resize/crop width
2169 * @param int $h resize/crop height
2170 * @return string token or empty string if no token required
2172 function media_get_token($id,$w,$h){
2173 // token is only required for modified images
2174 if ($w ||
$h ||
media_isexternal($id)) {
2176 if ($w) $token .= '.'.$w;
2177 if ($h) $token .= '.'.$h;
2179 return substr(\dokuwiki\PassHash
::hmac('md5', $token, auth_cookiesalt()),0,6);
2186 * Download a remote file and return local filename
2188 * returns false if download fails. Uses cached file if available and
2191 * @author Andreas Gohr <andi@splitbrain.org>
2192 * @author Pavel Vitis <Pavel.Vitis@seznam.cz>
2194 * @param string $url
2195 * @param string $ext extension
2196 * @param int $cache cachetime in seconds
2197 * @return false|string path to cached file
2199 function media_get_from_URL($url,$ext,$cache){
2202 // if no cache or fetchsize just redirect
2203 if ($cache==0) return false;
2204 if (!$conf['fetchsize']) return false;
2206 $local = getCacheName(strtolower($url),".media.$ext");
2207 $mtime = @filemtime
($local); // 0 if not exists
2209 //decide if download needed:
2210 if(($mtime == 0) ||
// cache does not exist
2211 ($cache != -1 && $mtime < time() - $cache) // 'recache' and cache has expired
2213 if(media_image_download($url, $local)) {
2220 //if cache exists use it else
2221 if($mtime) return $local;
2228 * Download image files
2230 * @author Andreas Gohr <andi@splitbrain.org>
2232 * @param string $url
2233 * @param string $file path to file in which to put the downloaded content
2236 function media_image_download($url,$file){
2238 $http = new DokuHTTPClient();
2239 $http->keep_alive
= false; // we do single ops here, no need for keep-alive
2241 $http->max_bodysize
= $conf['fetchsize'];
2242 $http->timeout
= 25; //max. 25 sec
2243 $http->header_regexp
= '!\r\nContent-Type: image/(jpe?g|gif|png)!i';
2245 $data = $http->get($url);
2246 if(!$data) return false;
2248 $fileexists = file_exists($file);
2249 $fp = @fopen
($file,"w");
2250 if(!$fp) return false;
2253 if(!$fileexists and $conf['fperm']) chmod($file, $conf['fperm']);
2255 // check if it is really an image
2256 $info = @getimagesize
($file);
2266 * resize images using external ImageMagick convert program
2268 * @author Pavel Vitis <Pavel.Vitis@seznam.cz>
2269 * @author Andreas Gohr <andi@splitbrain.org>
2271 * @param string $ext extension
2272 * @param string $from filename path to file
2273 * @param int $from_w original width
2274 * @param int $from_h original height
2275 * @param string $to path to resized file
2276 * @param int $to_w desired width
2277 * @param int $to_h desired height
2280 function media_resize_imageIM($ext,$from,$from_w,$from_h,$to,$to_w,$to_h){
2283 // check if convert is configured
2284 if(!$conf['im_convert']) return false;
2287 $cmd = $conf['im_convert'];
2288 $cmd .= ' -resize '.$to_w.'x'.$to_h.'!';
2289 if ($ext == 'jpg' ||
$ext == 'jpeg') {
2290 $cmd .= ' -quality '.$conf['jpg_quality'];
2292 $cmd .= " $from $to";
2294 @exec
($cmd,$out,$retval);
2295 if ($retval == 0) return true;
2300 * crop images using external ImageMagick convert program
2302 * @author Andreas Gohr <andi@splitbrain.org>
2304 * @param string $ext extension
2305 * @param string $from filename path to file
2306 * @param int $from_w original width
2307 * @param int $from_h original height
2308 * @param string $to path to resized file
2309 * @param int $to_w desired width
2310 * @param int $to_h desired height
2311 * @param int $ofs_x offset of crop centre
2312 * @param int $ofs_y offset of crop centre
2315 function media_crop_imageIM($ext,$from,$from_w,$from_h,$to,$to_w,$to_h,$ofs_x,$ofs_y){
2318 // check if convert is configured
2319 if(!$conf['im_convert']) return false;
2322 $cmd = $conf['im_convert'];
2323 $cmd .= ' -crop '.$to_w.'x'.$to_h.'+'.$ofs_x.'+'.$ofs_y;
2324 if ($ext == 'jpg' ||
$ext == 'jpeg') {
2325 $cmd .= ' -quality '.$conf['jpg_quality'];
2327 $cmd .= " $from $to";
2329 @exec
($cmd,$out,$retval);
2330 if ($retval == 0) return true;
2335 * resize or crop images using PHP's libGD support
2337 * @author Andreas Gohr <andi@splitbrain.org>
2338 * @author Sebastian Wienecke <s_wienecke@web.de>
2340 * @param string $ext extension
2341 * @param string $from filename path to file
2342 * @param int $from_w original width
2343 * @param int $from_h original height
2344 * @param string $to path to resized file
2345 * @param int $to_w desired width
2346 * @param int $to_h desired height
2347 * @param int $ofs_x offset of crop centre
2348 * @param int $ofs_y offset of crop centre
2351 function media_resize_imageGD($ext,$from,$from_w,$from_h,$to,$to_w,$to_h,$ofs_x=0,$ofs_y=0){
2354 if($conf['gdlib'] < 1) return false; //no GDlib available or wanted
2356 // check available memory
2357 if(!is_mem_available(($from_w * $from_h * 4) +
($to_w * $to_h * 4))){
2361 // create an image of the given filetype
2363 if ($ext == 'jpg' ||
$ext == 'jpeg'){
2364 if(!function_exists("imagecreatefromjpeg")) return false;
2365 $image = @imagecreatefromjpeg
($from);
2366 }elseif($ext == 'png') {
2367 if(!function_exists("imagecreatefrompng")) return false;
2368 $image = @imagecreatefrompng
($from);
2370 }elseif($ext == 'gif') {
2371 if(!function_exists("imagecreatefromgif")) return false;
2372 $image = @imagecreatefromgif
($from);
2374 if(!$image) return false;
2377 if(($conf['gdlib']>1) && function_exists("imagecreatetruecolor") && $ext != 'gif'){
2378 $newimg = @imagecreatetruecolor
($to_w, $to_h);
2380 if(!$newimg) $newimg = @imagecreate
($to_w, $to_h);
2382 imagedestroy($image);
2386 //keep png alpha channel if possible
2387 if($ext == 'png' && $conf['gdlib']>1 && function_exists('imagesavealpha')){
2388 imagealphablending($newimg, false);
2389 imagesavealpha($newimg,true);
2392 //keep gif transparent color if possible
2393 if($ext == 'gif' && function_exists('imagefill') && function_exists('imagecolorallocate')) {
2394 if(function_exists('imagecolorsforindex') && function_exists('imagecolortransparent')) {
2395 $transcolorindex = @imagecolortransparent
($image);
2396 if($transcolorindex >= 0 ) { //transparent color exists
2397 $transcolor = @imagecolorsforindex
($image, $transcolorindex);
2398 $transcolorindex = @imagecolorallocate
(
2401 $transcolor['green'],
2404 @imagefill
($newimg, 0, 0, $transcolorindex);
2405 @imagecolortransparent
($newimg, $transcolorindex);
2406 }else{ //filling with white
2407 $whitecolorindex = @imagecolorallocate
($newimg, 255, 255, 255);
2408 @imagefill
($newimg, 0, 0, $whitecolorindex);
2410 }else{ //filling with white
2411 $whitecolorindex = @imagecolorallocate
($newimg, 255, 255, 255);
2412 @imagefill
($newimg, 0, 0, $whitecolorindex);
2416 //try resampling first
2417 if(function_exists("imagecopyresampled")){
2418 if(!@imagecopyresampled
($newimg, $image, 0, 0, $ofs_x, $ofs_y, $to_w, $to_h, $from_w, $from_h)) {
2419 imagecopyresized($newimg, $image, 0, 0, $ofs_x, $ofs_y, $to_w, $to_h, $from_w, $from_h);
2422 imagecopyresized($newimg, $image, 0, 0, $ofs_x, $ofs_y, $to_w, $to_h, $from_w, $from_h);
2426 if ($ext == 'jpg' ||
$ext == 'jpeg'){
2427 if(!function_exists('imagejpeg')){
2430 $okay = imagejpeg($newimg, $to, $conf['jpg_quality']);
2432 }elseif($ext == 'png') {
2433 if(!function_exists('imagepng')){
2436 $okay = imagepng($newimg, $to);
2438 }elseif($ext == 'gif') {
2439 if(!function_exists('imagegif')){
2442 $okay = imagegif($newimg, $to);
2446 // destroy GD image ressources
2447 if($image) imagedestroy($image);
2448 if($newimg) imagedestroy($newimg);
2454 * Return other media files with the same base name
2455 * but different extensions.
2457 * @param string $src - ID of media file
2458 * @param string[] $exts - alternative extensions to find other files for
2459 * @return array - array(mime type => file ID)
2461 * @author Anika Henke <anika@selfthinker.org>
2463 function media_alternativefiles($src, $exts){
2466 list($srcExt, /* $srcMime */) = mimetype($src);
2467 $filebase = substr($src, 0, -1 * (strlen($srcExt)+
1));
2469 foreach($exts as $ext) {
2470 $fileid = $filebase.'.'.$ext;
2471 $file = mediaFN($fileid);
2472 if(file_exists($file)) {
2473 list(/* $fileExt */, $fileMime) = mimetype($file);
2474 $files[$fileMime] = $fileid;
2481 * Check if video/audio is supported to be embedded.
2483 * @param string $mime - mimetype of media file
2484 * @param string $type - type of media files to check ('video', 'audio', or null for all)
2487 * @author Anika Henke <anika@selfthinker.org>
2489 function media_supportedav($mime, $type=NULL){
2490 $supportedAudio = array(
2491 'ogg' => 'audio/ogg',
2492 'mp3' => 'audio/mpeg',
2493 'wav' => 'audio/wav',
2495 $supportedVideo = array(
2496 'webm' => 'video/webm',
2497 'ogv' => 'video/ogg',
2498 'mp4' => 'video/mp4',
2500 if ($type == 'audio') {
2501 $supportedAv = $supportedAudio;
2502 } elseif ($type == 'video') {
2503 $supportedAv = $supportedVideo;
2505 $supportedAv = array_merge($supportedAudio, $supportedVideo);
2507 return in_array($mime, $supportedAv);
2511 * Return track media files with the same base name
2512 * but extensions that indicate kind and lang.
2513 * ie for foo.webm search foo.sub.lang.vtt, foo.cap.lang.vtt...
2515 * @param string $src - ID of media file
2516 * @return array - array(mediaID => array( kind, srclang ))
2518 * @author Schplurtz le Déboulonné <Schplurtz@laposte.net>
2520 function media_trackfiles($src){
2522 'sub' => 'subtitles',
2523 'cap' => 'captions',
2524 'des' => 'descriptions',
2525 'cha' => 'chapters',
2530 $re='/\\.(sub|cap|des|cha|met)\\.([^.]+)\\.vtt$/';
2531 $baseid=pathinfo($src, PATHINFO_FILENAME
);
2532 $pattern=mediaFN($baseid).'.*.*.vtt';
2533 $list=glob($pattern);
2534 foreach($list as $track) {
2535 if(preg_match($re, $track, $matches)){
2536 $files[$baseid.'.'.$matches[1].'.'.$matches[2].'.vtt']=array(
2537 $kinds[$matches[1]],
2545 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */