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