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
;
13 use dokuwiki\Utf8\Sort
;
16 * Lists pages which currently use a media file selected for deletion
18 * References uses the same visual as search results and share
19 * their CSS tags except pagenames won't be links.
21 * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net>
26 function media_filesinuse($data,$id){
28 echo '<h1>'.$lang['reference'].' <code>'.hsc(noNS($id)).'</code></h1>';
29 echo '<p>'.hsc($lang['ref_inuse']).'</p>';
31 $hidden=0; //count of hits without read permission
32 foreach($data as $row){
33 if(auth_quickaclcheck($row) >= AUTH_READ
&& isVisiblePage($row)){
34 echo '<div class="search_result">';
35 echo '<span class="mediaref_ref">'.hsc($row).'</span>';
41 print '<div class="mediaref_hidden">'.$lang['ref_hidden'].'</div>';
46 * Handles the saving of image meta data
48 * @author Andreas Gohr <andi@splitbrain.org>
49 * @author Kate Arzamastseva <pshns@ukr.net>
51 * @param string $id media id
52 * @param int $auth permission level
54 * @return false|string
56 function media_metasave($id,$auth,$data){
57 if($auth < AUTH_UPLOAD
) return false;
58 if(!checkSecurityToken()) return false;
63 $meta = new JpegMeta($src);
66 foreach($data as $key => $val){
69 $meta->deleteField($key);
71 $meta->setField($key,$val);
75 $old = @filemtime
($src);
76 if(!file_exists(mediaFN($id, $old)) && file_exists($src)) {
77 // add old revision to the attic
78 media_saveOldRevision($id);
80 $filesize_old = filesize($src);
82 if($conf['fperm']) chmod($src, $conf['fperm']);
83 @clearstatcache
(true, $src);
84 $new = @filemtime
($src);
85 $filesize_new = filesize($src);
86 $sizechange = $filesize_new - $filesize_old;
88 // add a log entry to the media changelog
89 addMediaLogEntry($new, $id, DOKU_CHANGE_TYPE_EDIT
, $lang['media_meta_edited'], '', null, $sizechange);
91 msg($lang['metasaveok'],1);
94 msg($lang['metasaveerr'],-1);
100 * check if a media is external source
102 * @author Gerrit Uitslag <klapinklapin@gmail.com>
104 * @param string $id the media ID or URL
107 function media_isexternal($id){
108 if (preg_match('#^(?:https?|ftp)://#i', $id)) return true;
113 * Check if a media item is public (eg, external URL or readable by @ALL)
115 * @author Andreas Gohr <andi@splitbrain.org>
117 * @param string $id the media ID or URL
120 function media_ispublic($id){
121 if(media_isexternal($id)) return true;
123 if(auth_aclcheck(getNS($id).':*', '', array()) >= AUTH_READ
) return true;
128 * Display the form to edit image meta data
130 * @author Andreas Gohr <andi@splitbrain.org>
131 * @author Kate Arzamastseva <pshns@ukr.net>
133 * @param string $id media id
134 * @param int $auth permission level
137 function media_metaform($id,$auth){
140 if($auth < AUTH_UPLOAD
) {
141 echo '<div class="nothing">'.$lang['media_perm_upload'].'</div>'.NL
;
145 // load the field descriptions
146 static $fields = null;
147 if(is_null($fields)){
148 $config_files = getConfigFiles('mediameta');
149 foreach ($config_files as $config_file) {
150 if(file_exists($config_file)) include($config_file);
157 $form = new Doku_Form(array('action' => media_managerURL(array('tab_details' => 'view'), '&'),
159 $form->addHidden('img', $id);
160 $form->addHidden('mediado', 'save');
161 foreach($fields as $key => $field){
163 if (empty($field[0])) continue;
164 $tags = array($field[0]);
165 if(is_array($field[3])) $tags = array_merge($tags,$field[3]);
166 $value = tpl_img_getTag($tags,'',$src);
167 $value = cleanText($value);
169 // prepare attributes
171 $p['class'] = 'edit';
172 $p['id'] = 'meta__'.$key;
173 $p['name'] = 'meta['.$field[0].']';
174 $p_attrs = array('class' => 'edit');
176 $form->addElement('<div class="row">');
177 if($field[2] == 'text'){
183 ($lang[$field[1]]) ?
$lang[$field[1]] : $field[1] . ':',
190 $att = buildAttributes($p);
191 $form->addElement('<label for="meta__'.$key.'">'.$lang[$field[1]].'</label>');
192 $form->addElement("<textarea $att rows=\"6\" cols=\"50\">".formText($value).'</textarea>');
194 $form->addElement('</div>'.NL
);
196 $form->addElement('<div class="buttons">');
202 array('accesskey' => 's', 'name' => 'mediado[save]')
205 $form->addElement('</div>'.NL
);
212 * Convenience function to check if a media file is still in use
214 * @author Michael Klier <chi@chimeric.de>
216 * @param string $id media id
219 function media_inuse($id) {
222 if($conf['refcheck']){
223 $mediareferences = ft_mediause($id,true);
224 if(!count($mediareferences)) {
227 return $mediareferences;
235 * Handles media file deletions
237 * If configured, checks for media references before deletion
239 * @author Andreas Gohr <andi@splitbrain.org>
241 * @param string $id media id
242 * @param int $auth no longer used
243 * @return int One of: 0,
244 * DOKU_MEDIA_DELETED,
245 * DOKU_MEDIA_DELETED | DOKU_MEDIA_EMPTY_NS,
246 * DOKU_MEDIA_NOT_AUTH,
249 function media_delete($id,$auth){
251 $auth = auth_quickaclcheck(ltrim(getNS($id).':*', ':'));
252 if($auth < AUTH_DELETE
) return DOKU_MEDIA_NOT_AUTH
;
253 if(media_inuse($id)) return DOKU_MEDIA_INUSE
;
255 $file = mediaFN($id);
257 // trigger an event - MEDIA_DELETE_FILE
260 $data['name'] = \dokuwiki\Utf8\PhpString
::basename($file);
261 $data['path'] = $file;
262 $data['size'] = (file_exists($file)) ?
filesize($file) : 0;
264 $data['unl'] = false;
265 $data['del'] = false;
266 $evt = new Event('MEDIA_DELETE_FILE',$data);
267 if ($evt->advise_before()) {
268 $old = @filemtime
($file);
269 if(!file_exists(mediaFN($id, $old)) && file_exists($file)) {
270 // add old revision to the attic
271 media_saveOldRevision($id);
274 $data['unl'] = @unlink
($file);
276 $sizechange = 0 - $data['size'];
277 addMediaLogEntry(time(), $id, DOKU_CHANGE_TYPE_DELETE
, $lang['deleted'], '', null, $sizechange);
279 $data['del'] = io_sweepNS($id, 'mediadir');
282 $evt->advise_after();
285 if($data['unl'] && $data['del']){
286 return DOKU_MEDIA_DELETED | DOKU_MEDIA_EMPTY_NS
;
289 return $data['unl'] ? DOKU_MEDIA_DELETED
: 0;
293 * Handle file uploads via XMLHttpRequest
295 * @param string $ns target namespace
296 * @param int $auth current auth check result
297 * @return false|string false on error, id of the new file on success
299 function media_upload_xhr($ns,$auth){
300 if(!checkSecurityToken()) return false;
303 $id = $INPUT->get
->str('qqfile');
304 list($ext,$mime) = mimetype($id);
305 $input = fopen("php://input", "r");
306 if (!($tmp = io_mktmpdir())) return false;
307 $path = $tmp.'/'.md5($id);
308 $target = fopen($path, "w");
309 $realSize = stream_copy_to_stream($input, $target);
312 if (isset($_SERVER["CONTENT_LENGTH"]) && ($realSize != (int)$_SERVER["CONTENT_LENGTH"])){
318 array('name' => $path,
322 (($INPUT->get
->str('ow') == 'true') ?
true : false),
327 if ($tmp) io_rmdir($tmp, true);
328 if (is_array($res)) {
329 msg($res[0], $res[1]);
336 * Handles media file uploads
338 * @author Andreas Gohr <andi@splitbrain.org>
339 * @author Michael Klier <chi@chimeric.de>
341 * @param string $ns target namespace
342 * @param int $auth current auth check result
343 * @param bool|array $file $_FILES member, $_FILES['upload'] if false
344 * @return false|string false on error, id of the new file on success
346 function media_upload($ns,$auth,$file=false){
347 if(!checkSecurityToken()) return false;
352 $id = $INPUT->post
->str('mediaid');
353 if (!$file) $file = $_FILES['upload'];
354 if(empty($id)) $id = $file['name'];
356 // check for errors (messages are done in lib/exe/mediamanager.php)
357 if($file['error']) return false;
360 list($fext,$fmime) = mimetype($file['name']);
361 list($iext,$imime) = mimetype($id);
363 // no extension specified in id - read original one
366 }elseif($fext && $fext != $iext){
367 // extension was changed, print warning
368 msg(sprintf($lang['mediaextchange'],$fext,$iext));
371 $res = media_save(array('name' => $file['tmp_name'],
373 'ext' => $iext), $ns.':'.$id,
374 $INPUT->post
->bool('ow'), $auth, 'copy_uploaded_file');
375 if (is_array($res)) {
376 msg($res[0], $res[1]);
383 * An alternative to move_uploaded_file that copies
385 * Using copy, makes sure any setgid bits on the media directory are honored
387 * @see move_uploaded_file()
389 * @param string $from
393 function copy_uploaded_file($from, $to){
394 if(!is_uploaded_file($from)) return false;
395 $ok = copy($from, $to);
401 * This generates an action event and delegates to _media_upload_action().
402 * Action plugins are allowed to pre/postprocess the uploaded file.
403 * (The triggered event is preventable.)
406 * $data[0] fn_tmp: the temporary file name (read from $_FILES)
407 * $data[1] fn: the file name of the uploaded file
408 * $data[2] id: the future directory id of the uploaded file
409 * $data[3] imime: the mimetype of the uploaded file
410 * $data[4] overwrite: if an existing file is going to be overwritten
411 * $data[5] move: name of function that performs move/copy/..
413 * @triggers MEDIA_UPLOAD_FINISH
416 * @param string $id media id
417 * @param bool $ow overwrite?
418 * @param int $auth permission level
419 * @param string $move name of functions that performs move/copy/..
420 * @return false|array|string
422 function media_save($file, $id, $ow, $auth, $move) {
423 if($auth < AUTH_UPLOAD
) {
424 return array("You don't have permissions to upload files.", -1);
427 if (!isset($file['mime']) ||
!isset($file['ext'])) {
428 list($ext, $mime) = mimetype($id);
429 if (!isset($file['mime'])) {
430 $file['mime'] = $mime;
432 if (!isset($file['ext'])) {
443 // get filetype regexp
444 $types = array_keys(getMimeTypes());
447 return preg_quote($q, "/");
451 $regex = join('|',$types);
453 // because a temp file was created already
454 if(!preg_match('/\.('.$regex.')$/i',$fn)) {
455 return array($lang['uploadwrong'],-1);
458 //check for overwrite
459 $overwrite = file_exists($fn);
460 $auth_ow = (($conf['mediarevisions']) ? AUTH_UPLOAD
: AUTH_DELETE
);
461 if($overwrite && (!$ow ||
$auth < $auth_ow)) {
462 return array($lang['uploadexist'], 0);
464 // check for valid content
465 $ok = media_contentcheck($file['name'], $file['mime']);
467 return array(sprintf($lang['uploadbadcontent'],'.' . $file['ext']),-1);
469 return array($lang['uploadspam'],-1);
471 return array($lang['uploadxss'],-1);
474 // prepare event data
476 $data[0] = $file['name'];
479 $data[3] = $file['mime'];
480 $data[4] = $overwrite;
484 return Event
::createAndTrigger('MEDIA_UPLOAD_FINISH', $data, '_media_upload_action', true);
488 * Callback adapter for media_upload_finish() triggered by MEDIA_UPLOAD_FINISH
490 * @author Michael Klier <chi@chimeric.de>
492 * @param array $data event data
493 * @return false|array|string
495 function _media_upload_action($data) {
496 // fixme do further sanity tests of given data?
497 if(is_array($data) && count($data)===6) {
498 return media_upload_finish($data[0], $data[1], $data[2], $data[3], $data[4], $data[5]);
500 return false; //callback error
505 * Saves an uploaded media file
507 * @author Andreas Gohr <andi@splitbrain.org>
508 * @author Michael Klier <chi@chimeric.de>
509 * @author Kate Arzamastseva <pshns@ukr.net>
511 * @param string $fn_tmp
513 * @param string $id media id
514 * @param string $imime mime type
515 * @param bool $overwrite overwrite existing?
516 * @param string $move function name
517 * @return array|string
519 function media_upload_finish($fn_tmp, $fn, $id, $imime, $overwrite, $move = 'move_uploaded_file') {
524 $old = @filemtime
($fn);
525 if(!file_exists(mediaFN($id, $old)) && file_exists($fn)) {
526 // add old revision to the attic if missing
527 media_saveOldRevision($id);
531 io_createNamespace($id, 'media');
533 $filesize_old = file_exists($fn) ?
filesize($fn) : 0;
535 if($move($fn_tmp, $fn)) {
536 @clearstatcache
(true,$fn);
537 $new = @filemtime
($fn);
538 // Set the correct permission here.
539 // Always chmod media because they may be saved with different permissions than expected from the php umask.
540 // (Should normally chmod to $conf['fperm'] only if $conf['fperm'] is set.)
541 chmod($fn, $conf['fmode']);
542 msg($lang['uploadsucc'],1);
543 media_notify($id,$fn,$imime,$old,$new);
544 // add a log entry to the media changelog
545 $filesize_new = filesize($fn);
546 $sizechange = $filesize_new - $filesize_old;
551 DOKU_CHANGE_TYPE_REVERT
,
552 sprintf($lang['restored'], dformat($REV)),
557 } elseif($overwrite) {
558 addMediaLogEntry($new, $id, DOKU_CHANGE_TYPE_EDIT
, '', '', null, $sizechange);
560 addMediaLogEntry($new, $id, DOKU_CHANGE_TYPE_CREATE
, $lang['created'], '', null, $sizechange);
564 return array($lang['uploadfail'],-1);
569 * Moves the current version of media file to the media_attic
572 * @author Kate Arzamastseva <pshns@ukr.net>
575 * @return int - revision date
577 function media_saveOldRevision($id){
580 $oldf = mediaFN($id);
581 if(!file_exists($oldf)) return '';
582 $date = filemtime($oldf);
583 if (!$conf['mediarevisions']) return $date;
585 $medialog = new MediaChangeLog($id);
586 if (!$medialog->getRevisionInfo($date)) {
587 // there was an external edit,
588 // there is no log entry for current version of file
589 $sizechange = filesize($oldf);
590 if(!file_exists(mediaMetaFN($id, '.changes'))) {
591 addMediaLogEntry($date, $id, DOKU_CHANGE_TYPE_CREATE
, $lang['created'], '', null, $sizechange);
593 $oldRev = $medialog->getRevisions(-1, 1); // from changelog
594 $oldRev = (int) (empty($oldRev) ?
0 : $oldRev[0]);
595 $filesize_old = filesize(mediaFN($id, $oldRev));
596 $sizechange = $sizechange - $filesize_old;
598 addMediaLogEntry($date, $id, DOKU_CHANGE_TYPE_EDIT
, '', '', null, $sizechange);
602 $newf = mediaFN($id,$date);
603 io_makeFileDir($newf);
604 if(copy($oldf, $newf)) {
605 // Set the correct permission here.
606 // Always chmod media because they may be saved with different permissions than expected from the php umask.
607 // (Should normally chmod to $conf['fperm'] only if $conf['fperm'] is set.)
608 chmod($newf, $conf['fmode']);
614 * This function checks if the uploaded content is really what the
615 * mimetype says it is. We also do spam checking for text types here.
617 * We need to do this stuff because we can not rely on the browser
618 * to do this check correctly. Yes, IE is broken as usual.
620 * @author Andreas Gohr <andi@splitbrain.org>
621 * @link http://www.splitbrain.org/blog/2007-02/12-internet_explorer_facilitates_cross_site_scripting
622 * @fixme check all 26 magic IE filetypes here?
624 * @param string $file path to file
625 * @param string $mime mimetype
628 function media_contentcheck($file,$mime){
630 if($conf['iexssprotect']){
631 $fh = @fopen
($file, 'rb');
633 $bytes = fread($fh, 256);
635 if(preg_match('/<(script|a|img|html|body|iframe)[\s>]/i',$bytes)){
636 return -3; //XSS: possibly malicious content
640 if(substr($mime,0,6) == 'image/'){
641 $info = @getimagesize
($file);
642 if($mime == 'image/gif' && $info[2] != 1){
643 return -1; // uploaded content did not match the file extension
644 }elseif($mime == 'image/jpeg' && $info[2] != 2){
646 }elseif($mime == 'image/png' && $info[2] != 3){
649 # fixme maybe check other images types as well
650 }elseif(substr($mime,0,5) == 'text/'){
652 $TEXT = io_readFile($file);
653 if(checkwordblock()){
654 return -2; //blocked by the spam blacklist
661 * Send a notify mail on uploads
663 * @author Andreas Gohr <andi@splitbrain.org>
665 * @param string $id media id
666 * @param string $file path to file
667 * @param string $mime mime type
668 * @param bool|int $old_rev revision timestamp or false
671 function media_notify($id,$file,$mime,$old_rev=false,$current_rev=false){
673 if(empty($conf['notify'])) return false; //notify enabled?
675 $subscription = new MediaSubscriptionSender();
676 return $subscription->sendMediaDiff($conf['notify'], 'uploadmail', $id, $old_rev, $current_rev);
680 * List all files in a given Media namespace
682 * @param string $ns namespace
683 * @param null|int $auth permission level
684 * @param string $jump id
685 * @param bool $fullscreenview
686 * @param bool|string $sort sorting order, false skips sorting
688 function media_filelist($ns,$auth=null,$jump='',$fullscreenview=false,$sort=false){
693 // check auth our self if not given (needed for ajax calls)
694 if(is_null($auth)) $auth = auth_quickaclcheck("$ns:*");
696 if (!$fullscreenview) echo '<h1 id="media__ns">:'.hsc($ns).'</h1>'.NL
;
698 if($auth < AUTH_READ
){
699 // FIXME: print permission warning here instead?
700 echo '<div class="nothing">'.$lang['nothingfound'].'</div>'.NL
;
702 if (!$fullscreenview) {
703 media_uploadform($ns, $auth);
704 media_searchform($ns);
707 $dir = utf8_encodeFN(str_replace(':','/',$ns));
709 search($data,$conf['mediadir'],'search_media',
710 array('showmsg'=>true,'depth'=>1),$dir,1,$sort);
713 echo '<div class="nothing">'.$lang['nothingfound'].'</div>'.NL
;
715 if ($fullscreenview) {
716 echo '<ul class="' . _media_get_list_type() . '">';
718 foreach($data as $item){
719 if (!$fullscreenview) {
720 media_printfile($item,$auth,$jump);
722 media_printfile_thumbs($item,$auth,$jump);
725 if ($fullscreenview) echo '</ul>'.NL
;
731 * Prints tabs for files list actions
733 * @author Kate Arzamastseva <pshns@ukr.net>
734 * @author Adrian Lang <mail@adrianlang.de>
736 * @param string $selected_tab - opened tab
739 function media_tabs_files($selected_tab = ''){
742 foreach(array('files' => 'mediaselect',
743 'upload' => 'media_uploadtab',
744 'search' => 'media_searchtab') as $tab => $caption) {
745 $tabs[$tab] = array('href' => media_managerURL(array('tab_files' => $tab), '&'),
746 'caption' => $lang[$caption]);
749 html_tabs($tabs, $selected_tab);
753 * Prints tabs for files details actions
755 * @author Kate Arzamastseva <pshns@ukr.net>
756 * @param string $image filename of the current image
757 * @param string $selected_tab opened tab
759 function media_tabs_details($image, $selected_tab = ''){
763 $tabs['view'] = array('href' => media_managerURL(array('tab_details' => 'view'), '&'),
764 'caption' => $lang['media_viewtab']);
766 list(, $mime) = mimetype($image);
767 if ($mime == 'image/jpeg' && file_exists(mediaFN($image))) {
768 $tabs['edit'] = array('href' => media_managerURL(array('tab_details' => 'edit'), '&'),
769 'caption' => $lang['media_edittab']);
771 if ($conf['mediarevisions']) {
772 $tabs['history'] = array('href' => media_managerURL(array('tab_details' => 'history'), '&'),
773 'caption' => $lang['media_historytab']);
776 html_tabs($tabs, $selected_tab);
780 * Prints options for the tab that displays a list of all files
782 * @author Kate Arzamastseva <pshns@ukr.net>
784 function media_tab_files_options(){
788 $form = new Doku_Form(array('class' => 'options', 'method' => 'get',
789 'action' => wl($ID)));
790 $media_manager_params = media_managerURL(array(), '', false, true);
791 foreach($media_manager_params as $pKey => $pVal){
792 $form->addHidden($pKey, $pVal);
794 $form->addHidden('sectok', null);
795 if ($INPUT->has('q')) {
796 $form->addHidden('q', $INPUT->str('q'));
798 $form->addElement('<ul>'.NL
);
799 foreach(array('list' => array('listType', array('thumbs', 'rows')),
800 'sort' => array('sortBy', array('name', 'date')))
801 as $group => $content) {
802 $checked = "_media_get_${group}_type";
803 $checked = $checked();
805 $form->addElement('<li class="' . $content[0] . '">');
806 foreach($content[1] as $option) {
808 if ($checked == $option) {
809 $attrs['checked'] = 'checked';
811 $form->addElement(form_makeRadioField($group . '_dwmedia', $option,
812 $lang['media_' . $group . '_' . $option],
813 $content[0] . '__' . $option,
816 $form->addElement('</li>'.NL
);
818 $form->addElement('<li>');
819 $form->addElement(form_makeButton('submit', '', $lang['btn_apply']));
820 $form->addElement('</li>'.NL
);
821 $form->addElement('</ul>'.NL
);
826 * Returns type of sorting for the list of files in media manager
828 * @author Kate Arzamastseva <pshns@ukr.net>
830 * @return string - sort type
832 function _media_get_sort_type() {
833 return _media_get_display_param('sort', array('default' => 'name', 'date'));
837 * Returns type of listing for the list of files in media manager
839 * @author Kate Arzamastseva <pshns@ukr.net>
841 * @return string - list type
843 function _media_get_list_type() {
844 return _media_get_display_param('list', array('default' => 'thumbs', 'rows'));
848 * Get display parameters
850 * @param string $param name of parameter
851 * @param array $values allowed values, where default value has index key 'default'
852 * @return string the parameter value
854 function _media_get_display_param($param, $values) {
856 if (in_array($INPUT->str($param), $values)) {
858 return $INPUT->str($param);
860 $val = get_doku_pref($param, $values['default']);
861 if (!in_array($val, $values)) {
862 $val = $values['default'];
869 * Prints tab that displays a list of all files
871 * @author Kate Arzamastseva <pshns@ukr.net>
874 * @param null|int $auth permission level
875 * @param string $jump item id
877 function media_tab_files($ns,$auth=null,$jump='') {
879 if(is_null($auth)) $auth = auth_quickaclcheck("$ns:*");
881 if($auth < AUTH_READ
){
882 echo '<div class="nothing">'.$lang['media_perm_read'].'</div>'.NL
;
884 media_filelist($ns,$auth,$jump,true,_media_get_sort_type());
889 * Prints tab that displays uploading form
891 * @author Kate Arzamastseva <pshns@ukr.net>
894 * @param null|int $auth permission level
895 * @param string $jump item id
897 function media_tab_upload($ns,$auth=null,$jump='') {
899 if(is_null($auth)) $auth = auth_quickaclcheck("$ns:*");
901 echo '<div class="upload">'.NL
;
902 if ($auth >= AUTH_UPLOAD
) {
903 echo '<p>' . $lang['mediaupload'] . '</p>';
905 media_uploadform($ns, $auth, true);
910 * Prints tab that displays search form
912 * @author Kate Arzamastseva <pshns@ukr.net>
915 * @param null|int $auth permission level
917 function media_tab_search($ns,$auth=null) {
920 $do = $INPUT->str('mediado');
921 $query = $INPUT->str('q');
922 echo '<div class="search">'.NL
;
924 media_searchform($ns, $query, true);
925 if ($do == 'searchlist' ||
$query) {
926 media_searchlist($query,$ns,$auth,true,_media_get_sort_type());
932 * Prints tab that displays mediafile details
934 * @author Kate Arzamastseva <pshns@ukr.net>
936 * @param string $image media id
938 * @param null|int $auth permission level
939 * @param string|int $rev revision timestamp or empty string
941 function media_tab_view($image, $ns, $auth=null, $rev='') {
943 if(is_null($auth)) $auth = auth_quickaclcheck("$ns:*");
945 if ($image && $auth >= AUTH_READ
) {
946 $meta = new JpegMeta(mediaFN($image, $rev));
947 media_preview($image, $auth, $rev, $meta);
948 media_preview_buttons($image, $auth, $rev);
949 media_details($image, $auth, $rev, $meta);
952 echo '<div class="nothing">'.$lang['media_perm_read'].'</div>'.NL
;
957 * Prints tab that displays form for editing mediafile metadata
959 * @author Kate Arzamastseva <pshns@ukr.net>
961 * @param string $image media id
963 * @param null|int $auth permission level
965 function media_tab_edit($image, $ns, $auth=null) {
966 if(is_null($auth)) $auth = auth_quickaclcheck("$ns:*");
969 list(, $mime) = mimetype($image);
970 if ($mime == 'image/jpeg') media_metaform($image,$auth);
975 * Prints tab that displays mediafile revisions
977 * @author Kate Arzamastseva <pshns@ukr.net>
979 * @param string $image media id
981 * @param null|int $auth permission level
983 function media_tab_history($image, $ns, $auth=null) {
987 if(is_null($auth)) $auth = auth_quickaclcheck("$ns:*");
988 $do = $INPUT->str('mediado');
990 if ($auth >= AUTH_READ
&& $image) {
992 media_diff($image, $ns, $auth);
994 $first = $INPUT->int('first');
995 html_revisions($first, $image);
998 echo '<div class="nothing">'.$lang['media_perm_read'].'</div>'.NL
;
1003 * Prints mediafile details
1005 * @param string $image media id
1006 * @param int $auth permission level
1007 * @param int|string $rev revision timestamp or empty string
1008 * @param JpegMeta|bool $meta
1010 * @author Kate Arzamastseva <pshns@ukr.net>
1012 function media_preview($image, $auth, $rev='', $meta=false) {
1014 $size = media_image_preview_size($image, $rev, $meta);
1018 echo '<div class="image">';
1022 $more['rev'] = $rev;
1024 $t = @filemtime
(mediaFN($image));
1028 $more['w'] = $size[0];
1029 $more['h'] = $size[1];
1030 $src = ml($image, $more);
1032 echo '<a href="'.$src.'" target="_blank" title="'.$lang['mediaview'].'">';
1033 echo '<img src="'.$src.'" alt="" style="max-width: '.$size[0].'px;" />';
1041 * Prints mediafile action buttons
1043 * @author Kate Arzamastseva <pshns@ukr.net>
1045 * @param string $image media id
1046 * @param int $auth permission level
1047 * @param string|int $rev revision timestamp, or empty string
1049 function media_preview_buttons($image, $auth, $rev='') {
1050 global $lang, $conf;
1052 echo '<ul class="actions">'.NL
;
1054 if($auth >= AUTH_DELETE
&& !$rev && file_exists(mediaFN($image))){
1057 $form = new Doku_Form(array('id' => 'mediamanager__btn_delete',
1058 'action'=>media_managerURL(array('delete' => $image), '&')));
1059 $form->addElement(form_makeButton('submit','',$lang['btn_delete']));
1065 $auth_ow = (($conf['mediarevisions']) ? AUTH_UPLOAD
: AUTH_DELETE
);
1066 if($auth >= $auth_ow && !$rev){
1068 // upload new version button
1069 $form = new Doku_Form(array('id' => 'mediamanager__btn_update',
1070 'action'=>media_managerURL(array('image' => $image, 'mediado' => 'update'), '&')));
1071 $form->addElement(form_makeButton('submit','',$lang['media_update']));
1077 if($auth >= AUTH_UPLOAD
&& $rev && $conf['mediarevisions'] && file_exists(mediaFN($image, $rev))){
1080 $form = new Doku_Form(array('id' => 'mediamanager__btn_restore',
1081 'action'=>media_managerURL(array('image' => $image), '&')));
1082 $form->addHidden('mediado','restore');
1083 $form->addHidden('rev',$rev);
1084 $form->addElement(form_makeButton('submit','',$lang['media_restore']));
1094 * Returns image width and height for mediamanager preview panel
1096 * @author Kate Arzamastseva <pshns@ukr.net>
1097 * @param string $image
1098 * @param int|string $rev
1099 * @param JpegMeta|bool $meta
1101 * @return array|false
1103 function media_image_preview_size($image, $rev, $meta, $size = 500) {
1104 if (!preg_match("/\.(jpe?g|gif|png)$/", $image) ||
!file_exists(mediaFN($image, $rev))) return false;
1106 $info = getimagesize(mediaFN($image, $rev));
1107 $w = (int) $info[0];
1108 $h = (int) $info[1];
1110 if($meta && ($w > $size ||
$h > $size)){
1111 $ratio = $meta->getResizeRatio($size, $size);
1112 $w = floor($w * $ratio);
1113 $h = floor($h * $ratio);
1115 return array($w, $h);
1119 * Returns the requested EXIF/IPTC tag from the image meta
1121 * @author Kate Arzamastseva <pshns@ukr.net>
1123 * @param array $tags array with tags, first existing is returned
1124 * @param JpegMeta $meta
1125 * @param string $alt alternative value
1128 function media_getTag($tags,$meta,$alt=''){
1129 if($meta === false) return $alt;
1130 $info = $meta->getField($tags);
1131 if($info == false) return $alt;
1136 * Returns mediafile tags
1138 * @author Kate Arzamastseva <pshns@ukr.net>
1140 * @param JpegMeta $meta
1141 * @return array list of tags of the mediafile
1143 function media_file_tags($meta) {
1144 // load the field descriptions
1145 static $fields = null;
1146 if(is_null($fields)){
1147 $config_files = getConfigFiles('mediameta');
1148 foreach ($config_files as $config_file) {
1149 if(file_exists($config_file)) include($config_file);
1155 foreach($fields as $key => $tag){
1157 if (!empty($tag[0])) $t = array($tag[0]);
1158 if(isset($tag[3]) && is_array($tag[3])) $t = array_merge($t,$tag[3]);
1159 $value = media_getTag($t, $meta);
1160 $tags[] = array('tag' => $tag, 'value' => $value);
1167 * Prints mediafile tags
1169 * @author Kate Arzamastseva <pshns@ukr.net>
1171 * @param string $image image id
1172 * @param int $auth permission level
1173 * @param string|int $rev revision timestamp, or empty string
1174 * @param bool|JpegMeta $meta image object, or create one if false
1176 function media_details($image, $auth, $rev='', $meta=false) {
1179 if (!$meta) $meta = new JpegMeta(mediaFN($image, $rev));
1180 $tags = media_file_tags($meta);
1183 foreach($tags as $tag){
1184 if ($tag['value']) {
1185 $value = cleanText($tag['value']);
1186 echo '<dt>'.$lang[$tag['tag'][1]].'</dt><dd>';
1187 if ($tag['tag'][2] == 'date') echo dformat($value);
1188 else echo hsc($value);
1194 echo '<dt>'.$lang['reference'].':</dt>';
1195 $media_usage = ft_mediause($image,true);
1196 if(count($media_usage) > 0){
1197 foreach($media_usage as $path){
1198 echo '<dd>'.html_wikilink($path).'</dd>';
1201 echo '<dd>'.$lang['nothingfound'].'</dd>';
1208 * Shows difference between two revisions of file
1210 * @author Kate Arzamastseva <pshns@ukr.net>
1212 * @param string $image image id
1214 * @param int $auth permission level
1215 * @param bool $fromajax
1216 * @return false|null|string
1218 function media_diff($image, $ns, $auth, $fromajax = false) {
1222 if ($auth < AUTH_READ ||
!$image ||
!$conf['mediarevisions']) return '';
1224 $rev1 = $INPUT->int('rev');
1226 $rev2 = $INPUT->ref('rev2');
1227 if(is_array($rev2)){
1228 $rev1 = (int) $rev2[0];
1229 $rev2 = (int) $rev2[1];
1236 $rev2 = $INPUT->int('rev2');
1239 if ($rev1 && !file_exists(mediaFN($image, $rev1))) $rev1 = false;
1240 if ($rev2 && !file_exists(mediaFN($image, $rev2))) $rev2 = false;
1242 if($rev1 && $rev2){ // two specific revisions wanted
1243 // make sure order is correct (older on the left)
1251 }elseif($rev1){ // single revision given, compare to current
1254 }else{ // no revision was given, compare previous to current
1256 $medialog = new MediaChangeLog($image);
1257 $revs = $medialog->getRevisions(0, 1);
1258 if (file_exists(mediaFN($image, $revs[0]))) {
1265 // prepare event data
1272 $data[5] = $fromajax;
1275 return Event
::createAndTrigger('MEDIA_DIFF', $data, '_media_file_diff', true);
1279 * Callback for media file diff
1281 * @param array $data event data
1282 * @return false|null
1284 function _media_file_diff($data) {
1285 if(is_array($data) && count($data)===6) {
1286 media_file_diff($data[0], $data[1], $data[2], $data[3], $data[4], $data[5]);
1293 * Shows difference between two revisions of image
1295 * @author Kate Arzamastseva <pshns@ukr.net>
1297 * @param string $image
1298 * @param string|int $l_rev revision timestamp, or empty string
1299 * @param string|int $r_rev revision timestamp, or empty string
1301 * @param int $auth permission level
1302 * @param bool $fromajax
1304 function media_file_diff($image, $l_rev, $r_rev, $ns, $auth, $fromajax){
1308 $l_meta = new JpegMeta(mediaFN($image, $l_rev));
1309 $r_meta = new JpegMeta(mediaFN($image, $r_rev));
1311 $is_img = preg_match('/\.(jpe?g|gif|png)$/', $image);
1313 $l_size = media_image_preview_size($image, $l_rev, $l_meta);
1314 $r_size = media_image_preview_size($image, $r_rev, $r_meta);
1315 $is_img = ($l_size && $r_size && ($l_size[0] >= 30 ||
$r_size[0] >= 30));
1317 $difftype = $INPUT->str('difftype');
1320 $form = new Doku_Form(array(
1321 'action' => media_managerURL(array(), '&'),
1323 'id' => 'mediamanager__form_diffview',
1324 'class' => 'diffView'
1326 $form->addHidden('sectok', null);
1327 $form->addElement('<input type="hidden" name="rev2[]" value="'.$l_rev.'" ></input>');
1328 $form->addElement('<input type="hidden" name="rev2[]" value="'.$r_rev.'" ></input>');
1329 $form->addHidden('mediado', 'diff');
1332 echo NL
.'<div id="mediamanager__diff" >'.NL
;
1335 if ($difftype == 'opacity' ||
$difftype == 'portions') {
1336 media_image_diff($image, $l_rev, $r_rev, $l_size, $r_size, $difftype);
1337 if (!$fromajax) echo '</div>';
1342 list($l_head, $r_head) = html_diff_head($l_rev, $r_rev, $image, true);
1348 <th
><?php
echo $l_head; ?
></th
>
1349 <th
><?php
echo $r_head; ?
></th
>
1353 echo '<tr class="image">';
1355 media_preview($image, $auth, $l_rev, $l_meta);
1359 media_preview($image, $auth, $r_rev, $r_meta);
1363 echo '<tr class="actions">';
1365 media_preview_buttons($image, $auth, $l_rev);
1369 media_preview_buttons($image, $auth, $r_rev);
1373 $l_tags = media_file_tags($l_meta);
1374 $r_tags = media_file_tags($r_meta);
1375 // FIXME r_tags-only stuff
1376 foreach ($l_tags as $key => $l_tag) {
1377 if ($l_tag['value'] != $r_tags[$key]['value']) {
1378 $r_tags[$key]['highlighted'] = true;
1379 $l_tags[$key]['highlighted'] = true;
1380 } else if (!$l_tag['value'] ||
!$r_tags[$key]['value']) {
1381 unset($r_tags[$key]);
1382 unset($l_tags[$key]);
1387 foreach(array($l_tags,$r_tags) as $tags){
1390 echo '<dl class="img_tags">';
1391 foreach($tags as $tag){
1392 $value = cleanText($tag['value']);
1393 if (!$value) $value = '-';
1394 echo '<dt>'.$lang[$tag['tag'][1]].'</dt>';
1396 if ($tag['highlighted']) {
1399 if ($tag['tag'][2] == 'date') echo dformat($value);
1400 else echo hsc($value);
1401 if ($tag['highlighted']) {
1415 if ($is_img && !$fromajax) echo '</div>';
1419 * Prints two images side by side
1422 * @author Kate Arzamastseva <pshns@ukr.net>
1424 * @param string $image image id
1425 * @param int $l_rev revision timestamp, or empty string
1426 * @param int $r_rev revision timestamp, or empty string
1427 * @param array $l_size array with width and height
1428 * @param array $r_size array with width and height
1429 * @param string $type
1431 function media_image_diff($image, $l_rev, $r_rev, $l_size, $r_size, $type) {
1432 if ($l_size != $r_size) {
1433 if ($r_size[0] > $l_size[0]) {
1438 $l_more = array('rev' => $l_rev, 'h' => $l_size[1], 'w' => $l_size[0]);
1439 $r_more = array('rev' => $r_rev, 'h' => $l_size[1], 'w' => $l_size[0]);
1441 $l_src = ml($image, $l_more);
1442 $r_src = ml($image, $r_more);
1445 echo '<div class="slider" style="max-width: '.($l_size[0]-20).'px;" ></div>'.NL
;
1447 // two images in divs
1448 echo '<div class="imageDiff ' . $type . '">'.NL
;
1449 echo '<div class="image1" style="max-width: '.$l_size[0].'px;">';
1450 echo '<img src="'.$l_src.'" alt="" />';
1452 echo '<div class="image2" style="max-width: '.$l_size[0].'px;">';
1453 echo '<img src="'.$r_src.'" alt="" />';
1459 * Restores an old revision of a media file
1461 * @param string $image media id
1462 * @param int $rev revision timestamp or empty string
1464 * @return string - file's id
1466 * @author Kate Arzamastseva <pshns@ukr.net>
1468 function media_restore($image, $rev, $auth){
1470 if ($auth < AUTH_UPLOAD ||
!$conf['mediarevisions']) return false;
1471 $removed = (!file_exists(mediaFN($image)) && file_exists(mediaMetaFN($image, '.changes')));
1472 if (!$image ||
(!file_exists(mediaFN($image)) && !$removed)) return false;
1473 if (!$rev ||
!file_exists(mediaFN($image, $rev))) return false;
1474 list(,$imime,) = mimetype($image);
1475 $res = media_upload_finish(mediaFN($image, $rev),
1481 if (is_array($res)) {
1482 msg($res[0], $res[1]);
1489 * List all files found by the search request
1491 * @author Tobias Sarnowski <sarnowski@cosmocode.de>
1492 * @author Andreas Gohr <gohr@cosmocode.de>
1493 * @author Kate Arzamastseva <pshns@ukr.net>
1494 * @triggers MEDIA_SEARCH
1496 * @param string $query
1498 * @param null|int $auth
1499 * @param bool $fullscreen
1500 * @param string $sort
1502 function media_searchlist($query,$ns,$auth=null,$fullscreen=false,$sort='natural'){
1512 if (!blank($query)) {
1513 $evt = new Event('MEDIA_SEARCH', $evdata);
1514 if ($evt->advise_before()) {
1515 $dir = utf8_encodeFN(str_replace(':','/',$evdata['ns']));
1516 $quoted = preg_quote($evdata['query'],'/');
1518 $quoted = str_replace(array('\*', '\?'), array('.*', '.'), $quoted, $count);
1520 //if we use globbing file name must match entirely but may be preceded by arbitrary namespace
1521 if ($count > 0) $quoted = '^([^:]*:)*'.$quoted.'$';
1523 $pattern = '/'.$quoted.'/i';
1524 search($evdata['data'],
1527 array('showmsg'=>false,'pattern'=>$pattern),
1532 $evt->advise_after();
1537 echo '<h1 id="media__ns">'.sprintf($lang['searchmedia_in'],hsc($ns).':*').'</h1>'.NL
;
1538 media_searchform($ns,$query);
1541 if(!count($evdata['data'])){
1542 echo '<div class="nothing">'.$lang['nothingfound'].'</div>'.NL
;
1545 echo '<ul class="' . _media_get_list_type() . '">';
1547 foreach($evdata['data'] as $item){
1548 if (!$fullscreen) media_printfile($item,$item['perm'],'',true);
1549 else media_printfile_thumbs($item,$item['perm'],false,true);
1551 if ($fullscreen) echo '</ul>'.NL
;
1556 * Formats and prints one file in the list
1558 * @param array $item
1559 * @param int $auth permission level
1560 * @param string $jump item id
1561 * @param bool $display_namespace
1563 function media_printfile($item,$auth,$jump,$display_namespace=false){
1566 // Prepare zebra coloring
1567 // I always wanted to use this variable name :-D
1568 static $twibble = 1;
1570 $zebra = ($twibble == -1) ?
'odd' : 'even';
1572 // Automatically jump to recent action
1573 if($jump == $item['id']) {
1574 $jump = ' id="scroll__here" ';
1579 // Prepare fileicons
1580 list($ext) = mimetype($item['file'],false);
1581 $class = preg_replace('/[^_\-a-z0-9]+/i','_',$ext);
1582 $class = 'select mediafile mf_'.$class;
1585 $file = utf8_decodeFN($item['file']);
1590 $info .= (int) $item['meta']->getField('File.Width');
1592 $info .= (int) $item['meta']->getField('File.Height');
1595 $info .= '<i>'.dformat($item['mtime']).'</i>';
1597 $info .= filesize_h($item['size']);
1600 echo '<div class="'.$zebra.'"'.$jump.' title="'.hsc($item['id']).'">'.NL
;
1601 if (!$display_namespace) {
1602 echo '<a id="h_:'.$item['id'].'" class="'.$class.'">'.hsc($file).'</a> ';
1604 echo '<a id="h_:'.$item['id'].'" class="'.$class.'">'.hsc($item['id']).'</a><br/>';
1606 echo '<span class="info">('.$info.')</span>'.NL
;
1609 $link = ml($item['id'],'',true);
1610 echo ' <a href="'.$link.'" target="_blank"><img src="'.DOKU_BASE
.'lib/images/magnifier.png" '.
1611 'alt="'.$lang['mediaview'].'" title="'.$lang['mediaview'].'" class="btn" /></a>';
1613 // mediamanager button
1614 $link = wl('',array('do'=>'media','image'=>$item['id'],'ns'=>getNS($item['id'])));
1615 echo ' <a href="'.$link.'" target="_blank"><img src="'.DOKU_BASE
.'lib/images/mediamanager.png" '.
1616 'alt="'.$lang['btn_media'].'" title="'.$lang['btn_media'].'" class="btn" /></a>';
1619 if($item['writable'] && $auth >= AUTH_DELETE
){
1620 $link = DOKU_BASE
.'lib/exe/mediamanager.php?delete='.rawurlencode($item['id']).
1621 '&sectok='.getSecurityToken();
1622 echo ' <a href="'.$link.'" class="btn_media_delete" title="'.$item['id'].'">'.
1623 '<img src="'.DOKU_BASE
.'lib/images/trash.png" alt="'.$lang['btn_delete'].'" '.
1624 'title="'.$lang['btn_delete'].'" class="btn" /></a>';
1627 echo '<div class="example" id="ex_'.str_replace(':','_',$item['id']).'">';
1628 echo $lang['mediausage'].' <code>{{:'.$item['id'].'}}</code>';
1630 if($item['isimg']) media_printimgdetail($item);
1631 echo '<div class="clearer"></div>'.NL
;
1636 * Display a media icon
1638 * @param string $filename media id
1639 * @param string $size the size subfolder, if not specified 16x16 is used
1640 * @return string html
1642 function media_printicon($filename, $size=''){
1643 list($ext) = mimetype(mediaFN($filename),false);
1645 if (file_exists(DOKU_INC
.'lib/images/fileicons/'.$size.'/'.$ext.'.png')) {
1646 $icon = DOKU_BASE
.'lib/images/fileicons/'.$size.'/'.$ext.'.png';
1648 $icon = DOKU_BASE
.'lib/images/fileicons/'.$size.'/file.png';
1651 return '<img src="'.$icon.'" alt="'.$filename.'" class="icon" />';
1655 * Formats and prints one file in the list in the thumbnails view
1657 * @author Kate Arzamastseva <pshns@ukr.net>
1659 * @param array $item
1660 * @param int $auth permission level
1661 * @param bool|string $jump item id
1662 * @param bool $display_namespace
1664 function media_printfile_thumbs($item,$auth,$jump=false,$display_namespace=false){
1667 $file = utf8_decodeFN($item['file']);
1670 echo '<li><dl title="'.hsc($item['id']).'">'.NL
;
1673 if($item['isimg']) {
1674 media_printimgdetail($item, true);
1677 echo '<a id="d_:'.$item['id'].'" class="image" title="'.$item['id'].'" href="'.
1678 media_managerURL(array('image' => hsc($item['id']), 'ns' => getNS($item['id']),
1679 'tab_details' => 'view')).'">';
1680 echo media_printicon($item['id'], '32x32');
1684 if (!$display_namespace) {
1687 $name = hsc($item['id']);
1689 echo '<dd class="name"><a href="'.media_managerURL(array('image' => hsc($item['id']), 'ns' => getNS($item['id']),
1690 'tab_details' => 'view')).'" id="h_:'.$item['id'].'">'.$name.'</a></dd>'.NL
;
1694 $size .= (int) $item['meta']->getField('File.Width');
1696 $size .= (int) $item['meta']->getField('File.Height');
1697 echo '<dd class="size">'.$size.'</dd>'.NL
;
1699 echo '<dd class="size"> </dd>'.NL
;
1701 $date = dformat($item['mtime']);
1702 echo '<dd class="date">'.$date.'</dd>'.NL
;
1703 $filesize = filesize_h($item['size']);
1704 echo '<dd class="filesize">'.$filesize.'</dd>'.NL
;
1705 echo '</dl></li>'.NL
;
1709 * Prints a thumbnail and metainfo
1711 * @param array $item
1712 * @param bool $fullscreen
1714 function media_printimgdetail($item, $fullscreen=false){
1715 // prepare thumbnail
1716 $size = $fullscreen ?
90 : 120;
1718 $w = (int) $item['meta']->getField('File.Width');
1719 $h = (int) $item['meta']->getField('File.Height');
1720 if($w>$size ||
$h>$size){
1722 $ratio = $item['meta']->getResizeRatio($size);
1724 $ratio = $item['meta']->getResizeRatio($size,$size);
1726 $w = floor($w * $ratio);
1727 $h = floor($h * $ratio);
1729 $src = ml($item['id'],array('w'=>$w,'h'=>$h,'t'=>$item['mtime']));
1732 // In fullscreen mediamanager view, image resizing is done via CSS.
1736 $p['alt'] = $item['id'];
1737 $att = buildAttributes($p);
1741 echo '<a id="l_:'.$item['id'].'" class="image thumb" href="'.
1742 media_managerURL(['image' => hsc($item['id']), 'ns' => getNS($item['id']), 'tab_details' => 'view']).'">';
1743 echo '<img src="'.$src.'" '.$att.' />';
1747 if ($fullscreen) return;
1749 echo '<div class="detail">';
1750 echo '<div class="thumb">';
1751 echo '<a id="d_:'.$item['id'].'" class="select">';
1752 echo '<img src="'.$src.'" '.$att.' />';
1756 // read EXIF/IPTC data
1757 $t = $item['meta']->getField(array('IPTC.Headline','xmp.dc:title'));
1758 $d = $item['meta']->getField(array('IPTC.Caption','EXIF.UserComment',
1759 'EXIF.TIFFImageDescription',
1760 'EXIF.TIFFUserComment'));
1761 if(\dokuwiki\Utf8\PhpString
::strlen($d) > 250) $d = \dokuwiki\Utf8\PhpString
::substr($d,0,250).'...';
1762 $k = $item['meta']->getField(array('IPTC.Keywords','IPTC.Category','xmp.dc:subject'));
1764 // print EXIF/IPTC data
1765 if($t ||
$d ||
$k ){
1767 if($t) echo '<strong>'.hsc($t).'</strong><br />';
1768 if($d) echo hsc($d).'<br />';
1769 if($t) echo '<em>'.hsc($k).'</em>';
1776 * Build link based on the current, adding/rewriting parameters
1778 * @author Kate Arzamastseva <pshns@ukr.net>
1780 * @param array|bool $params
1781 * @param string $amp separator
1782 * @param bool $abs absolute url?
1783 * @param bool $params_array return the parmeters array?
1784 * @return string|array - link or link parameters
1786 function media_managerURL($params=false, $amp='&', $abs=false, $params_array=false) {
1790 $gets = array('do' => 'media');
1791 $media_manager_params = array('tab_files', 'tab_details', 'image', 'ns', 'list', 'sort');
1792 foreach ($media_manager_params as $x) {
1793 if ($INPUT->has($x)) $gets[$x] = $INPUT->str($x);
1797 $gets = $params +
$gets;
1800 if (isset($gets['delete'])) {
1801 unset($gets['image']);
1802 unset($gets['tab_details']);
1805 if ($params_array) return $gets;
1807 return wl($ID,$gets,$abs,$amp);
1811 * Print the media upload form if permissions are correct
1813 * @author Andreas Gohr <andi@splitbrain.org>
1814 * @author Kate Arzamastseva <pshns@ukr.net>
1817 * @param int $auth permission level
1818 * @param bool $fullscreen
1820 function media_uploadform($ns, $auth, $fullscreen = false){
1825 if($auth < AUTH_UPLOAD
) {
1826 echo '<div class="nothing">'.$lang['media_perm_upload'].'</div>'.NL
;
1829 $auth_ow = (($conf['mediarevisions']) ? AUTH_UPLOAD
: AUTH_DELETE
);
1833 if ($auth >= $auth_ow && $fullscreen && $INPUT->str('mediado') == 'update') {
1835 $id = cleanID($INPUT->str('image'));
1838 // The default HTML upload form
1839 $params = array('id' => 'dw__upload',
1840 'enctype' => 'multipart/form-data');
1842 $params['action'] = DOKU_BASE
.'lib/exe/mediamanager.php';
1844 $params['action'] = media_managerURL(array('tab_files' => 'files',
1845 'tab_details' => 'view'), '&');
1848 $form = new Doku_Form($params);
1849 if (!$fullscreen) echo '<div class="upload">' . $lang['mediaupload'] . '</div>';
1850 $form->addElement(formSecurityToken());
1851 $form->addHidden('ns', hsc($ns));
1852 $form->addElement(form_makeOpenTag('p'));
1853 $form->addElement(form_makeFileField('upload', $lang['txt_upload'], 'upload__file'));
1854 $form->addElement(form_makeCloseTag('p'));
1855 $form->addElement(form_makeOpenTag('p'));
1856 $form->addElement(form_makeTextField('mediaid', noNS($id), $lang['txt_filename'], 'upload__name'));
1857 $form->addElement(form_makeButton('submit', '', $lang['btn_upload']));
1858 $form->addElement(form_makeCloseTag('p'));
1860 if($auth >= $auth_ow){
1861 $form->addElement(form_makeOpenTag('p'));
1863 if ($update) $attrs['checked'] = 'checked';
1864 $form->addElement(form_makeCheckboxField('ow', 1, $lang['txt_overwrt'], 'dw__ow', 'check', $attrs));
1865 $form->addElement(form_makeCloseTag('p'));
1868 echo NL
.'<div id="mediamanager__uploader">'.NL
;
1869 html_form('upload', $form);
1873 echo '<p class="maxsize">';
1874 printf($lang['maxuploadsize'],filesize_h(media_getuploadsize()));
1875 echo ' <a class="allowedmime" href="#">' . $lang['allowedmime'] . '</a>';
1876 echo ' <span>' . implode(', ', array_keys(getMimeTypes())) .'</span>';
1881 * Returns the size uploaded files may have
1883 * This uses a conservative approach using the lowest number found
1884 * in any of the limiting ini settings
1886 * @returns int size in bytes
1888 function media_getuploadsize(){
1891 $post = (int) php_to_byte(@ini_get
('post_max_size'));
1892 $suho = (int) php_to_byte(@ini_get
('suhosin.post.max_value_length'));
1893 $upld = (int) php_to_byte(@ini_get
('upload_max_filesize'));
1895 if($post && ($post < $okay ||
$okay == 0)) $okay = $post;
1896 if($suho && ($suho < $okay ||
$okay == 0)) $okay = $suho;
1897 if($upld && ($upld < $okay ||
$okay == 0)) $okay = $upld;
1903 * Print the search field form
1905 * @author Tobias Sarnowski <sarnowski@cosmocode.de>
1906 * @author Kate Arzamastseva <pshns@ukr.net>
1909 * @param string $query
1910 * @param bool $fullscreen
1912 function media_searchform($ns,$query='',$fullscreen=false){
1915 // The default HTML search form
1916 $params = array('id' => 'dw__mediasearch');
1918 $params['action'] = DOKU_BASE
.'lib/exe/mediamanager.php';
1920 $params['action'] = media_managerURL(array(), '&');
1922 $form = new Doku_Form($params);
1923 $form->addHidden('ns', $ns);
1924 $form->addHidden($fullscreen ?
'mediado' : 'do', 'searchlist');
1926 $form->addElement(form_makeOpenTag('p'));
1931 $lang['searchmedia'],
1934 array('title' => sprintf($lang['searchmedia_in'], hsc($ns) . ':*'))
1937 $form->addElement(form_makeButton('submit', '', $lang['btn_search']));
1938 $form->addElement(form_makeCloseTag('p'));
1939 html_form('searchmedia', $form);
1943 * Build a tree outline of available media namespaces
1945 * @author Andreas Gohr <andi@splitbrain.org>
1949 function media_nstree($ns){
1953 // currently selected namespace
1957 $ns = (string)getNS($ID);
1960 $ns_dir = utf8_encodeFN(str_replace(':','/',$ns));
1963 search($data,$conf['mediadir'],'search_index',array('ns' => $ns_dir, 'nofiles' => true));
1965 // wrap a list with the root level around the other namespaces
1966 array_unshift($data, array('level' => 0, 'id' => '', 'open' =>'true',
1967 'label' => '['.$lang['mediaroot'].']'));
1969 // insert the current ns into the hierarchy if it isn't already part of it
1970 $ns_parts = explode(':', $ns);
1973 foreach ($ns_parts as $level => $part) {
1974 if ($tmp_ns) $tmp_ns .= ':'.$part;
1975 else $tmp_ns = $part;
1977 // find the namespace parts or insert them
1978 while ($data[$pos]['id'] != $tmp_ns) {
1980 $pos >= count($data) ||
1981 ($data[$pos]['level'] <= $level+
1 && Sort
::strcmp($data[$pos]['id'], $tmp_ns) > 0)
1983 array_splice($data, $pos, 0, array(array('level' => $level+
1, 'id' => $tmp_ns, 'open' => 'true')));
1990 echo html_buildlist($data,'idx','media_nstree_item','media_nstree_li');
1994 * Userfunction for html_buildlist
1996 * Prints a media namespace tree item
1998 * @author Andreas Gohr <andi@splitbrain.org>
2000 * @param array $item
2001 * @return string html
2003 function media_nstree_item($item){
2005 $pos = strrpos($item['id'], ':');
2006 $label = substr($item['id'], $pos > 0 ?
$pos +
1 : 0);
2007 if(empty($item['label'])) $item['label'] = $label;
2010 if (!($INPUT->str('do') == 'media'))
2011 $ret .= '<a href="'.DOKU_BASE
.'lib/exe/mediamanager.php?ns='.idfilter($item['id']).'" class="idx_dir">';
2012 else $ret .= '<a href="'.media_managerURL(array('ns' => idfilter($item['id'], false), 'tab_files' => 'files'))
2013 .'" class="idx_dir">';
2014 $ret .= $item['label'];
2020 * Userfunction for html_buildlist
2022 * Prints a media namespace tree item opener
2024 * @author Andreas Gohr <andi@splitbrain.org>
2026 * @param array $item
2027 * @return string html
2029 function media_nstree_li($item){
2030 $class='media level'.$item['level'];
2033 $img = DOKU_BASE
.'lib/images/minus.gif';
2036 $class .= ' closed';
2037 $img = DOKU_BASE
.'lib/images/plus.gif';
2040 // TODO: only deliver an image if it actually has a subtree...
2041 return '<li class="'.$class.'">'.
2042 '<img src="'.$img.'" alt="'.$alt.'" />';
2046 * Resizes the given image to the given size
2048 * @author Andreas Gohr <andi@splitbrain.org>
2050 * @param string $file filename, path to file
2051 * @param string $ext extension
2052 * @param int $w desired width
2053 * @param int $h desired height
2054 * @return string path to resized or original size if failed
2056 function media_resize_image($file, $ext, $w, $h=0){
2059 $info = @getimagesize
($file); //get original size
2060 if($info == false) return $file; // that's no image - it's a spaceship!
2062 if(!$h) $h = round(($w * $info[1]) / $info[0]);
2063 if(!$w) $w = round(($h * $info[0]) / $info[1]);
2065 // we wont scale up to infinity
2066 if($w > 2000 ||
$h > 2000) return $file;
2068 // resize necessary? - (w,h) = native dimensions
2069 if(($w == $info[0]) && ($h == $info[1])) return $file;
2072 $local = getCacheName($file,'.media.'.$w.'x'.$h.'.'.$ext);
2073 $mtime = @filemtime
($local); // 0 if not exists
2075 if($mtime > filemtime($file) ||
2076 media_resize_imageIM($ext, $file, $info[0], $info[1], $local, $w, $h) ||
2077 media_resize_imageGD($ext, $file, $info[0], $info[1], $local, $w, $h)
2079 if($conf['fperm']) @chmod
($local, $conf['fperm']);
2082 //still here? resizing failed
2087 * Crops the given image to the wanted ratio, then calls media_resize_image to scale it
2088 * to the wanted size
2090 * Crops are centered horizontally but prefer the upper third of an vertical
2091 * image because most pics are more interesting in that area (rule of thirds)
2093 * @author Andreas Gohr <andi@splitbrain.org>
2095 * @param string $file filename, path to file
2096 * @param string $ext extension
2097 * @param int $w desired width
2098 * @param int $h desired height
2099 * @return string path to resized or original size if failed
2101 function media_crop_image($file, $ext, $w, $h=0){
2105 $info = @getimagesize
($file); //get original size
2106 if($info == false) return $file; // that's no image - it's a spaceship!
2108 // calculate crop size
2109 $fr = $info[0]/$info[1];
2112 // check if the crop can be handled completely by resize,
2113 // i.e. the specified width & height match the aspect ratio of the source image
2114 if ($w == round($h*$fr)) {
2115 return media_resize_image($file, $ext, $w);
2121 $ch = (int) ($info[0]/$tr);
2123 $cw = (int) ($info[1]*$tr);
2128 $cw = (int) ($info[1]*$tr);
2132 $ch = (int) ($info[0]/$tr);
2135 // calculate crop offset
2136 $cx = (int) (($info[0]-$cw)/2);
2137 $cy = (int) (($info[1]-$ch)/3);
2140 $local = getCacheName($file,'.media.'.$cw.'x'.$ch.'.crop.'.$ext);
2141 $mtime = @filemtime
($local); // 0 if not exists
2143 if( $mtime > @filemtime
($file) ||
2144 media_crop_imageIM($ext,$file,$info[0],$info[1],$local,$cw,$ch,$cx,$cy) ||
2145 media_resize_imageGD($ext,$file,$cw,$ch,$local,$cw,$ch,$cx,$cy) ){
2146 if($conf['fperm']) @chmod
($local, $conf['fperm']);
2147 return media_resize_image($local,$ext, $w, $h);
2150 //still here? cropping failed
2151 return media_resize_image($file,$ext, $w, $h);
2155 * Calculate a token to be used to verify fetch requests for resized or
2156 * cropped images have been internally generated - and prevent external
2157 * DDOS attacks via fetch
2159 * @author Christopher Smith <chris@jalakai.co.uk>
2161 * @param string $id id of the image
2162 * @param int $w resize/crop width
2163 * @param int $h resize/crop height
2164 * @return string token or empty string if no token required
2166 function media_get_token($id,$w,$h){
2167 // token is only required for modified images
2168 if ($w ||
$h ||
media_isexternal($id)) {
2170 if ($w) $token .= '.'.$w;
2171 if ($h) $token .= '.'.$h;
2173 return substr(\dokuwiki\PassHash
::hmac('md5', $token, auth_cookiesalt()),0,6);
2180 * Download a remote file and return local filename
2182 * returns false if download fails. Uses cached file if available and
2185 * @author Andreas Gohr <andi@splitbrain.org>
2186 * @author Pavel Vitis <Pavel.Vitis@seznam.cz>
2188 * @param string $url
2189 * @param string $ext extension
2190 * @param int $cache cachetime in seconds
2191 * @return false|string path to cached file
2193 function media_get_from_URL($url,$ext,$cache){
2196 // if no cache or fetchsize just redirect
2197 if ($cache==0) return false;
2198 if (!$conf['fetchsize']) return false;
2200 $local = getCacheName(strtolower($url),".media.$ext");
2201 $mtime = @filemtime
($local); // 0 if not exists
2203 //decide if download needed:
2204 if(($mtime == 0) ||
// cache does not exist
2205 ($cache != -1 && $mtime < time() - $cache) // 'recache' and cache has expired
2207 if(media_image_download($url, $local)) {
2214 //if cache exists use it else
2215 if($mtime) return $local;
2222 * Download image files
2224 * @author Andreas Gohr <andi@splitbrain.org>
2226 * @param string $url
2227 * @param string $file path to file in which to put the downloaded content
2230 function media_image_download($url,$file){
2232 $http = new DokuHTTPClient();
2233 $http->keep_alive
= false; // we do single ops here, no need for keep-alive
2235 $http->max_bodysize
= $conf['fetchsize'];
2236 $http->timeout
= 25; //max. 25 sec
2237 $http->header_regexp
= '!\r\nContent-Type: image/(jpe?g|gif|png)!i';
2239 $data = $http->get($url);
2240 if(!$data) return false;
2242 $fileexists = file_exists($file);
2243 $fp = @fopen
($file,"w");
2244 if(!$fp) return false;
2247 if(!$fileexists and $conf['fperm']) chmod($file, $conf['fperm']);
2249 // check if it is really an image
2250 $info = @getimagesize
($file);
2260 * resize images using external ImageMagick convert program
2262 * @author Pavel Vitis <Pavel.Vitis@seznam.cz>
2263 * @author Andreas Gohr <andi@splitbrain.org>
2265 * @param string $ext extension
2266 * @param string $from filename path to file
2267 * @param int $from_w original width
2268 * @param int $from_h original height
2269 * @param string $to path to resized file
2270 * @param int $to_w desired width
2271 * @param int $to_h desired height
2274 function media_resize_imageIM($ext,$from,$from_w,$from_h,$to,$to_w,$to_h){
2277 // check if convert is configured
2278 if(!$conf['im_convert']) return false;
2281 $cmd = $conf['im_convert'];
2282 $cmd .= ' -resize '.$to_w.'x'.$to_h.'!';
2283 if ($ext == 'jpg' ||
$ext == 'jpeg') {
2284 $cmd .= ' -quality '.$conf['jpg_quality'];
2286 $cmd .= " $from $to";
2288 @exec
($cmd,$out,$retval);
2289 if ($retval == 0) return true;
2294 * crop images using external ImageMagick convert program
2296 * @author Andreas Gohr <andi@splitbrain.org>
2298 * @param string $ext extension
2299 * @param string $from filename path to file
2300 * @param int $from_w original width
2301 * @param int $from_h original height
2302 * @param string $to path to resized file
2303 * @param int $to_w desired width
2304 * @param int $to_h desired height
2305 * @param int $ofs_x offset of crop centre
2306 * @param int $ofs_y offset of crop centre
2309 function media_crop_imageIM($ext,$from,$from_w,$from_h,$to,$to_w,$to_h,$ofs_x,$ofs_y){
2312 // check if convert is configured
2313 if(!$conf['im_convert']) return false;
2316 $cmd = $conf['im_convert'];
2317 $cmd .= ' -crop '.$to_w.'x'.$to_h.'+'.$ofs_x.'+'.$ofs_y;
2318 if ($ext == 'jpg' ||
$ext == 'jpeg') {
2319 $cmd .= ' -quality '.$conf['jpg_quality'];
2321 $cmd .= " $from $to";
2323 @exec
($cmd,$out,$retval);
2324 if ($retval == 0) return true;
2329 * resize or crop images using PHP's libGD support
2331 * @author Andreas Gohr <andi@splitbrain.org>
2332 * @author Sebastian Wienecke <s_wienecke@web.de>
2334 * @param string $ext extension
2335 * @param string $from filename path to file
2336 * @param int $from_w original width
2337 * @param int $from_h original height
2338 * @param string $to path to resized file
2339 * @param int $to_w desired width
2340 * @param int $to_h desired height
2341 * @param int $ofs_x offset of crop centre
2342 * @param int $ofs_y offset of crop centre
2345 function media_resize_imageGD($ext,$from,$from_w,$from_h,$to,$to_w,$to_h,$ofs_x=0,$ofs_y=0){
2348 if($conf['gdlib'] < 1) return false; //no GDlib available or wanted
2350 // check available memory
2351 if(!is_mem_available(($from_w * $from_h * 4) +
($to_w * $to_h * 4))){
2355 // create an image of the given filetype
2357 if ($ext == 'jpg' ||
$ext == 'jpeg'){
2358 if(!function_exists("imagecreatefromjpeg")) return false;
2359 $image = @imagecreatefromjpeg
($from);
2360 }elseif($ext == 'png') {
2361 if(!function_exists("imagecreatefrompng")) return false;
2362 $image = @imagecreatefrompng
($from);
2364 }elseif($ext == 'gif') {
2365 if(!function_exists("imagecreatefromgif")) return false;
2366 $image = @imagecreatefromgif
($from);
2368 if(!$image) return false;
2371 if(($conf['gdlib']>1) && function_exists("imagecreatetruecolor") && $ext != 'gif'){
2372 $newimg = @imagecreatetruecolor
($to_w, $to_h);
2374 if(!$newimg) $newimg = @imagecreate
($to_w, $to_h);
2376 imagedestroy($image);
2380 //keep png alpha channel if possible
2381 if($ext == 'png' && $conf['gdlib']>1 && function_exists('imagesavealpha')){
2382 imagealphablending($newimg, false);
2383 imagesavealpha($newimg,true);
2386 //keep gif transparent color if possible
2387 if($ext == 'gif' && function_exists('imagefill') && function_exists('imagecolorallocate')) {
2388 if(function_exists('imagecolorsforindex') && function_exists('imagecolortransparent')) {
2389 $transcolorindex = @imagecolortransparent
($image);
2390 if($transcolorindex >= 0 ) { //transparent color exists
2391 $transcolor = @imagecolorsforindex
($image, $transcolorindex);
2392 $transcolorindex = @imagecolorallocate
(
2395 $transcolor['green'],
2398 @imagefill
($newimg, 0, 0, $transcolorindex);
2399 @imagecolortransparent
($newimg, $transcolorindex);
2400 }else{ //filling with white
2401 $whitecolorindex = @imagecolorallocate
($newimg, 255, 255, 255);
2402 @imagefill
($newimg, 0, 0, $whitecolorindex);
2404 }else{ //filling with white
2405 $whitecolorindex = @imagecolorallocate
($newimg, 255, 255, 255);
2406 @imagefill
($newimg, 0, 0, $whitecolorindex);
2410 //try resampling first
2411 if(function_exists("imagecopyresampled")){
2412 if(!@imagecopyresampled
($newimg, $image, 0, 0, $ofs_x, $ofs_y, $to_w, $to_h, $from_w, $from_h)) {
2413 imagecopyresized($newimg, $image, 0, 0, $ofs_x, $ofs_y, $to_w, $to_h, $from_w, $from_h);
2416 imagecopyresized($newimg, $image, 0, 0, $ofs_x, $ofs_y, $to_w, $to_h, $from_w, $from_h);
2420 if ($ext == 'jpg' ||
$ext == 'jpeg'){
2421 if(!function_exists('imagejpeg')){
2424 $okay = imagejpeg($newimg, $to, $conf['jpg_quality']);
2426 }elseif($ext == 'png') {
2427 if(!function_exists('imagepng')){
2430 $okay = imagepng($newimg, $to);
2432 }elseif($ext == 'gif') {
2433 if(!function_exists('imagegif')){
2436 $okay = imagegif($newimg, $to);
2440 // destroy GD image ressources
2441 if($image) imagedestroy($image);
2442 if($newimg) imagedestroy($newimg);
2448 * Return other media files with the same base name
2449 * but different extensions.
2451 * @param string $src - ID of media file
2452 * @param string[] $exts - alternative extensions to find other files for
2453 * @return array - array(mime type => file ID)
2455 * @author Anika Henke <anika@selfthinker.org>
2457 function media_alternativefiles($src, $exts){
2460 list($srcExt, /* $srcMime */) = mimetype($src);
2461 $filebase = substr($src, 0, -1 * (strlen($srcExt)+
1));
2463 foreach($exts as $ext) {
2464 $fileid = $filebase.'.'.$ext;
2465 $file = mediaFN($fileid);
2466 if(file_exists($file)) {
2467 list(/* $fileExt */, $fileMime) = mimetype($file);
2468 $files[$fileMime] = $fileid;
2475 * Check if video/audio is supported to be embedded.
2477 * @param string $mime - mimetype of media file
2478 * @param string $type - type of media files to check ('video', 'audio', or null for all)
2481 * @author Anika Henke <anika@selfthinker.org>
2483 function media_supportedav($mime, $type=NULL){
2484 $supportedAudio = array(
2485 'ogg' => 'audio/ogg',
2486 'mp3' => 'audio/mpeg',
2487 'wav' => 'audio/wav',
2489 $supportedVideo = array(
2490 'webm' => 'video/webm',
2491 'ogv' => 'video/ogg',
2492 'mp4' => 'video/mp4',
2494 if ($type == 'audio') {
2495 $supportedAv = $supportedAudio;
2496 } elseif ($type == 'video') {
2497 $supportedAv = $supportedVideo;
2499 $supportedAv = array_merge($supportedAudio, $supportedVideo);
2501 return in_array($mime, $supportedAv);
2505 * Return track media files with the same base name
2506 * but extensions that indicate kind and lang.
2507 * ie for foo.webm search foo.sub.lang.vtt, foo.cap.lang.vtt...
2509 * @param string $src - ID of media file
2510 * @return array - array(mediaID => array( kind, srclang ))
2512 * @author Schplurtz le Déboulonné <Schplurtz@laposte.net>
2514 function media_trackfiles($src){
2516 'sub' => 'subtitles',
2517 'cap' => 'captions',
2518 'des' => 'descriptions',
2519 'cha' => 'chapters',
2524 $re='/\\.(sub|cap|des|cha|met)\\.([^.]+)\\.vtt$/';
2525 $baseid=pathinfo($src, PATHINFO_FILENAME
);
2526 $pattern=mediaFN($baseid).'.*.*.vtt';
2527 $list=glob($pattern);
2528 foreach($list as $track) {
2529 if(preg_match($re, $track, $matches)){
2530 $files[$baseid.'.'.$matches[1].'.'.$matches[2].'.vtt']=array(
2531 $kinds[$matches[1]],
2539 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */