xref: /dokuwiki/inc/media.php (revision 25a7457ca09e648a58f9a5db5aa595253ce8a885)
1<?php
2/**
3 * All output and handler function needed for the media management popup
4 *
5 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
6 * @author     Andreas Gohr <andi@splitbrain.org>
7 */
8
9if(!defined('DOKU_INC')) die('meh.');
10if(!defined('NL')) define('NL',"\n");
11
12/**
13 * Lists pages which currently use a media file selected for deletion
14 *
15 * References uses the same visual as search results and share
16 * their CSS tags except pagenames won't be links.
17 *
18 * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net>
19 */
20function media_filesinuse($data,$id){
21    global $lang;
22    echo '<h1>'.$lang['reference'].' <code>'.hsc(noNS($id)).'</code></h1>';
23    echo '<p>'.hsc($lang['ref_inuse']).'</p>';
24
25    $hidden=0; //count of hits without read permission
26    foreach($data as $row){
27        if(auth_quickaclcheck($row) >= AUTH_READ && isVisiblePage($row)){
28            echo '<div class="search_result">';
29            echo '<span class="mediaref_ref">'.hsc($row).'</span>';
30            echo '</div>';
31        }else
32            $hidden++;
33    }
34    if ($hidden){
35        print '<div class="mediaref_hidden">'.$lang['ref_hidden'].'</div>';
36    }
37}
38
39/**
40 * Handles the saving of image meta data
41 *
42 * @author Andreas Gohr <andi@splitbrain.org>
43 * @author Kate Arzamastseva <pshns@ukr.net>
44 */
45function media_metasave($id,$auth,$data){
46    if($auth < AUTH_UPLOAD) return false;
47    if(!checkSecurityToken()) return false;
48    global $lang;
49    global $conf;
50    $src = mediaFN($id);
51
52    $meta = new JpegMeta($src);
53    $meta->_parseAll();
54
55    foreach($data as $key => $val){
56        $val=trim($val);
57        if(empty($val)){
58            $meta->deleteField($key);
59        }else{
60            $meta->setField($key,$val);
61        }
62    }
63
64    $old = @filemtime($src);
65    if(!@file_exists(mediaFN($id, $old)) && @file_exists($src)) {
66        // add old revision to the attic
67        media_saveOldRevision($id);
68    }
69
70    if($meta->save()){
71        if($conf['fperm']) chmod($src, $conf['fperm']);
72
73        $new = @filemtime($src);
74        // add a log entry to the media changelog
75        addMediaLogEntry($new, $id, DOKU_CHANGE_TYPE_EDIT, $lang['media_meta_edited']);
76
77        msg($lang['metasaveok'],1);
78        return $id;
79    }else{
80        msg($lang['metasaveerr'],-1);
81        return false;
82    }
83}
84
85/**
86 * check if a media is external source
87 *
88 * @author Gerrit Uitslag <klapinklapin@gmail.com>
89 * @param string $id the media ID or URL
90 * @return bool
91 */
92function media_isexternal($id){
93    if (preg_match('#^(https?|ftp)://#i', $id)) return true;
94    return false;
95}
96
97/**
98 * Check if a media item is public (eg, external URL or readable by @ALL)
99 *
100 * @author Andreas Gohr <andi@splitbrain.org>
101 * @param string $id  the media ID or URL
102 * @return bool
103 */
104function media_ispublic($id){
105    if(media_isexternal($id)) return true;
106    $id = cleanID($id);
107    if(auth_aclcheck(getNS($id).':*', '', array()) >= AUTH_READ) return true;
108    return false;
109}
110
111/**
112 * Display the form to edit image meta data
113 *
114 * @author Andreas Gohr <andi@splitbrain.org>
115 * @author Kate Arzamastseva <pshns@ukr.net>
116 */
117function media_metaform($id,$auth){
118    global $lang, $config_cascade;
119
120    if($auth < AUTH_UPLOAD) {
121        echo '<div class="nothing">'.$lang['media_perm_upload'].'</div>'.NL;
122        return false;
123    }
124
125    // load the field descriptions
126    static $fields = null;
127    if(is_null($fields)){
128        $config_files = getConfigFiles('mediameta');
129        foreach ($config_files as $config_file) {
130            if(@file_exists($config_file)) include($config_file);
131        }
132    }
133
134    $src = mediaFN($id);
135
136    // output
137    $form = new Doku_Form(array('action' => media_managerURL(array('tab_details' => 'view'), '&'),
138                                'class' => 'meta'));
139    $form->addHidden('img', $id);
140    $form->addHidden('mediado', 'save');
141    foreach($fields as $key => $field){
142        // get current value
143        if (empty($field[0])) continue;
144        $tags = array($field[0]);
145        if(is_array($field[3])) $tags = array_merge($tags,$field[3]);
146        $value = tpl_img_getTag($tags,'',$src);
147        $value = cleanText($value);
148
149        // prepare attributes
150        $p = array();
151        $p['class'] = 'edit';
152        $p['id']    = 'meta__'.$key;
153        $p['name']  = 'meta['.$field[0].']';
154        $p_attrs    = array('class' => 'edit');
155
156        $form->addElement('<div class="row">');
157        if($field[2] == 'text'){
158            $form->addElement(form_makeField('text', $p['name'], $value, ($lang[$field[1]]) ? $lang[$field[1]] : $field[1] . ':', $p['id'], $p['class'], $p_attrs));
159        }else{
160            $att = buildAttributes($p);
161            $form->addElement('<label for="meta__'.$key.'">'.$lang[$field[1]].'</label>');
162            $form->addElement("<textarea $att rows=\"6\" cols=\"50\">".formText($value).'</textarea>');
163        }
164        $form->addElement('</div>'.NL);
165    }
166    $form->addElement('<div class="buttons">');
167    $form->addElement(form_makeButton('submit', '', $lang['btn_save'], array('accesskey' => 's', 'name' => 'mediado[save]')));
168    $form->addElement('</div>'.NL);
169    $form->printForm();
170}
171
172/**
173 * Convenience function to check if a media file is still in use
174 *
175 * @author Michael Klier <chi@chimeric.de>
176 */
177function media_inuse($id) {
178    global $conf;
179    $mediareferences = array();
180    if($conf['refcheck']){
181        $mediareferences = ft_mediause($id,true);
182        if(!count($mediareferences)) {
183            return false;
184        } else {
185            return $mediareferences;
186        }
187    } else {
188        return false;
189    }
190}
191
192define('DOKU_MEDIA_DELETED', 1);
193define('DOKU_MEDIA_NOT_AUTH', 2);
194define('DOKU_MEDIA_INUSE', 4);
195define('DOKU_MEDIA_EMPTY_NS', 8);
196
197/**
198 * Handles media file deletions
199 *
200 * If configured, checks for media references before deletion
201 *
202 * @author Andreas Gohr <andi@splitbrain.org>
203 * @return int One of: 0,
204 *                     DOKU_MEDIA_DELETED,
205 *                     DOKU_MEDIA_DELETED | DOKU_MEDIA_EMPTY_NS,
206 *                     DOKU_MEDIA_NOT_AUTH,
207 *                     DOKU_MEDIA_INUSE
208 */
209function media_delete($id,$auth){
210    global $lang;
211    if($auth < AUTH_DELETE) return DOKU_MEDIA_NOT_AUTH;
212    if(media_inuse($id)) return DOKU_MEDIA_INUSE;
213
214    $file = mediaFN($id);
215
216    // trigger an event - MEDIA_DELETE_FILE
217    $data['id']   = $id;
218    $data['name'] = utf8_basename($file);
219    $data['path'] = $file;
220    $data['size'] = (@file_exists($file)) ? filesize($file) : 0;
221
222    $data['unl'] = false;
223    $data['del'] = false;
224    $evt = new Doku_Event('MEDIA_DELETE_FILE',$data);
225    if ($evt->advise_before()) {
226        $old = @filemtime($file);
227        if(!@file_exists(mediaFN($id, $old)) && @file_exists($file)) {
228            // add old revision to the attic
229            media_saveOldRevision($id);
230        }
231
232        $data['unl'] = @unlink($file);
233        if($data['unl']){
234            addMediaLogEntry(time(), $id, DOKU_CHANGE_TYPE_DELETE, $lang['deleted']);
235            $data['del'] = io_sweepNS($id,'mediadir');
236        }
237    }
238    $evt->advise_after();
239    unset($evt);
240
241    if($data['unl'] && $data['del']){
242        return DOKU_MEDIA_DELETED | DOKU_MEDIA_EMPTY_NS;
243    }
244
245    return $data['unl'] ? DOKU_MEDIA_DELETED : 0;
246}
247
248/**
249 * Handle file uploads via XMLHttpRequest
250 *
251 * @return mixed false on error, id of the new file on success
252 */
253function media_upload_xhr($ns,$auth){
254    if(!checkSecurityToken()) return false;
255    global $INPUT;
256
257    $id = $INPUT->get->str('qqfile');
258    list($ext,$mime,$dl) = mimetype($id);
259    $input = fopen("php://input", "r");
260    if (!($tmp = io_mktmpdir())) return false;
261    $path = $tmp.'/'.md5($id);
262    $target = fopen($path, "w");
263    $realSize = stream_copy_to_stream($input, $target);
264    fclose($target);
265    fclose($input);
266    if (isset($_SERVER["CONTENT_LENGTH"]) && ($realSize != (int)$_SERVER["CONTENT_LENGTH"])){
267        unlink($path);
268        return false;
269    }
270
271    $res = media_save(
272        array('name' => $path,
273            'mime' => $mime,
274            'ext'  => $ext),
275        $ns.':'.$id,
276        (($INPUT->get->str('ow') == 'checked') ? true : false),
277        $auth,
278        'copy'
279    );
280    unlink($path);
281    if ($tmp) dir_delete($tmp);
282    if (is_array($res)) {
283        msg($res[0], $res[1]);
284        return false;
285    }
286    return $res;
287}
288
289/**
290 * Handles media file uploads
291 *
292 * @author Andreas Gohr <andi@splitbrain.org>
293 * @author Michael Klier <chi@chimeric.de>
294 * @return mixed false on error, id of the new file on success
295 */
296function media_upload($ns,$auth,$file=false){
297    if(!checkSecurityToken()) return false;
298    global $lang;
299    global $INPUT;
300
301    // get file and id
302    $id   = $INPUT->post->str('mediaid');
303    if (!$file) $file = $_FILES['upload'];
304    if(empty($id)) $id = $file['name'];
305
306    // check for errors (messages are done in lib/exe/mediamanager.php)
307    if($file['error']) return false;
308
309    // check extensions
310    list($fext,$fmime,$dl) = mimetype($file['name']);
311    list($iext,$imime,$dl) = mimetype($id);
312    if($fext && !$iext){
313        // no extension specified in id - read original one
314        $id   .= '.'.$fext;
315        $imime = $fmime;
316    }elseif($fext && $fext != $iext){
317        // extension was changed, print warning
318        msg(sprintf($lang['mediaextchange'],$fext,$iext));
319    }
320
321    $res = media_save(array('name' => $file['tmp_name'],
322                            'mime' => $imime,
323                            'ext'  => $iext), $ns.':'.$id,
324                      $INPUT->post->bool('ow'), $auth, 'copy_uploaded_file');
325    if (is_array($res)) {
326        msg($res[0], $res[1]);
327        return false;
328    }
329    return $res;
330}
331
332/**
333 * An alternative to move_uploaded_file that copies
334 *
335 * Using copy, makes sure any setgid bits on the media directory are honored
336 *
337 * @see   move_uploaded_file()
338 * @param string $from
339 * @param string $to
340 * @return bool
341 */
342function copy_uploaded_file($from, $to){
343    if(!is_uploaded_file($from)) return false;
344    $ok = copy($from, $to);
345    @unlink($from);
346    return $ok;
347}
348
349/**
350 * This generates an action event and delegates to _media_upload_action().
351 * Action plugins are allowed to pre/postprocess the uploaded file.
352 * (The triggered event is preventable.)
353 *
354 * Event data:
355 * $data[0]     fn_tmp: the temporary file name (read from $_FILES)
356 * $data[1]     fn: the file name of the uploaded file
357 * $data[2]     id: the future directory id of the uploaded file
358 * $data[3]     imime: the mimetype of the uploaded file
359 * $data[4]     overwrite: if an existing file is going to be overwritten
360 *
361 * @triggers MEDIA_UPLOAD_FINISH
362 */
363function media_save($file, $id, $ow, $auth, $move) {
364    if($auth < AUTH_UPLOAD) {
365        return array("You don't have permissions to upload files.", -1);
366    }
367
368    if (!isset($file['mime']) || !isset($file['ext'])) {
369        list($ext, $mime) = mimetype($id);
370        if (!isset($file['mime'])) {
371            $file['mime'] = $mime;
372        }
373        if (!isset($file['ext'])) {
374            $file['ext'] = $ext;
375        }
376    }
377
378    global $lang, $conf;
379
380    // get filename
381    $id   = cleanID($id);
382    $fn   = mediaFN($id);
383
384    // get filetype regexp
385    $types = array_keys(getMimeTypes());
386    $types = array_map(create_function('$q','return preg_quote($q,"/");'),$types);
387    $regex = join('|',$types);
388
389    // because a temp file was created already
390    if(!preg_match('/\.('.$regex.')$/i',$fn)) {
391        return array($lang['uploadwrong'],-1);
392    }
393
394    //check for overwrite
395    $overwrite = @file_exists($fn);
396    $auth_ow = (($conf['mediarevisions']) ? AUTH_UPLOAD : AUTH_DELETE);
397    if($overwrite && (!$ow || $auth < $auth_ow)) {
398        return array($lang['uploadexist'], 0);
399    }
400    // check for valid content
401    $ok = media_contentcheck($file['name'], $file['mime']);
402    if($ok == -1){
403        return array(sprintf($lang['uploadbadcontent'],'.' . $file['ext']),-1);
404    }elseif($ok == -2){
405        return array($lang['uploadspam'],-1);
406    }elseif($ok == -3){
407        return array($lang['uploadxss'],-1);
408    }
409
410    // prepare event data
411    $data[0] = $file['name'];
412    $data[1] = $fn;
413    $data[2] = $id;
414    $data[3] = $file['mime'];
415    $data[4] = $overwrite;
416    $data[5] = $move;
417
418    // trigger event
419    return trigger_event('MEDIA_UPLOAD_FINISH', $data, '_media_upload_action', true);
420}
421
422/**
423 * Callback adapter for media_upload_finish()
424 * @author Michael Klier <chi@chimeric.de>
425 */
426function _media_upload_action($data) {
427    // fixme do further sanity tests of given data?
428    if(is_array($data) && count($data)===6) {
429        return media_upload_finish($data[0], $data[1], $data[2], $data[3], $data[4], $data[5]);
430    } else {
431        return false; //callback error
432    }
433}
434
435/**
436 * Saves an uploaded media file
437 *
438 * @author Andreas Gohr <andi@splitbrain.org>
439 * @author Michael Klier <chi@chimeric.de>
440 * @author Kate Arzamastseva <pshns@ukr.net>
441 */
442function media_upload_finish($fn_tmp, $fn, $id, $imime, $overwrite, $move = 'move_uploaded_file') {
443    global $conf;
444    global $lang;
445    global $REV;
446
447    $old = @filemtime($fn);
448    if(!@file_exists(mediaFN($id, $old)) && @file_exists($fn)) {
449        // add old revision to the attic if missing
450        media_saveOldRevision($id);
451    }
452
453    // prepare directory
454    io_createNamespace($id, 'media');
455
456    if($move($fn_tmp, $fn)) {
457        @clearstatcache(true,$fn);
458        $new = @filemtime($fn);
459        // Set the correct permission here.
460        // Always chmod media because they may be saved with different permissions than expected from the php umask.
461        // (Should normally chmod to $conf['fperm'] only if $conf['fperm'] is set.)
462        chmod($fn, $conf['fmode']);
463        msg($lang['uploadsucc'],1);
464        media_notify($id,$fn,$imime,$old);
465        // add a log entry to the media changelog
466        if ($REV){
467            addMediaLogEntry($new, $id, DOKU_CHANGE_TYPE_REVERT, sprintf($lang['restored'], dformat($REV)), $REV);
468        } elseif ($overwrite) {
469            addMediaLogEntry($new, $id, DOKU_CHANGE_TYPE_EDIT);
470        } else {
471            addMediaLogEntry($new, $id, DOKU_CHANGE_TYPE_CREATE, $lang['created']);
472        }
473        return $id;
474    }else{
475        return array($lang['uploadfail'],-1);
476    }
477}
478
479/**
480 * Moves the current version of media file to the media_attic
481 * directory
482 *
483 * @author Kate Arzamastseva <pshns@ukr.net>
484 * @param string $id
485 * @return int - revision date
486 */
487function media_saveOldRevision($id){
488    global $conf, $lang;
489
490    $oldf = mediaFN($id);
491    if(!@file_exists($oldf)) return '';
492    $date = filemtime($oldf);
493    if (!$conf['mediarevisions']) return $date;
494
495    if (!getRevisionInfo($id, $date, 8192, true)) {
496        // there was an external edit,
497        // there is no log entry for current version of file
498        if (!@file_exists(mediaMetaFN($id,'.changes'))) {
499            addMediaLogEntry($date, $id, DOKU_CHANGE_TYPE_CREATE, $lang['created']);
500        } else {
501            addMediaLogEntry($date, $id, DOKU_CHANGE_TYPE_EDIT);
502        }
503    }
504
505    $newf = mediaFN($id,$date);
506    io_makeFileDir($newf);
507    if(copy($oldf, $newf)) {
508        // Set the correct permission here.
509        // Always chmod media because they may be saved with different permissions than expected from the php umask.
510        // (Should normally chmod to $conf['fperm'] only if $conf['fperm'] is set.)
511        chmod($newf, $conf['fmode']);
512    }
513    return $date;
514}
515
516/**
517 * This function checks if the uploaded content is really what the
518 * mimetype says it is. We also do spam checking for text types here.
519 *
520 * We need to do this stuff because we can not rely on the browser
521 * to do this check correctly. Yes, IE is broken as usual.
522 *
523 * @author Andreas Gohr <andi@splitbrain.org>
524 * @link   http://www.splitbrain.org/blog/2007-02/12-internet_explorer_facilitates_cross_site_scripting
525 * @fixme  check all 26 magic IE filetypes here?
526 */
527function media_contentcheck($file,$mime){
528    global $conf;
529    if($conf['iexssprotect']){
530        $fh = @fopen($file, 'rb');
531        if($fh){
532            $bytes = fread($fh, 256);
533            fclose($fh);
534            if(preg_match('/<(script|a|img|html|body|iframe)[\s>]/i',$bytes)){
535                return -3;
536            }
537        }
538    }
539    if(substr($mime,0,6) == 'image/'){
540        $info = @getimagesize($file);
541        if($mime == 'image/gif' && $info[2] != 1){
542            return -1;
543        }elseif($mime == 'image/jpeg' && $info[2] != 2){
544            return -1;
545        }elseif($mime == 'image/png' && $info[2] != 3){
546            return -1;
547        }
548        # fixme maybe check other images types as well
549    }elseif(substr($mime,0,5) == 'text/'){
550        global $TEXT;
551        $TEXT = io_readFile($file);
552        if(checkwordblock()){
553            return -2;
554        }
555    }
556    return 0;
557}
558
559/**
560 * Send a notify mail on uploads
561 *
562 * @author Andreas Gohr <andi@splitbrain.org>
563 */
564function media_notify($id,$file,$mime,$old_rev=false){
565    global $conf;
566    if(empty($conf['notify'])) return; //notify enabled?
567
568    $subscription = new Subscription();
569    return $subscription->send_media_diff($conf['notify'], 'uploadmail', $id, $old_rev, '');
570}
571
572/**
573 * List all files in a given Media namespace
574 */
575function media_filelist($ns,$auth=null,$jump='',$fullscreenview=false,$sort=false){
576    global $conf;
577    global $lang;
578    $ns = cleanID($ns);
579
580    // check auth our self if not given (needed for ajax calls)
581    if(is_null($auth)) $auth = auth_quickaclcheck("$ns:*");
582
583    if (!$fullscreenview) echo '<h1 id="media__ns">:'.hsc($ns).'</h1>'.NL;
584
585    if($auth < AUTH_READ){
586        // FIXME: print permission warning here instead?
587        echo '<div class="nothing">'.$lang['nothingfound'].'</div>'.NL;
588    }else{
589        if (!$fullscreenview) {
590            media_uploadform($ns, $auth);
591            media_searchform($ns);
592        }
593
594        $dir = utf8_encodeFN(str_replace(':','/',$ns));
595        $data = array();
596        search($data,$conf['mediadir'],'search_media',
597                array('showmsg'=>true,'depth'=>1),$dir,1,$sort);
598
599        if(!count($data)){
600            echo '<div class="nothing">'.$lang['nothingfound'].'</div>'.NL;
601        }else {
602            if ($fullscreenview) {
603                echo '<ul class="' . _media_get_list_type() . '">';
604            }
605            foreach($data as $item){
606                if (!$fullscreenview) {
607                    media_printfile($item,$auth,$jump);
608                } else {
609                    media_printfile_thumbs($item,$auth,$jump);
610                }
611            }
612            if ($fullscreenview) echo '</ul>'.NL;
613        }
614    }
615}
616
617/**
618 * Prints tabs for files list actions
619 *
620 * @author Kate Arzamastseva <pshns@ukr.net>
621 * @author Adrian Lang <mail@adrianlang.de>
622 *
623 * @param string $selected_tab - opened tab
624 */
625
626function media_tabs_files($selected_tab = ''){
627    global $lang;
628    $tabs = array();
629    foreach(array('files'  => 'mediaselect',
630                  'upload' => 'media_uploadtab',
631                  'search' => 'media_searchtab') as $tab => $caption) {
632        $tabs[$tab] = array('href'    => media_managerURL(array('tab_files' => $tab), '&'),
633                            'caption' => $lang[$caption]);
634    }
635
636    html_tabs($tabs, $selected_tab);
637}
638
639/**
640 * Prints tabs for files details actions
641 *
642 * @author Kate Arzamastseva <pshns@ukr.net>
643 * @param string $selected_tab - opened tab
644 */
645function media_tabs_details($image, $selected_tab = ''){
646    global $lang, $conf;
647
648    $tabs = array();
649    $tabs['view'] = array('href'    => media_managerURL(array('tab_details' => 'view'), '&'),
650                          'caption' => $lang['media_viewtab']);
651
652    list($ext, $mime) = mimetype($image);
653    if ($mime == 'image/jpeg' && @file_exists(mediaFN($image))) {
654        $tabs['edit'] = array('href'    => media_managerURL(array('tab_details' => 'edit'), '&'),
655                              'caption' => $lang['media_edittab']);
656    }
657    if ($conf['mediarevisions']) {
658        $tabs['history'] = array('href'    => media_managerURL(array('tab_details' => 'history'), '&'),
659                                 'caption' => $lang['media_historytab']);
660    }
661
662    html_tabs($tabs, $selected_tab);
663}
664
665/**
666 * Prints options for the tab that displays a list of all files
667 *
668 * @author Kate Arzamastseva <pshns@ukr.net>
669 */
670function media_tab_files_options(){
671    global $lang;
672    global $NS;
673    global $INPUT;
674    global $ID;
675    $form = new Doku_Form(array('class' => 'options', 'method' => 'get',
676                                'action' => wl($ID)));
677    $media_manager_params = media_managerURL(array(), '', false, true);
678    foreach($media_manager_params as $pKey => $pVal){
679        $form->addHidden($pKey, $pVal);
680    }
681    $form->addHidden('sectok', null);
682    if ($INPUT->has('q')) {
683        $form->addHidden('q', $INPUT->str('q'));
684    }
685    $form->addElement('<ul>'.NL);
686    foreach(array('list' => array('listType', array('thumbs', 'rows')),
687                  'sort' => array('sortBy', array('name', 'date')))
688            as $group => $content) {
689        $checked = "_media_get_${group}_type";
690        $checked = $checked();
691
692        $form->addElement('<li class="' . $content[0] . '">');
693        foreach($content[1] as $option) {
694            $attrs = array();
695            if ($checked == $option) {
696                $attrs['checked'] = 'checked';
697            }
698            $form->addElement(form_makeRadioField($group, $option,
699                                       $lang['media_' . $group . '_' . $option],
700                                                  $content[0] . '__' . $option,
701                                                  $option, $attrs));
702        }
703        $form->addElement('</li>'.NL);
704    }
705    $form->addElement('<li>');
706    $form->addElement(form_makeButton('submit', '', $lang['btn_apply']));
707    $form->addElement('</li>'.NL);
708    $form->addElement('</ul>'.NL);
709    $form->printForm();
710}
711
712/**
713 * Returns type of sorting for the list of files in media manager
714 *
715 * @author Kate Arzamastseva <pshns@ukr.net>
716 * @return string - sort type
717 */
718function _media_get_sort_type() {
719    return _media_get_display_param('sort', array('default' => 'name', 'date'));
720}
721
722function _media_get_list_type() {
723    return _media_get_display_param('list', array('default' => 'thumbs', 'rows'));
724}
725
726function _media_get_display_param($param, $values) {
727    global $INPUT;
728    if (in_array($INPUT->str($param), $values)) {
729        // FIXME: Set cookie
730        return $INPUT->str($param);
731    } else {
732        $val = get_doku_pref($param, $values['default']);
733        if (!in_array($val, $values)) {
734            $val = $values['default'];
735        }
736        return $val;
737    }
738}
739
740/**
741 * Prints tab that displays a list of all files
742 *
743 * @author Kate Arzamastseva <pshns@ukr.net>
744 */
745function media_tab_files($ns,$auth=null,$jump='') {
746    global $lang;
747    if(is_null($auth)) $auth = auth_quickaclcheck("$ns:*");
748
749    if($auth < AUTH_READ){
750        echo '<div class="nothing">'.$lang['media_perm_read'].'</div>'.NL;
751    }else{
752        media_filelist($ns,$auth,$jump,true,_media_get_sort_type());
753    }
754}
755
756/**
757 * Prints tab that displays uploading form
758 *
759 * @author Kate Arzamastseva <pshns@ukr.net>
760 */
761function media_tab_upload($ns,$auth=null,$jump='') {
762    global $lang;
763    if(is_null($auth)) $auth = auth_quickaclcheck("$ns:*");
764
765    echo '<div class="upload">'.NL;
766    if ($auth >= AUTH_UPLOAD) {
767        echo '<p>' . $lang['mediaupload'] . '</p>';
768    }
769    media_uploadform($ns, $auth, true);
770    echo '</div>'.NL;
771}
772
773/**
774 * Prints tab that displays search form
775 *
776 * @author Kate Arzamastseva <pshns@ukr.net>
777 */
778function media_tab_search($ns,$auth=null) {
779    global $lang;
780    global $INPUT;
781
782    $do = $INPUT->str('mediado');
783    $query = $INPUT->str('q');
784    echo '<div class="search">'.NL;
785
786    media_searchform($ns, $query, true);
787    if ($do == 'searchlist' || $query) {
788        media_searchlist($query,$ns,$auth,true,_media_get_sort_type());
789    }
790    echo '</div>'.NL;
791}
792
793/**
794 * Prints tab that displays mediafile details
795 *
796 * @author Kate Arzamastseva <pshns@ukr.net>
797 */
798function media_tab_view($image, $ns, $auth=null, $rev=false) {
799    global $lang, $conf;
800    if(is_null($auth)) $auth = auth_quickaclcheck("$ns:*");
801
802    if ($image && $auth >= AUTH_READ) {
803        $meta = new JpegMeta(mediaFN($image, $rev));
804        media_preview($image, $auth, $rev, $meta);
805        media_preview_buttons($image, $auth, $rev);
806        media_details($image, $auth, $rev, $meta);
807
808    } else {
809        echo '<div class="nothing">'.$lang['media_perm_read'].'</div>'.NL;
810    }
811}
812
813/**
814 * Prints tab that displays form for editing mediafile metadata
815 *
816 * @author Kate Arzamastseva <pshns@ukr.net>
817 */
818function media_tab_edit($image, $ns, $auth=null) {
819    global $lang;
820    if(is_null($auth)) $auth = auth_quickaclcheck("$ns:*");
821
822    if ($image) {
823        list($ext, $mime) = mimetype($image);
824        if ($mime == 'image/jpeg') media_metaform($image,$auth);
825    }
826}
827
828/**
829 * Prints tab that displays mediafile revisions
830 *
831 * @author Kate Arzamastseva <pshns@ukr.net>
832 */
833function media_tab_history($image, $ns, $auth=null) {
834    global $lang;
835    global $INPUT;
836
837    if(is_null($auth)) $auth = auth_quickaclcheck("$ns:*");
838    $do = $INPUT->str('mediado');
839
840    if ($auth >= AUTH_READ && $image) {
841        if ($do == 'diff'){
842            media_diff($image, $ns, $auth);
843        } else {
844            $first = $INPUT->int('first');
845            html_revisions($first, $image);
846        }
847    } else {
848        echo '<div class="nothing">'.$lang['media_perm_read'].'</div>'.NL;
849    }
850}
851
852/**
853 * Prints mediafile details
854 *
855 * @author Kate Arzamastseva <pshns@ukr.net>
856 */
857function media_preview($image, $auth, $rev=false, $meta=false) {
858
859    $size = media_image_preview_size($image, $rev, $meta);
860
861    if ($size) {
862        global $lang;
863        echo '<div class="image">';
864
865        $more = array();
866        if ($rev) {
867            $more['rev'] = $rev;
868        } else {
869            $t = @filemtime(mediaFN($image));
870            $more['t'] = $t;
871        }
872
873        $more['w'] = $size[0];
874        $more['h'] = $size[1];
875        $src = ml($image, $more);
876
877        echo '<a href="'.$src.'" target="_blank" title="'.$lang['mediaview'].'">';
878        echo '<img src="'.$src.'" alt="" style="max-width: '.$size[0].'px;" />';
879        echo '</a>';
880
881        echo '</div>'.NL;
882    }
883}
884
885/**
886 * Prints mediafile action buttons
887 *
888 * @author Kate Arzamastseva <pshns@ukr.net>
889 */
890function media_preview_buttons($image, $auth, $rev=false) {
891    global $lang, $conf;
892
893    echo '<ul class="actions">'.NL;
894
895    if($auth >= AUTH_DELETE && !$rev && @file_exists(mediaFN($image))){
896
897        // delete button
898        $form = new Doku_Form(array('id' => 'mediamanager__btn_delete',
899            'action'=>media_managerURL(array('delete' => $image), '&')));
900        $form->addElement(form_makeButton('submit','',$lang['btn_delete']));
901        echo '<li>';
902        $form->printForm();
903        echo '</li>'.NL;
904    }
905
906    $auth_ow = (($conf['mediarevisions']) ? AUTH_UPLOAD : AUTH_DELETE);
907    if($auth >= $auth_ow && !$rev){
908
909        // upload new version button
910        $form = new Doku_Form(array('id' => 'mediamanager__btn_update',
911            'action'=>media_managerURL(array('image' => $image, 'mediado' => 'update'), '&')));
912        $form->addElement(form_makeButton('submit','',$lang['media_update']));
913        echo '<li>';
914        $form->printForm();
915        echo '</li>'.NL;
916    }
917
918    if($auth >= AUTH_UPLOAD && $rev && $conf['mediarevisions'] && @file_exists(mediaFN($image, $rev))){
919
920        // restore button
921        $form = new Doku_Form(array('id' => 'mediamanager__btn_restore',
922            'action'=>media_managerURL(array('image' => $image), '&')));
923        $form->addHidden('mediado','restore');
924        $form->addHidden('rev',$rev);
925        $form->addElement(form_makeButton('submit','',$lang['media_restore']));
926        echo '<li>';
927        $form->printForm();
928        echo '</li>'.NL;
929    }
930
931    echo '</ul>'.NL;
932}
933
934/**
935 * Returns image width and height for mediamanager preview panel
936 *
937 * @author Kate Arzamastseva <pshns@ukr.net>
938 * @param string $image
939 * @param int $rev
940 * @param JpegMeta $meta
941 * @return array
942 */
943function media_image_preview_size($image, $rev, $meta, $size = 500) {
944    if (!preg_match("/\.(jpe?g|gif|png)$/", $image) || !file_exists(mediaFN($image, $rev))) return false;
945
946    $info = getimagesize(mediaFN($image, $rev));
947    $w = (int) $info[0];
948    $h = (int) $info[1];
949
950    if($meta && ($w > $size || $h > $size)){
951        $ratio = $meta->getResizeRatio($size, $size);
952        $w = floor($w * $ratio);
953        $h = floor($h * $ratio);
954    }
955    return array($w, $h);
956}
957
958/**
959 * Returns the requested EXIF/IPTC tag from the image meta
960 *
961 * @author Kate Arzamastseva <pshns@ukr.net>
962 * @param array $tags
963 * @param JpegMeta $meta
964 * @param string $alt
965 * @return string
966 */
967function media_getTag($tags,$meta,$alt=''){
968    if($meta === false) return $alt;
969    $info = $meta->getField($tags);
970    if($info == false) return $alt;
971    return $info;
972}
973
974/**
975 * Returns mediafile tags
976 *
977 * @author Kate Arzamastseva <pshns@ukr.net>
978 * @param JpegMeta $meta
979 * @return array
980 */
981function media_file_tags($meta) {
982    global $config_cascade;
983
984    // load the field descriptions
985    static $fields = null;
986    if(is_null($fields)){
987        $config_files = getConfigFiles('mediameta');
988        foreach ($config_files as $config_file) {
989            if(@file_exists($config_file)) include($config_file);
990        }
991    }
992
993    $tags = array();
994
995    foreach($fields as $key => $tag){
996        $t = array();
997        if (!empty($tag[0])) $t = array($tag[0]);
998        if(is_array($tag[3])) $t = array_merge($t,$tag[3]);
999        $value = media_getTag($t, $meta);
1000        $tags[] = array('tag' => $tag, 'value' => $value);
1001    }
1002
1003    return $tags;
1004}
1005
1006/**
1007 * Prints mediafile tags
1008 *
1009 * @author Kate Arzamastseva <pshns@ukr.net>
1010 */
1011function media_details($image, $auth, $rev=false, $meta=false) {
1012    global $lang;
1013
1014    if (!$meta) $meta = new JpegMeta(mediaFN($image, $rev));
1015    $tags = media_file_tags($meta);
1016
1017    echo '<dl>'.NL;
1018    foreach($tags as $tag){
1019        if ($tag['value']) {
1020            $value = cleanText($tag['value']);
1021            echo '<dt>'.$lang[$tag['tag'][1]].':</dt><dd>';
1022            if ($tag['tag'][2] == 'date') echo dformat($value);
1023            else echo hsc($value);
1024            echo '</dd>'.NL;
1025        }
1026    }
1027    echo '</dl>'.NL;
1028}
1029
1030/**
1031 * Shows difference between two revisions of file
1032 *
1033 * @author Kate Arzamastseva <pshns@ukr.net>
1034 */
1035function media_diff($image, $ns, $auth, $fromajax = false) {
1036    global $lang;
1037    global $conf;
1038    global $INPUT;
1039
1040    if ($auth < AUTH_READ || !$image || !$conf['mediarevisions']) return '';
1041
1042    $rev1 = $INPUT->int('rev');
1043
1044    $rev2 = $INPUT->ref('rev2');
1045    if(is_array($rev2)){
1046        $rev1 = (int) $rev2[0];
1047        $rev2 = (int) $rev2[1];
1048
1049        if(!$rev1){
1050            $rev1 = $rev2;
1051            unset($rev2);
1052        }
1053    }else{
1054        $rev2 = $INPUT->int('rev2');
1055    }
1056
1057    if ($rev1 && !file_exists(mediaFN($image, $rev1))) $rev1 = false;
1058    if ($rev2 && !file_exists(mediaFN($image, $rev2))) $rev2 = false;
1059
1060    if($rev1 && $rev2){            // two specific revisions wanted
1061        // make sure order is correct (older on the left)
1062        if($rev1 < $rev2){
1063            $l_rev = $rev1;
1064            $r_rev = $rev2;
1065        }else{
1066            $l_rev = $rev2;
1067            $r_rev = $rev1;
1068        }
1069    }elseif($rev1){                // single revision given, compare to current
1070        $r_rev = '';
1071        $l_rev = $rev1;
1072    }else{                        // no revision was given, compare previous to current
1073        $r_rev = '';
1074        $revs = getRevisions($image, 0, 1, 8192, true);
1075        if (file_exists(mediaFN($image, $revs[0]))) {
1076            $l_rev = $revs[0];
1077        } else {
1078            $l_rev = '';
1079        }
1080    }
1081
1082    // prepare event data
1083    $data[0] = $image;
1084    $data[1] = $l_rev;
1085    $data[2] = $r_rev;
1086    $data[3] = $ns;
1087    $data[4] = $auth;
1088    $data[5] = $fromajax;
1089
1090    // trigger event
1091    return trigger_event('MEDIA_DIFF', $data, '_media_file_diff', true);
1092
1093}
1094
1095function _media_file_diff($data) {
1096    if(is_array($data) && count($data)===6) {
1097        return media_file_diff($data[0], $data[1], $data[2], $data[3], $data[4], $data[5]);
1098    } else {
1099        return false;
1100    }
1101}
1102
1103/**
1104 * Shows difference between two revisions of image
1105 *
1106 * @author Kate Arzamastseva <pshns@ukr.net>
1107 */
1108function media_file_diff($image, $l_rev, $r_rev, $ns, $auth, $fromajax){
1109    global $lang;
1110    global $config_cascade;
1111    global $INPUT;
1112
1113    $l_meta = new JpegMeta(mediaFN($image, $l_rev));
1114    $r_meta = new JpegMeta(mediaFN($image, $r_rev));
1115
1116    $is_img = preg_match("/\.(jpe?g|gif|png)$/", $image);
1117    if ($is_img) {
1118        $l_size = media_image_preview_size($image, $l_rev, $l_meta);
1119        $r_size = media_image_preview_size($image, $r_rev, $r_meta);
1120        $is_img = ($l_size && $r_size && ($l_size[0] >= 30 || $r_size[0] >= 30));
1121
1122        $difftype = $INPUT->str('difftype');
1123
1124        if (!$fromajax) {
1125            $form = new Doku_Form(array(
1126                'action' => media_managerURL(array(), '&'),
1127                'method' => 'get',
1128                'id' => 'mediamanager__form_diffview',
1129                'class' => 'diffView'
1130            ));
1131            $form->addHidden('sectok', null);
1132            $form->addElement('<input type="hidden" name="rev2[]" value="'.$l_rev.'" ></input>');
1133            $form->addElement('<input type="hidden" name="rev2[]" value="'.$r_rev.'" ></input>');
1134            $form->addHidden('mediado', 'diff');
1135            $form->printForm();
1136
1137            echo NL.'<div id="mediamanager__diff" >'.NL;
1138        }
1139
1140        if ($difftype == 'opacity' || $difftype == 'portions') {
1141            media_image_diff($image, $l_rev, $r_rev, $l_size, $r_size, $difftype);
1142            if (!$fromajax) echo '</div>';
1143            return '';
1144        }
1145    }
1146
1147    list($l_head, $r_head) = html_diff_head($l_rev, $r_rev, $image, true);
1148
1149    ?>
1150    <div class="table">
1151    <table>
1152      <tr>
1153        <th><?php echo $l_head; ?></th>
1154        <th><?php echo $r_head; ?></th>
1155      </tr>
1156    <?php
1157
1158    echo '<tr class="image">';
1159    echo '<td>';
1160    media_preview($image, $auth, $l_rev, $l_meta);
1161    echo '</td>';
1162
1163    echo '<td>';
1164    media_preview($image, $auth, $r_rev, $r_meta);
1165    echo '</td>';
1166    echo '</tr>'.NL;
1167
1168    echo '<tr class="actions">';
1169    echo '<td>';
1170    media_preview_buttons($image, $auth, $l_rev);
1171    echo '</td>';
1172
1173    echo '<td>';
1174    media_preview_buttons($image, $auth, $r_rev);
1175    echo '</td>';
1176    echo '</tr>'.NL;
1177
1178    $l_tags = media_file_tags($l_meta);
1179    $r_tags = media_file_tags($r_meta);
1180    // FIXME r_tags-only stuff
1181    foreach ($l_tags as $key => $l_tag) {
1182        if ($l_tag['value'] != $r_tags[$key]['value']) {
1183            $r_tags[$key]['highlighted'] = true;
1184            $l_tags[$key]['highlighted'] = true;
1185        } else if (!$l_tag['value'] || !$r_tags[$key]['value']) {
1186            unset($r_tags[$key]);
1187            unset($l_tags[$key]);
1188        }
1189    }
1190
1191    echo '<tr>';
1192    foreach(array($l_tags,$r_tags) as $tags){
1193        echo '<td>'.NL;
1194
1195        echo '<dl class="img_tags">';
1196        foreach($tags as $tag){
1197            $value = cleanText($tag['value']);
1198            if (!$value) $value = '-';
1199            echo '<dt>'.$lang[$tag['tag'][1]].':</dt>';
1200            echo '<dd>';
1201            if ($tag['highlighted']) {
1202                echo '<strong>';
1203            }
1204            if ($tag['tag'][2] == 'date') echo dformat($value);
1205            else echo hsc($value);
1206            if ($tag['highlighted']) {
1207                echo '</strong>';
1208            }
1209            echo '</dd>';
1210        }
1211        echo '</dl>'.NL;
1212
1213        echo '</td>';
1214    }
1215    echo '</tr>'.NL;
1216
1217    echo '</table>'.NL;
1218    echo '</div>'.NL;
1219
1220    if ($is_img && !$fromajax) echo '</div>';
1221}
1222
1223/**
1224 * Prints two images side by side
1225 * and slider
1226 *
1227 * @author Kate Arzamastseva <pshns@ukr.net>
1228 * @param string $image
1229 * @param int $l_rev
1230 * @param int $r_rev
1231 * @param array $l_size
1232 * @param array $r_size
1233 * @param string $type
1234 */
1235function media_image_diff($image, $l_rev, $r_rev, $l_size, $r_size, $type) {
1236    if ($l_size != $r_size) {
1237        if ($r_size[0] > $l_size[0]) {
1238            $l_size = $r_size;
1239        }
1240    }
1241
1242    $l_more = array('rev' => $l_rev, 'h' => $l_size[1], 'w' => $l_size[0]);
1243    $r_more = array('rev' => $r_rev, 'h' => $l_size[1], 'w' => $l_size[0]);
1244
1245    $l_src = ml($image, $l_more);
1246    $r_src = ml($image, $r_more);
1247
1248    // slider
1249    echo '<div class="slider" style="max-width: '.($l_size[0]-20).'px;" ></div>'.NL;
1250
1251    // two images in divs
1252    echo '<div class="imageDiff ' . $type . '">'.NL;
1253    echo '<div class="image1" style="max-width: '.$l_size[0].'px;">';
1254    echo '<img src="'.$l_src.'" alt="" />';
1255    echo '</div>'.NL;
1256    echo '<div class="image2" style="max-width: '.$l_size[0].'px;">';
1257    echo '<img src="'.$r_src.'" alt="" />';
1258    echo '</div>'.NL;
1259    echo '</div>'.NL;
1260}
1261
1262/**
1263 * Restores an old revision of a media file
1264 *
1265 * @param string $image
1266 * @param int $rev
1267 * @param int $auth
1268 * @return string - file's id
1269 * @author Kate Arzamastseva <pshns@ukr.net>
1270 */
1271function media_restore($image, $rev, $auth){
1272    global $conf;
1273    if ($auth < AUTH_UPLOAD || !$conf['mediarevisions']) return false;
1274    $removed = (!file_exists(mediaFN($image)) && file_exists(mediaMetaFN($image, '.changes')));
1275    if (!$image || (!file_exists(mediaFN($image)) && !$removed)) return false;
1276    if (!$rev || !file_exists(mediaFN($image, $rev))) return false;
1277    list($iext,$imime,$dl) = mimetype($image);
1278    $res = media_upload_finish(mediaFN($image, $rev),
1279        mediaFN($image),
1280        $image,
1281        $imime,
1282        true,
1283        'copy');
1284    if (is_array($res)) {
1285        msg($res[0], $res[1]);
1286        return false;
1287    }
1288    return $res;
1289}
1290
1291/**
1292 * List all files found by the search request
1293 *
1294 * @author Tobias Sarnowski <sarnowski@cosmocode.de>
1295 * @author Andreas Gohr <gohr@cosmocode.de>
1296 * @author Kate Arzamastseva <pshns@ukr.net>
1297 * @triggers MEDIA_SEARCH
1298 */
1299function media_searchlist($query,$ns,$auth=null,$fullscreen=false,$sort='natural'){
1300    global $conf;
1301    global $lang;
1302
1303    $ns = cleanID($ns);
1304
1305    if ($query) {
1306        $evdata = array(
1307                'ns'    => $ns,
1308                'data'  => array(),
1309                'query' => $query
1310                );
1311        $evt = new Doku_Event('MEDIA_SEARCH', $evdata);
1312        if ($evt->advise_before()) {
1313            $dir = utf8_encodeFN(str_replace(':','/',$evdata['ns']));
1314            $pattern = '/'.preg_quote($evdata['query'],'/').'/i';
1315            search($evdata['data'],
1316                    $conf['mediadir'],
1317                    'search_media',
1318                    array('showmsg'=>false,'pattern'=>$pattern),
1319                    $dir,
1320                    1,
1321                    $sort);
1322        }
1323        $evt->advise_after();
1324        unset($evt);
1325    }
1326
1327    if (!$fullscreen) {
1328        echo '<h1 id="media__ns">'.sprintf($lang['searchmedia_in'],hsc($ns).':*').'</h1>'.NL;
1329        media_searchform($ns,$query);
1330    }
1331
1332    if(!count($evdata['data'])){
1333        echo '<div class="nothing">'.$lang['nothingfound'].'</div>'.NL;
1334    }else {
1335        if ($fullscreen) {
1336            echo '<ul class="' . _media_get_list_type() . '">';
1337        }
1338        foreach($evdata['data'] as $item){
1339            if (!$fullscreen) media_printfile($item,$item['perm'],'',true);
1340            else media_printfile_thumbs($item,$item['perm'],false,true);
1341        }
1342        if ($fullscreen) echo '</ul>'.NL;
1343    }
1344}
1345
1346/**
1347 * Formats and prints one file in the list
1348 */
1349function media_printfile($item,$auth,$jump,$display_namespace=false){
1350    global $lang;
1351    global $conf;
1352
1353    // Prepare zebra coloring
1354    // I always wanted to use this variable name :-D
1355    static $twibble = 1;
1356    $twibble *= -1;
1357    $zebra = ($twibble == -1) ? 'odd' : 'even';
1358
1359    // Automatically jump to recent action
1360    if($jump == $item['id']) {
1361        $jump = ' id="scroll__here" ';
1362    }else{
1363        $jump = '';
1364    }
1365
1366    // Prepare fileicons
1367    list($ext,$mime,$dl) = mimetype($item['file'],false);
1368    $class = preg_replace('/[^_\-a-z0-9]+/i','_',$ext);
1369    $class = 'select mediafile mf_'.$class;
1370
1371    // Prepare filename
1372    $file = utf8_decodeFN($item['file']);
1373
1374    // Prepare info
1375    $info = '';
1376    if($item['isimg']){
1377        $info .= (int) $item['meta']->getField('File.Width');
1378        $info .= '&#215;';
1379        $info .= (int) $item['meta']->getField('File.Height');
1380        $info .= ' ';
1381    }
1382    $info .= '<i>'.dformat($item['mtime']).'</i>';
1383    $info .= ' ';
1384    $info .= filesize_h($item['size']);
1385
1386    // output
1387    echo '<div class="'.$zebra.'"'.$jump.' title="'.hsc($item['id']).'">'.NL;
1388    if (!$display_namespace) {
1389        echo '<a id="h_:'.$item['id'].'" class="'.$class.'">'.hsc($file).'</a> ';
1390    } else {
1391        echo '<a id="h_:'.$item['id'].'" class="'.$class.'">'.hsc($item['id']).'</a><br/>';
1392    }
1393    echo '<span class="info">('.$info.')</span>'.NL;
1394
1395    // view button
1396    $link = ml($item['id'],'',true);
1397    echo ' <a href="'.$link.'" target="_blank"><img src="'.DOKU_BASE.'lib/images/magnifier.png" '.
1398        'alt="'.$lang['mediaview'].'" title="'.$lang['mediaview'].'" class="btn" /></a>';
1399
1400    // mediamanager button
1401    $link = wl('',array('do'=>'media','image'=>$item['id'],'ns'=>getNS($item['id'])));
1402    echo ' <a href="'.$link.'" target="_blank"><img src="'.DOKU_BASE.'lib/images/mediamanager.png" '.
1403        'alt="'.$lang['btn_media'].'" title="'.$lang['btn_media'].'" class="btn" /></a>';
1404
1405    // delete button
1406    if($item['writable'] && $auth >= AUTH_DELETE){
1407        $link = DOKU_BASE.'lib/exe/mediamanager.php?delete='.rawurlencode($item['id']).
1408            '&amp;sectok='.getSecurityToken();
1409        echo ' <a href="'.$link.'" class="btn_media_delete" title="'.$item['id'].'">'.
1410            '<img src="'.DOKU_BASE.'lib/images/trash.png" alt="'.$lang['btn_delete'].'" '.
1411            'title="'.$lang['btn_delete'].'" class="btn" /></a>';
1412    }
1413
1414    echo '<div class="example" id="ex_'.str_replace(':','_',$item['id']).'">';
1415    echo $lang['mediausage'].' <code>{{:'.$item['id'].'}}</code>';
1416    echo '</div>';
1417    if($item['isimg']) media_printimgdetail($item);
1418    echo '<div class="clearer"></div>'.NL;
1419    echo '</div>'.NL;
1420}
1421
1422function media_printicon($filename){
1423    list($ext,$mime,$dl) = mimetype(mediaFN($filename),false);
1424
1425    if (@file_exists(DOKU_INC.'lib/images/fileicons/'.$ext.'.png')) {
1426        $icon = DOKU_BASE.'lib/images/fileicons/'.$ext.'.png';
1427    } else {
1428        $icon = DOKU_BASE.'lib/images/fileicons/file.png';
1429    }
1430
1431    return '<img src="'.$icon.'" alt="'.$filename.'" class="icon" />';
1432
1433}
1434
1435/**
1436 * Formats and prints one file in the list in the thumbnails view
1437 *
1438 * @author Kate Arzamastseva <pshns@ukr.net>
1439 */
1440function media_printfile_thumbs($item,$auth,$jump=false,$display_namespace=false){
1441    global $lang;
1442    global $conf;
1443
1444    // Prepare filename
1445    $file = utf8_decodeFN($item['file']);
1446
1447    // output
1448    echo '<li><dl title="'.hsc($item['id']).'">'.NL;
1449
1450        echo '<dt>';
1451    if($item['isimg']) {
1452        media_printimgdetail($item, true);
1453
1454    } else {
1455        echo '<a id="d_:'.$item['id'].'" class="image" title="'.$item['id'].'" href="'.
1456            media_managerURL(array('image' => hsc($item['id']), 'ns' => getNS($item['id']),
1457            'tab_details' => 'view')).'">';
1458        echo media_printicon($item['id']);
1459        echo '</a>';
1460    }
1461    echo '</dt>'.NL;
1462    if (!$display_namespace) {
1463        $name = hsc($file);
1464    } else {
1465        $name = hsc($item['id']);
1466    }
1467    echo '<dd class="name"><a href="'.media_managerURL(array('image' => hsc($item['id']), 'ns' => getNS($item['id']),
1468        'tab_details' => 'view')).'" id="h_:'.$item['id'].'">'.$name.'</a></dd>'.NL;
1469
1470    if($item['isimg']){
1471        $size = '';
1472        $size .= (int) $item['meta']->getField('File.Width');
1473        $size .= '&#215;';
1474        $size .= (int) $item['meta']->getField('File.Height');
1475        echo '<dd class="size">'.$size.'</dd>'.NL;
1476    } else {
1477        echo '<dd class="size">&#160;</dd>'.NL;
1478    }
1479    $date = dformat($item['mtime']);
1480    echo '<dd class="date">'.$date.'</dd>'.NL;
1481    $filesize = filesize_h($item['size']);
1482    echo '<dd class="filesize">'.$filesize.'</dd>'.NL;
1483    echo '</dl></li>'.NL;
1484}
1485
1486/**
1487 * Prints a thumbnail and metainfos
1488 */
1489function media_printimgdetail($item, $fullscreen=false){
1490    // prepare thumbnail
1491    $size = $fullscreen ? 90 : 120;
1492
1493    $w = (int) $item['meta']->getField('File.Width');
1494    $h = (int) $item['meta']->getField('File.Height');
1495    if($w>$size || $h>$size){
1496        if (!$fullscreen) {
1497            $ratio = $item['meta']->getResizeRatio($size);
1498        } else {
1499            $ratio = $item['meta']->getResizeRatio($size,$size);
1500        }
1501        $w = floor($w * $ratio);
1502        $h = floor($h * $ratio);
1503    }
1504    $src = ml($item['id'],array('w'=>$w,'h'=>$h,'t'=>$item['mtime']));
1505    $p = array();
1506    if (!$fullscreen) {
1507        // In fullscreen mediamanager view, image resizing is done via CSS.
1508        $p['width']  = $w;
1509        $p['height'] = $h;
1510    }
1511    $p['alt']    = $item['id'];
1512    $att = buildAttributes($p);
1513
1514    // output
1515    if ($fullscreen) {
1516        echo '<a id="l_:'.$item['id'].'" class="image thumb" href="'.
1517            media_managerURL(array('image' => hsc($item['id']), 'ns' => getNS($item['id']), 'tab_details' => 'view')).'">';
1518        echo '<img src="'.$src.'" '.$att.' />';
1519        echo '</a>';
1520    }
1521
1522    if ($fullscreen) return;
1523
1524    echo '<div class="detail">';
1525    echo '<div class="thumb">';
1526    echo '<a id="d_:'.$item['id'].'" class="select">';
1527    echo '<img src="'.$src.'" '.$att.' />';
1528    echo '</a>';
1529    echo '</div>';
1530
1531    // read EXIF/IPTC data
1532    $t = $item['meta']->getField(array('IPTC.Headline','xmp.dc:title'));
1533    $d = $item['meta']->getField(array('IPTC.Caption','EXIF.UserComment',
1534                'EXIF.TIFFImageDescription',
1535                'EXIF.TIFFUserComment'));
1536    if(utf8_strlen($d) > 250) $d = utf8_substr($d,0,250).'...';
1537    $k = $item['meta']->getField(array('IPTC.Keywords','IPTC.Category','xmp.dc:subject'));
1538
1539    // print EXIF/IPTC data
1540    if($t || $d || $k ){
1541        echo '<p>';
1542        if($t) echo '<strong>'.htmlspecialchars($t).'</strong><br />';
1543        if($d) echo htmlspecialchars($d).'<br />';
1544        if($t) echo '<em>'.htmlspecialchars($k).'</em>';
1545        echo '</p>';
1546    }
1547    echo '</div>';
1548}
1549
1550/**
1551 * Build link based on the current, adding/rewriting
1552 * parameters
1553 *
1554 * @author Kate Arzamastseva <pshns@ukr.net>
1555 * @param array $params
1556 * @param string $amp - separator
1557 * @return string - link
1558 */
1559function media_managerURL($params=false, $amp='&amp;', $abs=false, $params_array=false) {
1560    global $conf;
1561    global $ID;
1562    global $INPUT;
1563
1564    $gets = array('do' => 'media');
1565    $media_manager_params = array('tab_files', 'tab_details', 'image', 'ns', 'list', 'sort');
1566    foreach ($media_manager_params as $x) {
1567        if ($INPUT->has($x)) $gets[$x] = $INPUT->str($x);
1568    }
1569
1570    if ($params) {
1571        $gets = $params + $gets;
1572    }
1573    unset($gets['id']);
1574    if (isset($gets['delete'])) {
1575        unset($gets['image']);
1576        unset($gets['tab_details']);
1577    }
1578
1579    if ($params_array) return $gets;
1580
1581    return wl($ID,$gets,$abs,$amp);
1582}
1583
1584/**
1585 * Print the media upload form if permissions are correct
1586 *
1587 * @author Andreas Gohr <andi@splitbrain.org>
1588 * @author Kate Arzamastseva <pshns@ukr.net>
1589 */
1590function media_uploadform($ns, $auth, $fullscreen = false){
1591    global $lang;
1592    global $conf;
1593    global $INPUT;
1594
1595    if($auth < AUTH_UPLOAD) {
1596        echo '<div class="nothing">'.$lang['media_perm_upload'].'</div>'.NL;
1597        return;
1598    }
1599    $auth_ow = (($conf['mediarevisions']) ? AUTH_UPLOAD : AUTH_DELETE);
1600
1601    $update = false;
1602    $id = '';
1603    if ($auth >= $auth_ow && $fullscreen && $INPUT->str('mediado') == 'update') {
1604        $update = true;
1605        $id = cleanID($INPUT->str('image'));
1606    }
1607
1608    // The default HTML upload form
1609    $params = array('id'      => 'dw__upload',
1610                    'enctype' => 'multipart/form-data');
1611    if (!$fullscreen) {
1612        $params['action'] = DOKU_BASE.'lib/exe/mediamanager.php';
1613    } else {
1614        $params['action'] = media_managerURL(array('tab_files' => 'files',
1615            'tab_details' => 'view'), '&');
1616    }
1617
1618    $form = new Doku_Form($params);
1619    if (!$fullscreen) echo '<div class="upload">' . $lang['mediaupload'] . '</div>';
1620    $form->addElement(formSecurityToken());
1621    $form->addHidden('ns', hsc($ns));
1622    $form->addElement(form_makeOpenTag('p'));
1623    $form->addElement(form_makeFileField('upload', $lang['txt_upload'].':', 'upload__file'));
1624    $form->addElement(form_makeCloseTag('p'));
1625    $form->addElement(form_makeOpenTag('p'));
1626    $form->addElement(form_makeTextField('mediaid', noNS($id), $lang['txt_filename'].':', 'upload__name'));
1627    $form->addElement(form_makeButton('submit', '', $lang['btn_upload']));
1628    $form->addElement(form_makeCloseTag('p'));
1629
1630    if($auth >= $auth_ow){
1631        $form->addElement(form_makeOpenTag('p'));
1632        $attrs = array();
1633        if ($update) $attrs['checked'] = 'checked';
1634        $form->addElement(form_makeCheckboxField('ow', 1, $lang['txt_overwrt'], 'dw__ow', 'check', $attrs));
1635        $form->addElement(form_makeCloseTag('p'));
1636    }
1637
1638    echo NL.'<div id="mediamanager__uploader">'.NL;
1639    html_form('upload', $form);
1640
1641    echo '</div>'.NL;
1642
1643    echo '<p class="maxsize">';
1644    printf($lang['maxuploadsize'],filesize_h(media_getuploadsize()));
1645    echo '</p>'.NL;
1646
1647}
1648
1649/**
1650 * Returns the size uploaded files may have
1651 *
1652 * This uses a conservative approach using the lowest number found
1653 * in any of the limiting ini settings
1654 *
1655 * @returns int size in bytes
1656 */
1657function media_getuploadsize(){
1658    $okay = 0;
1659
1660    $post = (int) php_to_byte(@ini_get('post_max_size'));
1661    $suho = (int) php_to_byte(@ini_get('suhosin.post.max_value_length'));
1662    $upld = (int) php_to_byte(@ini_get('upload_max_filesize'));
1663
1664    if($post && ($post < $okay || $okay == 0)) $okay = $post;
1665    if($suho && ($suho < $okay || $okay == 0)) $okay = $suho;
1666    if($upld && ($upld < $okay || $okay == 0)) $okay = $upld;
1667
1668    return $okay;
1669}
1670
1671/**
1672 * Print the search field form
1673 *
1674 * @author Tobias Sarnowski <sarnowski@cosmocode.de>
1675 * @author Kate Arzamastseva <pshns@ukr.net>
1676 */
1677function media_searchform($ns,$query='',$fullscreen=false){
1678    global $lang;
1679
1680    // The default HTML search form
1681    $params = array('id' => 'dw__mediasearch');
1682    if (!$fullscreen) {
1683        $params['action'] = DOKU_BASE.'lib/exe/mediamanager.php';
1684    } else {
1685        $params['action'] = media_managerURL(array(), '&');
1686    }
1687    $form = new Doku_Form($params);
1688    $form->addHidden('ns', $ns);
1689    $form->addHidden($fullscreen ? 'mediado' : 'do', 'searchlist');
1690
1691    if (!$fullscreen) $form->addElement('<div class="upload">' . $lang['mediasearch'] . '</div>'.NL);
1692    $form->addElement(form_makeOpenTag('p'));
1693    $form->addElement(form_makeTextField('q', $query,$lang['searchmedia'],'','',array('title'=>sprintf($lang['searchmedia_in'],hsc($ns).':*'))));
1694    $form->addElement(form_makeButton('submit', '', $lang['btn_search']));
1695    $form->addElement(form_makeCloseTag('p'));
1696    html_form('searchmedia', $form);
1697}
1698
1699/**
1700 * Build a tree outline of available media namespaces
1701 *
1702 * @author Andreas Gohr <andi@splitbrain.org>
1703 */
1704function media_nstree($ns){
1705    global $conf;
1706    global $lang;
1707
1708    // currently selected namespace
1709    $ns  = cleanID($ns);
1710    if(empty($ns)){
1711        global $ID;
1712        $ns = (string)getNS($ID);
1713    }
1714
1715    $ns_dir  = utf8_encodeFN(str_replace(':','/',$ns));
1716
1717    $data = array();
1718    search($data,$conf['mediadir'],'search_index',array('ns' => $ns_dir, 'nofiles' => true));
1719
1720    // wrap a list with the root level around the other namespaces
1721    array_unshift($data, array('level' => 0, 'id' => '', 'open' =>'true',
1722                               'label' => '['.$lang['mediaroot'].']'));
1723
1724    // insert the current ns into the hierarchy if it isn't already part of it
1725    $ns_parts = explode(':', $ns);
1726    $tmp_ns = '';
1727    $pos = 0;
1728    foreach ($ns_parts as $level => $part) {
1729        if ($tmp_ns) $tmp_ns .= ':'.$part;
1730        else $tmp_ns = $part;
1731
1732        // find the namespace parts or insert them
1733        while ($data[$pos]['id'] != $tmp_ns) {
1734            if ($pos >= count($data) || ($data[$pos]['level'] <= $level+1 && strnatcmp(utf8_encodeFN($data[$pos]['id']), utf8_encodeFN($tmp_ns)) > 0)) {
1735                array_splice($data, $pos, 0, array(array('level' => $level+1, 'id' => $tmp_ns, 'open' => 'true')));
1736                break;
1737            }
1738            ++$pos;
1739        }
1740    }
1741
1742    echo html_buildlist($data,'idx','media_nstree_item','media_nstree_li');
1743}
1744
1745/**
1746 * Userfunction for html_buildlist
1747 *
1748 * Prints a media namespace tree item
1749 *
1750 * @author Andreas Gohr <andi@splitbrain.org>
1751 */
1752function media_nstree_item($item){
1753    global $INPUT;
1754    $pos   = strrpos($item['id'], ':');
1755    $label = substr($item['id'], $pos > 0 ? $pos + 1 : 0);
1756    if(!$item['label']) $item['label'] = $label;
1757
1758    $ret  = '';
1759    if (!($INPUT->str('do') == 'media'))
1760    $ret .= '<a href="'.DOKU_BASE.'lib/exe/mediamanager.php?ns='.idfilter($item['id']).'" class="idx_dir">';
1761    else $ret .= '<a href="'.media_managerURL(array('ns' => idfilter($item['id'], false), 'tab_files' => 'files'))
1762        .'" class="idx_dir">';
1763    $ret .= $item['label'];
1764    $ret .= '</a>';
1765    return $ret;
1766}
1767
1768/**
1769 * Userfunction for html_buildlist
1770 *
1771 * Prints a media namespace tree item opener
1772 *
1773 * @author Andreas Gohr <andi@splitbrain.org>
1774 */
1775function media_nstree_li($item){
1776    $class='media level'.$item['level'];
1777    if($item['open']){
1778        $class .= ' open';
1779        $img   = DOKU_BASE.'lib/images/minus.gif';
1780        $alt   = '−';
1781    }else{
1782        $class .= ' closed';
1783        $img   = DOKU_BASE.'lib/images/plus.gif';
1784        $alt   = '+';
1785    }
1786    // TODO: only deliver an image if it actually has a subtree...
1787    return '<li class="'.$class.'">'.
1788        '<img src="'.$img.'" alt="'.$alt.'" />';
1789}
1790
1791/**
1792 * Resizes the given image to the given size
1793 *
1794 * @author  Andreas Gohr <andi@splitbrain.org>
1795 */
1796function media_resize_image($file, $ext, $w, $h=0){
1797    global $conf;
1798
1799    $info = @getimagesize($file); //get original size
1800    if($info == false) return $file; // that's no image - it's a spaceship!
1801
1802    if(!$h) $h = round(($w * $info[1]) / $info[0]);
1803
1804    // we wont scale up to infinity
1805    if($w > 2000 || $h > 2000) return $file;
1806
1807    // resize necessary? - (w,h) = native dimensions
1808    if(($w == $info[0]) && ($h == $info[1])) return $file;
1809
1810    //cache
1811    $local = getCacheName($file,'.media.'.$w.'x'.$h.'.'.$ext);
1812    $mtime = @filemtime($local); // 0 if not exists
1813
1814    if( $mtime > filemtime($file) ||
1815            media_resize_imageIM($ext,$file,$info[0],$info[1],$local,$w,$h) ||
1816            media_resize_imageGD($ext,$file,$info[0],$info[1],$local,$w,$h) ){
1817        if($conf['fperm']) chmod($local, $conf['fperm']);
1818        return $local;
1819    }
1820    //still here? resizing failed
1821    return $file;
1822}
1823
1824/**
1825 * Crops the given image to the wanted ratio, then calls media_resize_image to scale it
1826 * to the wanted size
1827 *
1828 * Crops are centered horizontally but prefer the upper third of an vertical
1829 * image because most pics are more interesting in that area (rule of thirds)
1830 *
1831 * @author  Andreas Gohr <andi@splitbrain.org>
1832 */
1833function media_crop_image($file, $ext, $w, $h=0){
1834    global $conf;
1835
1836    if(!$h) $h = $w;
1837    $info = @getimagesize($file); //get original size
1838    if($info == false) return $file; // that's no image - it's a spaceship!
1839
1840    // calculate crop size
1841    $fr = $info[0]/$info[1];
1842    $tr = $w/$h;
1843
1844    // check if the crop can be handled completely by resize,
1845    // i.e. the specified width & height match the aspect ratio of the source image
1846    if ($w == round($h*$fr)) {
1847        return media_resize_image($file, $ext, $w);
1848    }
1849
1850    if($tr >= 1){
1851        if($tr > $fr){
1852            $cw = $info[0];
1853            $ch = (int) ($info[0]/$tr);
1854        }else{
1855            $cw = (int) ($info[1]*$tr);
1856            $ch = $info[1];
1857        }
1858    }else{
1859        if($tr < $fr){
1860            $cw = (int) ($info[1]*$tr);
1861            $ch = $info[1];
1862        }else{
1863            $cw = $info[0];
1864            $ch = (int) ($info[0]/$tr);
1865        }
1866    }
1867    // calculate crop offset
1868    $cx = (int) (($info[0]-$cw)/2);
1869    $cy = (int) (($info[1]-$ch)/3);
1870
1871    //cache
1872    $local = getCacheName($file,'.media.'.$cw.'x'.$ch.'.crop.'.$ext);
1873    $mtime = @filemtime($local); // 0 if not exists
1874
1875    if( $mtime > @filemtime($file) ||
1876            media_crop_imageIM($ext,$file,$info[0],$info[1],$local,$cw,$ch,$cx,$cy) ||
1877            media_resize_imageGD($ext,$file,$cw,$ch,$local,$cw,$ch,$cx,$cy) ){
1878        if($conf['fperm']) chmod($local, $conf['fperm']);
1879        return media_resize_image($local,$ext, $w, $h);
1880    }
1881
1882    //still here? cropping failed
1883    return media_resize_image($file,$ext, $w, $h);
1884}
1885
1886/**
1887 * Calculate a token to be used to verify fetch requests for resized or
1888 * cropped images have been internally generated - and prevent external
1889 * DDOS attacks via fetch
1890 *
1891 * @author Christopher Smith <chris@jalakai.co.uk>
1892 *
1893 * @param string  $id    id of the image
1894 * @param int     $w     resize/crop width
1895 * @param int     $h     resize/crop height
1896 * @return string
1897 */
1898function media_get_token($id,$w,$h){
1899    // token is only required for modified images
1900    if ($w || $h || media_isexternal($id)) {
1901        $token = $id;
1902        if ($w) $token .= '.'.$w;
1903        if ($h) $token .= '.'.$h;
1904
1905        return substr(PassHash::hmac('md5', $token, auth_cookiesalt()),0,6);
1906    }
1907
1908    return '';
1909}
1910
1911/**
1912 * Download a remote file and return local filename
1913 *
1914 * returns false if download fails. Uses cached file if available and
1915 * wanted
1916 *
1917 * @author  Andreas Gohr <andi@splitbrain.org>
1918 * @author  Pavel Vitis <Pavel.Vitis@seznam.cz>
1919 */
1920function media_get_from_URL($url,$ext,$cache){
1921    global $conf;
1922
1923    // if no cache or fetchsize just redirect
1924    if ($cache==0)           return false;
1925    if (!$conf['fetchsize']) return false;
1926
1927    $local = getCacheName(strtolower($url),".media.$ext");
1928    $mtime = @filemtime($local); // 0 if not exists
1929
1930    //decide if download needed:
1931    if( ($mtime == 0) ||                           // cache does not exist
1932            ($cache != -1 && $mtime < time()-$cache)   // 'recache' and cache has expired
1933      ){
1934        if(media_image_download($url,$local)){
1935            return $local;
1936        }else{
1937            return false;
1938        }
1939    }
1940
1941    //if cache exists use it else
1942    if($mtime) return $local;
1943
1944    //else return false
1945    return false;
1946}
1947
1948/**
1949 * Download image files
1950 *
1951 * @author Andreas Gohr <andi@splitbrain.org>
1952 */
1953function media_image_download($url,$file){
1954    global $conf;
1955    $http = new DokuHTTPClient();
1956    $http->keep_alive = false; // we do single ops here, no need for keep-alive
1957
1958    $http->max_bodysize = $conf['fetchsize'];
1959    $http->timeout = 25; //max. 25 sec
1960    $http->header_regexp = '!\r\nContent-Type: image/(jpe?g|gif|png)!i';
1961
1962    $data = $http->get($url);
1963    if(!$data) return false;
1964
1965    $fileexists = @file_exists($file);
1966    $fp = @fopen($file,"w");
1967    if(!$fp) return false;
1968    fwrite($fp,$data);
1969    fclose($fp);
1970    if(!$fileexists and $conf['fperm']) chmod($file, $conf['fperm']);
1971
1972    // check if it is really an image
1973    $info = @getimagesize($file);
1974    if(!$info){
1975        @unlink($file);
1976        return false;
1977    }
1978
1979    return true;
1980}
1981
1982/**
1983 * resize images using external ImageMagick convert program
1984 *
1985 * @author Pavel Vitis <Pavel.Vitis@seznam.cz>
1986 * @author Andreas Gohr <andi@splitbrain.org>
1987 */
1988function media_resize_imageIM($ext,$from,$from_w,$from_h,$to,$to_w,$to_h){
1989    global $conf;
1990
1991    // check if convert is configured
1992    if(!$conf['im_convert']) return false;
1993
1994    // prepare command
1995    $cmd  = $conf['im_convert'];
1996    $cmd .= ' -resize '.$to_w.'x'.$to_h.'!';
1997    if ($ext == 'jpg' || $ext == 'jpeg') {
1998        $cmd .= ' -quality '.$conf['jpg_quality'];
1999    }
2000    $cmd .= " $from $to";
2001
2002    @exec($cmd,$out,$retval);
2003    if ($retval == 0) return true;
2004    return false;
2005}
2006
2007/**
2008 * crop images using external ImageMagick convert program
2009 *
2010 * @author Andreas Gohr <andi@splitbrain.org>
2011 */
2012function media_crop_imageIM($ext,$from,$from_w,$from_h,$to,$to_w,$to_h,$ofs_x,$ofs_y){
2013    global $conf;
2014
2015    // check if convert is configured
2016    if(!$conf['im_convert']) return false;
2017
2018    // prepare command
2019    $cmd  = $conf['im_convert'];
2020    $cmd .= ' -crop '.$to_w.'x'.$to_h.'+'.$ofs_x.'+'.$ofs_y;
2021    if ($ext == 'jpg' || $ext == 'jpeg') {
2022        $cmd .= ' -quality '.$conf['jpg_quality'];
2023    }
2024    $cmd .= " $from $to";
2025
2026    @exec($cmd,$out,$retval);
2027    if ($retval == 0) return true;
2028    return false;
2029}
2030
2031/**
2032 * resize or crop images using PHP's libGD support
2033 *
2034 * @author Andreas Gohr <andi@splitbrain.org>
2035 * @author Sebastian Wienecke <s_wienecke@web.de>
2036 */
2037function media_resize_imageGD($ext,$from,$from_w,$from_h,$to,$to_w,$to_h,$ofs_x=0,$ofs_y=0){
2038    global $conf;
2039
2040    if($conf['gdlib'] < 1) return false; //no GDlib available or wanted
2041
2042    // check available memory
2043    if(!is_mem_available(($from_w * $from_h * 4) + ($to_w * $to_h * 4))){
2044        return false;
2045    }
2046
2047    // create an image of the given filetype
2048    if ($ext == 'jpg' || $ext == 'jpeg'){
2049        if(!function_exists("imagecreatefromjpeg")) return false;
2050        $image = @imagecreatefromjpeg($from);
2051    }elseif($ext == 'png') {
2052        if(!function_exists("imagecreatefrompng")) return false;
2053        $image = @imagecreatefrompng($from);
2054
2055    }elseif($ext == 'gif') {
2056        if(!function_exists("imagecreatefromgif")) return false;
2057        $image = @imagecreatefromgif($from);
2058    }
2059    if(!$image) return false;
2060
2061    if(($conf['gdlib']>1) && function_exists("imagecreatetruecolor") && $ext != 'gif'){
2062        $newimg = @imagecreatetruecolor ($to_w, $to_h);
2063    }
2064    if(!$newimg) $newimg = @imagecreate($to_w, $to_h);
2065    if(!$newimg){
2066        imagedestroy($image);
2067        return false;
2068    }
2069
2070    //keep png alpha channel if possible
2071    if($ext == 'png' && $conf['gdlib']>1 && function_exists('imagesavealpha')){
2072        imagealphablending($newimg, false);
2073        imagesavealpha($newimg,true);
2074    }
2075
2076    //keep gif transparent color if possible
2077    if($ext == 'gif' && function_exists('imagefill') && function_exists('imagecolorallocate')) {
2078        if(function_exists('imagecolorsforindex') && function_exists('imagecolortransparent')) {
2079            $transcolorindex = @imagecolortransparent($image);
2080            if($transcolorindex >= 0 ) { //transparent color exists
2081                $transcolor = @imagecolorsforindex($image, $transcolorindex);
2082                $transcolorindex = @imagecolorallocate($newimg, $transcolor['red'], $transcolor['green'], $transcolor['blue']);
2083                @imagefill($newimg, 0, 0, $transcolorindex);
2084                @imagecolortransparent($newimg, $transcolorindex);
2085            }else{ //filling with white
2086                $whitecolorindex = @imagecolorallocate($newimg, 255, 255, 255);
2087                @imagefill($newimg, 0, 0, $whitecolorindex);
2088            }
2089        }else{ //filling with white
2090            $whitecolorindex = @imagecolorallocate($newimg, 255, 255, 255);
2091            @imagefill($newimg, 0, 0, $whitecolorindex);
2092        }
2093    }
2094
2095    //try resampling first
2096    if(function_exists("imagecopyresampled")){
2097        if(!@imagecopyresampled($newimg, $image, 0, 0, $ofs_x, $ofs_y, $to_w, $to_h, $from_w, $from_h)) {
2098            imagecopyresized($newimg, $image, 0, 0, $ofs_x, $ofs_y, $to_w, $to_h, $from_w, $from_h);
2099        }
2100    }else{
2101        imagecopyresized($newimg, $image, 0, 0, $ofs_x, $ofs_y, $to_w, $to_h, $from_w, $from_h);
2102    }
2103
2104    $okay = false;
2105    if ($ext == 'jpg' || $ext == 'jpeg'){
2106        if(!function_exists('imagejpeg')){
2107            $okay = false;
2108        }else{
2109            $okay = imagejpeg($newimg, $to, $conf['jpg_quality']);
2110        }
2111    }elseif($ext == 'png') {
2112        if(!function_exists('imagepng')){
2113            $okay = false;
2114        }else{
2115            $okay =  imagepng($newimg, $to);
2116        }
2117    }elseif($ext == 'gif') {
2118        if(!function_exists('imagegif')){
2119            $okay = false;
2120        }else{
2121            $okay = imagegif($newimg, $to);
2122        }
2123    }
2124
2125    // destroy GD image ressources
2126    if($image) imagedestroy($image);
2127    if($newimg) imagedestroy($newimg);
2128
2129    return $okay;
2130}
2131
2132/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
2133