Failed Conditions
Push — defines ( 46028c )
by Andreas
02:53
created

inc/media.php (3 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

Code
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 <[email protected]>
7
 */
8
9
use dokuwiki\ChangeLog\MediaChangeLog;
10
use dokuwiki\HTTP\DokuHTTPClient;
11
use dokuwiki\Subscriptions\MediaSubscriptionSender;
12
use dokuwiki\Extension\Event;
13
14
/**
15
 * Lists pages which currently use a media file selected for deletion
16
 *
17
 * References uses the same visual as search results and share
18
 * their CSS tags except pagenames won't be links.
19
 *
20
 * @author Matthias Grimm <[email protected]>
21
 *
22
 * @param array $data
23
 * @param string $id
24
 */
25
function media_filesinuse($data,$id){
26
    global $lang;
27
    echo '<h1>'.$lang['reference'].' <code>'.hsc(noNS($id)).'</code></h1>';
28
    echo '<p>'.hsc($lang['ref_inuse']).'</p>';
29
30
    $hidden=0; //count of hits without read permission
31
    foreach($data as $row){
32
        if(auth_quickaclcheck($row) >= AUTH_READ && isVisiblePage($row)){
33
            echo '<div class="search_result">';
34
            echo '<span class="mediaref_ref">'.hsc($row).'</span>';
35
            echo '</div>';
36
        }else
37
            $hidden++;
38
    }
39
    if ($hidden){
40
        print '<div class="mediaref_hidden">'.$lang['ref_hidden'].'</div>';
41
    }
42
}
43
44
/**
45
 * Handles the saving of image meta data
46
 *
47
 * @author Andreas Gohr <[email protected]>
48
 * @author Kate Arzamastseva <[email protected]>
49
 *
50
 * @param string $id media id
51
 * @param int $auth permission level
52
 * @param array $data
53
 * @return false|string
54
 */
55
function media_metasave($id,$auth,$data){
56
    if($auth < AUTH_UPLOAD) return false;
57
    if(!checkSecurityToken()) return false;
58
    global $lang;
59
    global $conf;
60
    $src = mediaFN($id);
61
62
    $meta = new JpegMeta($src);
63
    $meta->_parseAll();
64
65
    foreach($data as $key => $val){
66
        $val=trim($val);
67
        if(empty($val)){
68
            $meta->deleteField($key);
69
        }else{
70
            $meta->setField($key,$val);
71
        }
72
    }
73
74
    $old = @filemtime($src);
75
    if(!file_exists(mediaFN($id, $old)) && file_exists($src)) {
76
        // add old revision to the attic
77
        media_saveOldRevision($id);
78
    }
79
    $filesize_old = filesize($src);
80
    if($meta->save()){
81
        if($conf['fperm']) chmod($src, $conf['fperm']);
82
        @clearstatcache(true, $src);
83
        $new = @filemtime($src);
84
        $filesize_new = filesize($src);
85
        $sizechange = $filesize_new - $filesize_old;
86
87
        // add a log entry to the media changelog
88
        addMediaLogEntry($new, $id, DOKU_CHANGE_TYPE_EDIT, $lang['media_meta_edited'], '', null, $sizechange);
89
90
        msg($lang['metasaveok'],1);
91
        return $id;
92
    }else{
93
        msg($lang['metasaveerr'],-1);
94
        return false;
95
    }
96
}
97
98
/**
99
 * check if a media is external source
100
 *
101
 * @author Gerrit Uitslag <[email protected]>
102
 *
103
 * @param string $id the media ID or URL
104
 * @return bool
105
 */
106
function media_isexternal($id){
107
    if (preg_match('#^(?:https?|ftp)://#i', $id)) return true;
108
    return false;
109
}
110
111
/**
112
 * Check if a media item is public (eg, external URL or readable by @ALL)
113
 *
114
 * @author Andreas Gohr <[email protected]>
115
 *
116
 * @param string $id  the media ID or URL
117
 * @return bool
118
 */
119
function media_ispublic($id){
120
    if(media_isexternal($id)) return true;
121
    $id = cleanID($id);
122
    if(auth_aclcheck(getNS($id).':*', '', array()) >= AUTH_READ) return true;
123
    return false;
124
}
125
126
/**
127
 * Display the form to edit image meta data
128
 *
129
 * @author Andreas Gohr <[email protected]>
130
 * @author Kate Arzamastseva <[email protected]>
131
 *
132
 * @param string $id media id
133
 * @param int $auth permission level
134
 * @return bool
135
 */
136
function media_metaform($id,$auth){
137
    global $lang;
138
139
    if($auth < AUTH_UPLOAD) {
140
        echo '<div class="nothing">'.$lang['media_perm_upload'].'</div>'.NL;
141
        return false;
142
    }
143
144
    // load the field descriptions
145
    static $fields = null;
146
    if(is_null($fields)){
147
        $config_files = getConfigFiles('mediameta');
148
        foreach ($config_files as $config_file) {
149
            if(file_exists($config_file)) include($config_file);
150
        }
151
    }
152
153
    $src = mediaFN($id);
154
155
    // output
156
    $form = new Doku_Form(array('action' => media_managerURL(array('tab_details' => 'view'), '&'),
157
                                'class' => 'meta'));
158
    $form->addHidden('img', $id);
159
    $form->addHidden('mediado', 'save');
160
    foreach($fields as $key => $field){
161
        // get current value
162
        if (empty($field[0])) continue;
163
        $tags = array($field[0]);
164
        if(is_array($field[3])) $tags = array_merge($tags,$field[3]);
165
        $value = tpl_img_getTag($tags,'',$src);
166
        $value = cleanText($value);
167
168
        // prepare attributes
169
        $p = array();
170
        $p['class'] = 'edit';
171
        $p['id']    = 'meta__'.$key;
172
        $p['name']  = 'meta['.$field[0].']';
173
        $p_attrs    = array('class' => 'edit');
174
175
        $form->addElement('<div class="row">');
176
        if($field[2] == 'text'){
177
            $form->addElement(
178
                form_makeField(
179
                    'text',
180
                    $p['name'],
181
                    $value,
182
                    ($lang[$field[1]]) ? $lang[$field[1]] : $field[1] . ':',
183
                    $p['id'],
184
                    $p['class'],
185
                    $p_attrs
186
                )
187
            );
188
        }else{
189
            $att = buildAttributes($p);
190
            $form->addElement('<label for="meta__'.$key.'">'.$lang[$field[1]].'</label>');
191
            $form->addElement("<textarea $att rows=\"6\" cols=\"50\">".formText($value).'</textarea>');
192
        }
193
        $form->addElement('</div>'.NL);
194
    }
195
    $form->addElement('<div class="buttons">');
196
    $form->addElement(
197
        form_makeButton(
198
            'submit',
199
            '',
200
            $lang['btn_save'],
201
            array('accesskey' => 's', 'name' => 'mediado[save]')
202
        )
203
    );
204
    $form->addElement('</div>'.NL);
205
    $form->printForm();
206
207
    return true;
208
}
209
210
/**
211
 * Convenience function to check if a media file is still in use
212
 *
213
 * @author Michael Klier <[email protected]>
214
 *
215
 * @param string $id media id
216
 * @return array|bool
217
 */
218
function media_inuse($id) {
219
    global $conf;
220
221
    if($conf['refcheck']){
222
        $mediareferences = ft_mediause($id,true);
223
        if(!count($mediareferences)) {
224
            return false;
225
        } else {
226
            return $mediareferences;
227
        }
228
    } else {
229
        return false;
230
    }
231
}
232
233
/**
234
 * Handles media file deletions
235
 *
236
 * If configured, checks for media references before deletion
237
 *
238
 * @author             Andreas Gohr <[email protected]>
239
 *
240
 * @param string $id media id
241
 * @param int $auth no longer used
242
 * @return int One of: 0,
243
 *                     DOKU_MEDIA_DELETED,
244
 *                     DOKU_MEDIA_DELETED | DOKU_MEDIA_EMPTY_NS,
245
 *                     DOKU_MEDIA_NOT_AUTH,
246
 *                     DOKU_MEDIA_INUSE
247
 */
248
function media_delete($id,$auth){
249
    global $lang;
250
    $auth = auth_quickaclcheck(ltrim(getNS($id).':*', ':'));
251
    if($auth < AUTH_DELETE) return DOKU_MEDIA_NOT_AUTH;
252
    if(media_inuse($id)) return DOKU_MEDIA_INUSE;
253
254
    $file = mediaFN($id);
255
256
    // trigger an event - MEDIA_DELETE_FILE
257
    $data = array();
258
    $data['id']   = $id;
259
    $data['name'] = \dokuwiki\Utf8\PhpString::basename($file);
260
    $data['path'] = $file;
261
    $data['size'] = (file_exists($file)) ? filesize($file) : 0;
262
263
    $data['unl'] = false;
264
    $data['del'] = false;
265
    $evt = new Event('MEDIA_DELETE_FILE',$data);
266
    if ($evt->advise_before()) {
267
        $old = @filemtime($file);
268
        if(!file_exists(mediaFN($id, $old)) && file_exists($file)) {
269
            // add old revision to the attic
270
            media_saveOldRevision($id);
271
        }
272
273
        $data['unl'] = @unlink($file);
274
        if($data['unl']) {
275
            $sizechange = 0 - $data['size'];
276
            addMediaLogEntry(time(), $id, DOKU_CHANGE_TYPE_DELETE, $lang['deleted'], '', null, $sizechange);
277
278
            $data['del'] = io_sweepNS($id, 'mediadir');
279
        }
280
    }
281
    $evt->advise_after();
282
    unset($evt);
283
284
    if($data['unl'] && $data['del']){
285
        return DOKU_MEDIA_DELETED | DOKU_MEDIA_EMPTY_NS;
286
    }
287
288
    return $data['unl'] ? DOKU_MEDIA_DELETED : 0;
289
}
290
291
/**
292
 * Handle file uploads via XMLHttpRequest
293
 *
294
 * @param string $ns   target namespace
295
 * @param int    $auth current auth check result
296
 * @return false|string false on error, id of the new file on success
297
 */
298
function media_upload_xhr($ns,$auth){
299
    if(!checkSecurityToken()) return false;
300
    global $INPUT;
301
302
    $id = $INPUT->get->str('qqfile');
303
    list($ext,$mime) = mimetype($id);
304
    $input = fopen("php://input", "r");
305
    if (!($tmp = io_mktmpdir())) return false;
306
    $path = $tmp.'/'.md5($id);
307
    $target = fopen($path, "w");
308
    $realSize = stream_copy_to_stream($input, $target);
309
    fclose($target);
310
    fclose($input);
311
    if (isset($_SERVER["CONTENT_LENGTH"]) && ($realSize != (int)$_SERVER["CONTENT_LENGTH"])){
312
        unlink($path);
313
        return false;
314
    }
315
316
    $res = media_save(
317
        array('name' => $path,
318
            'mime' => $mime,
319
            'ext'  => $ext),
320
        $ns.':'.$id,
321
        (($INPUT->get->str('ow') == 'true') ? true : false),
322
        $auth,
323
        'copy'
324
    );
325
    unlink($path);
326
    if ($tmp) io_rmdir($tmp, true);
327
    if (is_array($res)) {
328
        msg($res[0], $res[1]);
329
        return false;
330
    }
331
    return $res;
332
}
333
334
/**
335
 * Handles media file uploads
336
 *
337
 * @author Andreas Gohr <[email protected]>
338
 * @author Michael Klier <[email protected]>
339
 *
340
 * @param string     $ns    target namespace
341
 * @param int        $auth  current auth check result
342
 * @param bool|array $file  $_FILES member, $_FILES['upload'] if false
343
 * @return false|string false on error, id of the new file on success
344
 */
345
function media_upload($ns,$auth,$file=false){
346
    if(!checkSecurityToken()) return false;
347
    global $lang;
348
    global $INPUT;
349
350
    // get file and id
351
    $id   = $INPUT->post->str('mediaid');
352
    if (!$file) $file = $_FILES['upload'];
353
    if(empty($id)) $id = $file['name'];
354
355
    // check for errors (messages are done in lib/exe/mediamanager.php)
356
    if($file['error']) return false;
357
358
    // check extensions
359
    list($fext,$fmime) = mimetype($file['name']);
360
    list($iext,$imime) = mimetype($id);
361
    if($fext && !$iext){
362
        // no extension specified in id - read original one
363
        $id   .= '.'.$fext;
364
        $imime = $fmime;
365
    }elseif($fext && $fext != $iext){
366
        // extension was changed, print warning
367
        msg(sprintf($lang['mediaextchange'],$fext,$iext));
368
    }
369
370
    $res = media_save(array('name' => $file['tmp_name'],
371
                            'mime' => $imime,
372
                            'ext'  => $iext), $ns.':'.$id,
373
                      $INPUT->post->bool('ow'), $auth, 'copy_uploaded_file');
374
    if (is_array($res)) {
375
        msg($res[0], $res[1]);
376
        return false;
377
    }
378
    return $res;
379
}
380
381
/**
382
 * An alternative to move_uploaded_file that copies
383
 *
384
 * Using copy, makes sure any setgid bits on the media directory are honored
385
 *
386
 * @see   move_uploaded_file()
387
 *
388
 * @param string $from
389
 * @param string $to
390
 * @return bool
391
 */
392
function copy_uploaded_file($from, $to){
393
    if(!is_uploaded_file($from)) return false;
394
    $ok = copy($from, $to);
395
    @unlink($from);
396
    return $ok;
397
}
398
399
/**
400
 * This generates an action event and delegates to _media_upload_action().
401
 * Action plugins are allowed to pre/postprocess the uploaded file.
402
 * (The triggered event is preventable.)
403
 *
404
 * Event data:
405
 * $data[0]     fn_tmp:    the temporary file name (read from $_FILES)
406
 * $data[1]     fn:        the file name of the uploaded file
407
 * $data[2]     id:        the future directory id of the uploaded file
408
 * $data[3]     imime:     the mimetype of the uploaded file
409
 * $data[4]     overwrite: if an existing file is going to be overwritten
410
 * $data[5]     move:      name of function that performs move/copy/..
411
 *
412
 * @triggers MEDIA_UPLOAD_FINISH
413
 *
414
 * @param array  $file
415
 * @param string $id   media id
416
 * @param bool   $ow   overwrite?
417
 * @param int    $auth permission level
418
 * @param string $move name of functions that performs move/copy/..
419
 * @return false|array|string
420
 */
421
function media_save($file, $id, $ow, $auth, $move) {
422
    if($auth < AUTH_UPLOAD) {
423
        return array("You don't have permissions to upload files.", -1);
424
    }
425
426
    if (!isset($file['mime']) || !isset($file['ext'])) {
427
        list($ext, $mime) = mimetype($id);
428
        if (!isset($file['mime'])) {
429
            $file['mime'] = $mime;
430
        }
431
        if (!isset($file['ext'])) {
432
            $file['ext'] = $ext;
433
        }
434
    }
435
436
    global $lang, $conf;
437
438
    // get filename
439
    $id   = cleanID($id);
440
    $fn   = mediaFN($id);
441
442
    // get filetype regexp
443
    $types = array_keys(getMimeTypes());
444
    $types = array_map(
445
        function ($q) {
446
            return preg_quote($q, "/");
447
        },
448
        $types
449
    );
450
    $regex = join('|',$types);
451
452
    // because a temp file was created already
453
    if(!preg_match('/\.('.$regex.')$/i',$fn)) {
454
        return array($lang['uploadwrong'],-1);
455
    }
456
457
    //check for overwrite
458
    $overwrite = file_exists($fn);
459
    $auth_ow = (($conf['mediarevisions']) ? AUTH_UPLOAD : AUTH_DELETE);
460
    if($overwrite && (!$ow || $auth < $auth_ow)) {
461
        return array($lang['uploadexist'], 0);
462
    }
463
    // check for valid content
464
    $ok = media_contentcheck($file['name'], $file['mime']);
465
    if($ok == -1){
466
        return array(sprintf($lang['uploadbadcontent'],'.' . $file['ext']),-1);
467
    }elseif($ok == -2){
468
        return array($lang['uploadspam'],-1);
469
    }elseif($ok == -3){
470
        return array($lang['uploadxss'],-1);
471
    }
472
473
    // prepare event data
474
    $data = array();
475
    $data[0] = $file['name'];
476
    $data[1] = $fn;
477
    $data[2] = $id;
478
    $data[3] = $file['mime'];
479
    $data[4] = $overwrite;
480
    $data[5] = $move;
481
482
    // trigger event
483
    return Event::createAndTrigger('MEDIA_UPLOAD_FINISH', $data, '_media_upload_action', true);
484
}
485
486
/**
487
 * Callback adapter for media_upload_finish() triggered by MEDIA_UPLOAD_FINISH
488
 *
489
 * @author Michael Klier <[email protected]>
490
 *
491
 * @param array $data event data
492
 * @return false|array|string
493
 */
494
function _media_upload_action($data) {
495
    // fixme do further sanity tests of given data?
496
    if(is_array($data) && count($data)===6) {
497
        return media_upload_finish($data[0], $data[1], $data[2], $data[3], $data[4], $data[5]);
498
    } else {
499
        return false; //callback error
500
    }
501
}
502
503
/**
504
 * Saves an uploaded media file
505
 *
506
 * @author Andreas Gohr <[email protected]>
507
 * @author Michael Klier <[email protected]>
508
 * @author Kate Arzamastseva <[email protected]>
509
 *
510
 * @param string $fn_tmp
511
 * @param string $fn
512
 * @param string $id        media id
513
 * @param string $imime     mime type
514
 * @param bool   $overwrite overwrite existing?
515
 * @param string $move      function name
516
 * @return array|string
517
 */
518
function media_upload_finish($fn_tmp, $fn, $id, $imime, $overwrite, $move = 'move_uploaded_file') {
519
    global $conf;
520
    global $lang;
521
    global $REV;
522
523
    $old = @filemtime($fn);
524
    if(!file_exists(mediaFN($id, $old)) && file_exists($fn)) {
525
        // add old revision to the attic if missing
526
        media_saveOldRevision($id);
527
    }
528
529
    // prepare directory
530
    io_createNamespace($id, 'media');
531
532
    $filesize_old = file_exists($fn) ? filesize($fn) : 0;
533
534
    if($move($fn_tmp, $fn)) {
535
        @clearstatcache(true,$fn);
536
        $new = @filemtime($fn);
537
        // Set the correct permission here.
538
        // Always chmod media because they may be saved with different permissions than expected from the php umask.
539
        // (Should normally chmod to $conf['fperm'] only if $conf['fperm'] is set.)
540
        chmod($fn, $conf['fmode']);
541
        msg($lang['uploadsucc'],1);
542
        media_notify($id,$fn,$imime,$old,$new);
543
        // add a log entry to the media changelog
544
        $filesize_new = filesize($fn);
545
        $sizechange = $filesize_new - $filesize_old;
546
        if($REV) {
547
            addMediaLogEntry(
548
                $new,
549
                $id,
550
                DOKU_CHANGE_TYPE_REVERT,
551
                sprintf($lang['restored'], dformat($REV)),
552
                $REV,
553
                null,
554
                $sizechange
555
            );
556
        } elseif($overwrite) {
557
            addMediaLogEntry($new, $id, DOKU_CHANGE_TYPE_EDIT, '', '', null, $sizechange);
558
        } else {
559
            addMediaLogEntry($new, $id, DOKU_CHANGE_TYPE_CREATE, $lang['created'], '', null, $sizechange);
560
        }
561
        return $id;
562
    }else{
563
        return array($lang['uploadfail'],-1);
564
    }
565
}
566
567
/**
568
 * Moves the current version of media file to the media_attic
569
 * directory
570
 *
571
 * @author Kate Arzamastseva <[email protected]>
572
 *
573
 * @param string $id
574
 * @return int - revision date
575
 */
576
function media_saveOldRevision($id){
577
    global $conf, $lang;
578
579
    $oldf = mediaFN($id);
580
    if(!file_exists($oldf)) return '';
581
    $date = filemtime($oldf);
582
    if (!$conf['mediarevisions']) return $date;
583
584
    $medialog = new MediaChangeLog($id);
585
    if (!$medialog->getRevisionInfo($date)) {
586
        // there was an external edit,
587
        // there is no log entry for current version of file
588
        $sizechange = filesize($oldf);
589
        if(!file_exists(mediaMetaFN($id, '.changes'))) {
590
            addMediaLogEntry($date, $id, DOKU_CHANGE_TYPE_CREATE, $lang['created'], '', null, $sizechange);
591
        } else {
592
            $oldRev = $medialog->getRevisions(-1, 1); // from changelog
593
            $oldRev = (int) (empty($oldRev) ? 0 : $oldRev[0]);
594
            $filesize_old = filesize(mediaFN($id, $oldRev));
595
            $sizechange = $sizechange - $filesize_old;
596
597
            addMediaLogEntry($date, $id, DOKU_CHANGE_TYPE_EDIT, '', '', null, $sizechange);
598
        }
599
    }
600
601
    $newf = mediaFN($id,$date);
602
    io_makeFileDir($newf);
603
    if(copy($oldf, $newf)) {
604
        // Set the correct permission here.
605
        // Always chmod media because they may be saved with different permissions than expected from the php umask.
606
        // (Should normally chmod to $conf['fperm'] only if $conf['fperm'] is set.)
607
        chmod($newf, $conf['fmode']);
608
    }
609
    return $date;
610
}
611
612
/**
613
 * This function checks if the uploaded content is really what the
614
 * mimetype says it is. We also do spam checking for text types here.
615
 *
616
 * We need to do this stuff because we can not rely on the browser
617
 * to do this check correctly. Yes, IE is broken as usual.
618
 *
619
 * @author Andreas Gohr <[email protected]>
620
 * @link   http://www.splitbrain.org/blog/2007-02/12-internet_explorer_facilitates_cross_site_scripting
621
 * @fixme  check all 26 magic IE filetypes here?
622
 *
623
 * @param string $file path to file
624
 * @param string $mime mimetype
625
 * @return int
626
 */
627
function media_contentcheck($file,$mime){
628
    global $conf;
629
    if($conf['iexssprotect']){
630
        $fh = @fopen($file, 'rb');
631
        if($fh){
632
            $bytes = fread($fh, 256);
633
            fclose($fh);
634
            if(preg_match('/<(script|a|img|html|body|iframe)[\s>]/i',$bytes)){
635
                return -3; //XSS: possibly malicious content
636
            }
637
        }
638
    }
639
    if(substr($mime,0,6) == 'image/'){
640
        $info = @getimagesize($file);
641
        if($mime == 'image/gif' && $info[2] != 1){
642
            return -1; // uploaded content did not match the file extension
643
        }elseif($mime == 'image/jpeg' && $info[2] != 2){
644
            return -1;
645
        }elseif($mime == 'image/png' && $info[2] != 3){
646
            return -1;
647
        }
648
        # fixme maybe check other images types as well
649
    }elseif(substr($mime,0,5) == 'text/'){
650
        global $TEXT;
651
        $TEXT = io_readFile($file);
652
        if(checkwordblock()){
653
            return -2; //blocked by the spam blacklist
654
        }
655
    }
656
    return 0;
657
}
658
659
/**
660
 * Send a notify mail on uploads
661
 *
662
 * @author Andreas Gohr <[email protected]>
663
 *
664
 * @param string   $id      media id
665
 * @param string   $file    path to file
666
 * @param string   $mime    mime type
667
 * @param bool|int $old_rev revision timestamp or false
668
 * @return bool
669
 */
670
function media_notify($id,$file,$mime,$old_rev=false,$current_rev=false){
671
    global $conf;
672
    if(empty($conf['notify'])) return false; //notify enabled?
673
674
    $subscription = new MediaSubscriptionSender();
675
    return $subscription->sendMediaDiff($conf['notify'], 'uploadmail', $id, $old_rev, $current_rev);
676
}
677
678
/**
679
 * List all files in a given Media namespace
680
 *
681
 * @param string      $ns             namespace
682
 * @param null|int    $auth           permission level
683
 * @param string      $jump           id
684
 * @param bool        $fullscreenview
685
 * @param bool|string $sort           sorting order, false skips sorting
686
 */
687
function media_filelist($ns,$auth=null,$jump='',$fullscreenview=false,$sort=false){
688
    global $conf;
689
    global $lang;
690
    $ns = cleanID($ns);
691
692
    // check auth our self if not given (needed for ajax calls)
693
    if(is_null($auth)) $auth = auth_quickaclcheck("$ns:*");
694
695
    if (!$fullscreenview) echo '<h1 id="media__ns">:'.hsc($ns).'</h1>'.NL;
696
697
    if($auth < AUTH_READ){
698
        // FIXME: print permission warning here instead?
699
        echo '<div class="nothing">'.$lang['nothingfound'].'</div>'.NL;
700
    }else{
701
        if (!$fullscreenview) {
702
            media_uploadform($ns, $auth);
703
            media_searchform($ns);
704
        }
705
706
        $dir = utf8_encodeFN(str_replace(':','/',$ns));
707
        $data = array();
708
        search($data,$conf['mediadir'],'search_media',
709
                array('showmsg'=>true,'depth'=>1),$dir,1,$sort);
710
711
        if(!count($data)){
712
            echo '<div class="nothing">'.$lang['nothingfound'].'</div>'.NL;
713
        }else {
714
            if ($fullscreenview) {
715
                echo '<ul class="' . _media_get_list_type() . '">';
716
            }
717
            foreach($data as $item){
718
                if (!$fullscreenview) {
719
                    media_printfile($item,$auth,$jump);
720
                } else {
721
                    media_printfile_thumbs($item,$auth,$jump);
722
                }
723
            }
724
            if ($fullscreenview) echo '</ul>'.NL;
725
        }
726
    }
727
}
728
729
/**
730
 * Prints tabs for files list actions
731
 *
732
 * @author Kate Arzamastseva <[email protected]>
733
 * @author Adrian Lang <[email protected]>
734
 *
735
 * @param string $selected_tab - opened tab
736
 */
737
738
function media_tabs_files($selected_tab = ''){
739
    global $lang;
740
    $tabs = array();
741
    foreach(array('files'  => 'mediaselect',
742
                  'upload' => 'media_uploadtab',
743
                  'search' => 'media_searchtab') as $tab => $caption) {
744
        $tabs[$tab] = array('href'    => media_managerURL(array('tab_files' => $tab), '&'),
745
                            'caption' => $lang[$caption]);
746
    }
747
748
    html_tabs($tabs, $selected_tab);
749
}
750
751
/**
752
 * Prints tabs for files details actions
753
 *
754
 * @author Kate Arzamastseva <[email protected]>
755
 * @param string $image filename of the current image
756
 * @param string $selected_tab opened tab
757
 */
758
function media_tabs_details($image, $selected_tab = ''){
759
    global $lang, $conf;
760
761
    $tabs = array();
762
    $tabs['view'] = array('href'    => media_managerURL(array('tab_details' => 'view'), '&'),
763
                          'caption' => $lang['media_viewtab']);
764
765
    list(, $mime) = mimetype($image);
766
    if ($mime == 'image/jpeg' && file_exists(mediaFN($image))) {
767
        $tabs['edit'] = array('href'    => media_managerURL(array('tab_details' => 'edit'), '&'),
768
                              'caption' => $lang['media_edittab']);
769
    }
770
    if ($conf['mediarevisions']) {
771
        $tabs['history'] = array('href'    => media_managerURL(array('tab_details' => 'history'), '&'),
772
                                 'caption' => $lang['media_historytab']);
773
    }
774
775
    html_tabs($tabs, $selected_tab);
776
}
777
778
/**
779
 * Prints options for the tab that displays a list of all files
780
 *
781
 * @author Kate Arzamastseva <[email protected]>
782
 */
783
function media_tab_files_options(){
784
    global $lang;
785
    global $INPUT;
786
    global $ID;
787
    $form = new Doku_Form(array('class' => 'options', 'method' => 'get',
788
                                'action' => wl($ID)));
789
    $media_manager_params = media_managerURL(array(), '', false, true);
790
    foreach($media_manager_params as $pKey => $pVal){
791
        $form->addHidden($pKey, $pVal);
792
    }
793
    $form->addHidden('sectok', null);
794
    if ($INPUT->has('q')) {
795
        $form->addHidden('q', $INPUT->str('q'));
796
    }
797
    $form->addElement('<ul>'.NL);
798
    foreach(array('list' => array('listType', array('thumbs', 'rows')),
799
                  'sort' => array('sortBy', array('name', 'date')))
800
            as $group => $content) {
801
        $checked = "_media_get_${group}_type";
802
        $checked = $checked();
803
804
        $form->addElement('<li class="' . $content[0] . '">');
805
        foreach($content[1] as $option) {
806
            $attrs = array();
807
            if ($checked == $option) {
808
                $attrs['checked'] = 'checked';
809
            }
810
            $form->addElement(form_makeRadioField($group . '_dwmedia', $option,
811
                                       $lang['media_' . $group . '_' . $option],
812
                                                  $content[0] . '__' . $option,
813
                                                  $option, $attrs));
814
        }
815
        $form->addElement('</li>'.NL);
816
    }
817
    $form->addElement('<li>');
818
    $form->addElement(form_makeButton('submit', '', $lang['btn_apply']));
819
    $form->addElement('</li>'.NL);
820
    $form->addElement('</ul>'.NL);
821
    $form->printForm();
822
}
823
824
/**
825
 * Returns type of sorting for the list of files in media manager
826
 *
827
 * @author Kate Arzamastseva <[email protected]>
828
 *
829
 * @return string - sort type
830
 */
831
function _media_get_sort_type() {
832
    return _media_get_display_param('sort', array('default' => 'name', 'date'));
833
}
834
835
/**
836
 * Returns type of listing for the list of files in media manager
837
 *
838
 * @author Kate Arzamastseva <[email protected]>
839
 *
840
 * @return string - list type
841
 */
842
function _media_get_list_type() {
843
    return _media_get_display_param('list', array('default' => 'thumbs', 'rows'));
844
}
845
846
/**
847
 * Get display parameters
848
 *
849
 * @param string $param   name of parameter
850
 * @param array  $values  allowed values, where default value has index key 'default'
851
 * @return string the parameter value
852
 */
853
function _media_get_display_param($param, $values) {
854
    global $INPUT;
855
    if (in_array($INPUT->str($param), $values)) {
856
        // FIXME: Set cookie
857
        return $INPUT->str($param);
858
    } else {
859
        $val = get_doku_pref($param, $values['default']);
860
        if (!in_array($val, $values)) {
861
            $val = $values['default'];
862
        }
863
        return $val;
864
    }
865
}
866
867
/**
868
 * Prints tab that displays a list of all files
869
 *
870
 * @author Kate Arzamastseva <[email protected]>
871
 *
872
 * @param string    $ns
873
 * @param null|int  $auth permission level
874
 * @param string    $jump item id
875
 */
876
function media_tab_files($ns,$auth=null,$jump='') {
877
    global $lang;
878
    if(is_null($auth)) $auth = auth_quickaclcheck("$ns:*");
879
880
    if($auth < AUTH_READ){
881
        echo '<div class="nothing">'.$lang['media_perm_read'].'</div>'.NL;
882
    }else{
883
        media_filelist($ns,$auth,$jump,true,_media_get_sort_type());
884
    }
885
}
886
887
/**
888
 * Prints tab that displays uploading form
889
 *
890
 * @author Kate Arzamastseva <[email protected]>
891
 *
892
 * @param string   $ns
893
 * @param null|int $auth permission level
894
 * @param string   $jump item id
895
 */
896
function media_tab_upload($ns,$auth=null,$jump='') {
897
    global $lang;
898
    if(is_null($auth)) $auth = auth_quickaclcheck("$ns:*");
899
900
    echo '<div class="upload">'.NL;
901
    if ($auth >= AUTH_UPLOAD) {
902
        echo '<p>' . $lang['mediaupload'] . '</p>';
903
    }
904
    media_uploadform($ns, $auth, true);
905
    echo '</div>'.NL;
906
}
907
908
/**
909
 * Prints tab that displays search form
910
 *
911
 * @author Kate Arzamastseva <[email protected]>
912
 *
913
 * @param string $ns
914
 * @param null|int $auth permission level
915
 */
916
function media_tab_search($ns,$auth=null) {
917
    global $INPUT;
918
919
    $do = $INPUT->str('mediado');
920
    $query = $INPUT->str('q');
921
    echo '<div class="search">'.NL;
922
923
    media_searchform($ns, $query, true);
924
    if ($do == 'searchlist' || $query) {
925
        media_searchlist($query,$ns,$auth,true,_media_get_sort_type());
926
    }
927
    echo '</div>'.NL;
928
}
929
930
/**
931
 * Prints tab that displays mediafile details
932
 *
933
 * @author Kate Arzamastseva <[email protected]>
934
 *
935
 * @param string     $image media id
936
 * @param string     $ns
937
 * @param null|int   $auth  permission level
938
 * @param string|int $rev   revision timestamp or empty string
939
 */
940
function media_tab_view($image, $ns, $auth=null, $rev='') {
941
    global $lang;
942
    if(is_null($auth)) $auth = auth_quickaclcheck("$ns:*");
943
944
    if ($image && $auth >= AUTH_READ) {
945
        $meta = new JpegMeta(mediaFN($image, $rev));
946
        media_preview($image, $auth, $rev, $meta);
947
        media_preview_buttons($image, $auth, $rev);
948
        media_details($image, $auth, $rev, $meta);
949
950
    } else {
951
        echo '<div class="nothing">'.$lang['media_perm_read'].'</div>'.NL;
952
    }
953
}
954
955
/**
956
 * Prints tab that displays form for editing mediafile metadata
957
 *
958
 * @author Kate Arzamastseva <[email protected]>
959
 *
960
 * @param string     $image media id
961
 * @param string     $ns
962
 * @param null|int   $auth permission level
963
 */
964
function media_tab_edit($image, $ns, $auth=null) {
965
    if(is_null($auth)) $auth = auth_quickaclcheck("$ns:*");
966
967
    if ($image) {
968
        list(, $mime) = mimetype($image);
969
        if ($mime == 'image/jpeg') media_metaform($image,$auth);
970
    }
971
}
972
973
/**
974
 * Prints tab that displays mediafile revisions
975
 *
976
 * @author Kate Arzamastseva <[email protected]>
977
 *
978
 * @param string     $image media id
979
 * @param string     $ns
980
 * @param null|int   $auth permission level
981
 */
982
function media_tab_history($image, $ns, $auth=null) {
983
    global $lang;
984
    global $INPUT;
985
986
    if(is_null($auth)) $auth = auth_quickaclcheck("$ns:*");
987
    $do = $INPUT->str('mediado');
988
989
    if ($auth >= AUTH_READ && $image) {
990
        if ($do == 'diff'){
991
            media_diff($image, $ns, $auth);
992
        } else {
993
            $first = $INPUT->int('first');
994
            html_revisions($first, $image);
995
        }
996
    } else {
997
        echo '<div class="nothing">'.$lang['media_perm_read'].'</div>'.NL;
998
    }
999
}
1000
1001
/**
1002
 * Prints mediafile details
1003
 *
1004
 * @param string         $image media id
1005
 * @param int            $auth permission level
1006
 * @param int|string     $rev revision timestamp or empty string
1007
 * @param JpegMeta|bool  $meta
1008
 *
1009
 * @author Kate Arzamastseva <[email protected]>
1010
 */
1011
function media_preview($image, $auth, $rev='', $meta=false) {
1012
1013
    $size = media_image_preview_size($image, $rev, $meta);
1014
1015
    if ($size) {
1016
        global $lang;
1017
        echo '<div class="image">';
1018
1019
        $more = array();
1020
        if ($rev) {
1021
            $more['rev'] = $rev;
1022
        } else {
1023
            $t = @filemtime(mediaFN($image));
1024
            $more['t'] = $t;
1025
        }
1026
1027
        $more['w'] = $size[0];
1028
        $more['h'] = $size[1];
1029
        $src = ml($image, $more);
1030
1031
        echo '<a href="'.$src.'" target="_blank" title="'.$lang['mediaview'].'">';
1032
        echo '<img src="'.$src.'" alt="" style="max-width: '.$size[0].'px;" />';
1033
        echo '</a>';
1034
1035
        echo '</div>'.NL;
1036
    }
1037
}
1038
1039
/**
1040
 * Prints mediafile action buttons
1041
 *
1042
 * @author Kate Arzamastseva <[email protected]>
1043
 *
1044
 * @param string     $image media id
1045
 * @param int        $auth  permission level
1046
 * @param string|int $rev   revision timestamp, or empty string
1047
 */
1048
function media_preview_buttons($image, $auth, $rev='') {
1049
    global $lang, $conf;
1050
1051
    echo '<ul class="actions">'.NL;
1052
1053
    if($auth >= AUTH_DELETE && !$rev && file_exists(mediaFN($image))){
1054
1055
        // delete button
1056
        $form = new Doku_Form(array('id' => 'mediamanager__btn_delete',
1057
            'action'=>media_managerURL(array('delete' => $image), '&')));
1058
        $form->addElement(form_makeButton('submit','',$lang['btn_delete']));
1059
        echo '<li>';
1060
        $form->printForm();
1061
        echo '</li>'.NL;
1062
    }
1063
1064
    $auth_ow = (($conf['mediarevisions']) ? AUTH_UPLOAD : AUTH_DELETE);
1065
    if($auth >= $auth_ow && !$rev){
1066
1067
        // upload new version button
1068
        $form = new Doku_Form(array('id' => 'mediamanager__btn_update',
1069
            'action'=>media_managerURL(array('image' => $image, 'mediado' => 'update'), '&')));
1070
        $form->addElement(form_makeButton('submit','',$lang['media_update']));
1071
        echo '<li>';
1072
        $form->printForm();
1073
        echo '</li>'.NL;
1074
    }
1075
1076
    if($auth >= AUTH_UPLOAD && $rev && $conf['mediarevisions'] && file_exists(mediaFN($image, $rev))){
1077
1078
        // restore button
1079
        $form = new Doku_Form(array('id' => 'mediamanager__btn_restore',
1080
            'action'=>media_managerURL(array('image' => $image), '&')));
1081
        $form->addHidden('mediado','restore');
1082
        $form->addHidden('rev',$rev);
1083
        $form->addElement(form_makeButton('submit','',$lang['media_restore']));
1084
        echo '<li>';
1085
        $form->printForm();
1086
        echo '</li>'.NL;
1087
    }
1088
1089
    echo '</ul>'.NL;
1090
}
1091
1092
/**
1093
 * Returns image width and height for mediamanager preview panel
1094
 *
1095
 * @author Kate Arzamastseva <[email protected]>
1096
 * @param string         $image
1097
 * @param int|string     $rev
1098
 * @param JpegMeta|bool  $meta
1099
 * @param int            $size
1100
 * @return array|false
1101
 */
1102
function media_image_preview_size($image, $rev, $meta, $size = 500) {
1103
    if (!preg_match("/\.(jpe?g|gif|png)$/", $image) || !file_exists(mediaFN($image, $rev))) return false;
1104
1105
    $info = getimagesize(mediaFN($image, $rev));
1106
    $w = (int) $info[0];
1107
    $h = (int) $info[1];
1108
1109
    if($meta && ($w > $size || $h > $size)){
1110
        $ratio = $meta->getResizeRatio($size, $size);
1111
        $w = floor($w * $ratio);
1112
        $h = floor($h * $ratio);
1113
    }
1114
    return array($w, $h);
1115
}
1116
1117
/**
1118
 * Returns the requested EXIF/IPTC tag from the image meta
1119
 *
1120
 * @author Kate Arzamastseva <[email protected]>
1121
 *
1122
 * @param array    $tags array with tags, first existing is returned
1123
 * @param JpegMeta $meta
1124
 * @param string   $alt  alternative value
1125
 * @return string
1126
 */
1127
function media_getTag($tags,$meta,$alt=''){
1128
    if($meta === false) return $alt;
1129
    $info = $meta->getField($tags);
1130
    if($info == false) return $alt;
1131
    return $info;
1132
}
1133
1134
/**
1135
 * Returns mediafile tags
1136
 *
1137
 * @author Kate Arzamastseva <[email protected]>
1138
 *
1139
 * @param JpegMeta $meta
1140
 * @return array list of tags of the mediafile
1141
 */
1142
function media_file_tags($meta) {
1143
    // load the field descriptions
1144
    static $fields = null;
1145
    if(is_null($fields)){
1146
        $config_files = getConfigFiles('mediameta');
1147
        foreach ($config_files as $config_file) {
1148
            if(file_exists($config_file)) include($config_file);
1149
        }
1150
    }
1151
1152
    $tags = array();
1153
1154
    foreach($fields as $key => $tag){
1155
        $t = array();
1156
        if (!empty($tag[0])) $t = array($tag[0]);
1157
        if(isset($tag[3]) && is_array($tag[3])) $t = array_merge($t,$tag[3]);
1158
        $value = media_getTag($t, $meta);
1159
        $tags[] = array('tag' => $tag, 'value' => $value);
1160
    }
1161
1162
    return $tags;
1163
}
1164
1165
/**
1166
 * Prints mediafile tags
1167
 *
1168
 * @author Kate Arzamastseva <[email protected]>
1169
 *
1170
 * @param string        $image image id
1171
 * @param int           $auth  permission level
1172
 * @param string|int    $rev   revision timestamp, or empty string
1173
 * @param bool|JpegMeta $meta  image object, or create one if false
1174
 */
1175
function media_details($image, $auth, $rev='', $meta=false) {
1176
    global $lang;
1177
1178
    if (!$meta) $meta = new JpegMeta(mediaFN($image, $rev));
1179
    $tags = media_file_tags($meta);
1180
1181
    echo '<dl>'.NL;
1182
    foreach($tags as $tag){
1183
        if ($tag['value']) {
1184
            $value = cleanText($tag['value']);
1185
            echo '<dt>'.$lang[$tag['tag'][1]].'</dt><dd>';
1186
            if ($tag['tag'][2] == 'date') echo dformat($value);
1187
            else echo hsc($value);
1188
            echo '</dd>'.NL;
1189
        }
1190
    }
1191
    echo '</dl>'.NL;
1192
    echo '<dl>'.NL;
1193
    echo '<dt>'.$lang['reference'].':</dt>';
1194
    $media_usage = ft_mediause($image,true);
1195
    if(count($media_usage) > 0){
1196
        foreach($media_usage as $path){
1197
            echo '<dd>'.html_wikilink($path).'</dd>';
1198
        }
1199
    }else{
1200
        echo '<dd>'.$lang['nothingfound'].'</dd>';
1201
    }
1202
    echo '</dl>'.NL;
1203
1204
}
1205
1206
/**
1207
 * Shows difference between two revisions of file
1208
 *
1209
 * @author Kate Arzamastseva <[email protected]>
1210
 *
1211
 * @param string $image  image id
1212
 * @param string $ns
1213
 * @param int $auth permission level
1214
 * @param bool $fromajax
1215
 * @return false|null|string
1216
 */
1217
function media_diff($image, $ns, $auth, $fromajax = false) {
1218
    global $conf;
1219
    global $INPUT;
1220
1221
    if ($auth < AUTH_READ || !$image || !$conf['mediarevisions']) return '';
1222
1223
    $rev1 = $INPUT->int('rev');
1224
1225
    $rev2 = $INPUT->ref('rev2');
1226
    if(is_array($rev2)){
1227
        $rev1 = (int) $rev2[0];
1228
        $rev2 = (int) $rev2[1];
1229
1230
        if(!$rev1){
1231
            $rev1 = $rev2;
1232
            unset($rev2);
1233
        }
1234
    }else{
1235
        $rev2 = $INPUT->int('rev2');
1236
    }
1237
1238
    if ($rev1 && !file_exists(mediaFN($image, $rev1))) $rev1 = false;
1239
    if ($rev2 && !file_exists(mediaFN($image, $rev2))) $rev2 = false;
1240
1241
    if($rev1 && $rev2){            // two specific revisions wanted
1242
        // make sure order is correct (older on the left)
1243
        if($rev1 < $rev2){
1244
            $l_rev = $rev1;
1245
            $r_rev = $rev2;
1246
        }else{
1247
            $l_rev = $rev2;
1248
            $r_rev = $rev1;
1249
        }
1250
    }elseif($rev1){                // single revision given, compare to current
1251
        $r_rev = '';
1252
        $l_rev = $rev1;
1253
    }else{                        // no revision was given, compare previous to current
1254
        $r_rev = '';
1255
        $medialog = new MediaChangeLog($image);
1256
        $revs = $medialog->getRevisions(0, 1);
1257
        if (file_exists(mediaFN($image, $revs[0]))) {
1258
            $l_rev = $revs[0];
1259
        } else {
1260
            $l_rev = '';
1261
        }
1262
    }
1263
1264
    // prepare event data
1265
    $data = array();
1266
    $data[0] = $image;
1267
    $data[1] = $l_rev;
1268
    $data[2] = $r_rev;
1269
    $data[3] = $ns;
1270
    $data[4] = $auth;
1271
    $data[5] = $fromajax;
1272
1273
    // trigger event
1274
    return Event::createAndTrigger('MEDIA_DIFF', $data, '_media_file_diff', true);
1275
}
1276
1277
/**
1278
 * Callback for media file diff
1279
 *
1280
 * @param array $data event data
1281
 * @return false|null
1282
 */
1283
function _media_file_diff($data) {
1284
    if(is_array($data) && count($data)===6) {
1285
        media_file_diff($data[0], $data[1], $data[2], $data[3], $data[4], $data[5]);
1286
    } else {
1287
        return false;
1288
    }
1289
}
1290
1291
/**
1292
 * Shows difference between two revisions of image
1293
 *
1294
 * @author Kate Arzamastseva <[email protected]>
1295
 *
1296
 * @param string $image
1297
 * @param string|int $l_rev revision timestamp, or empty string
1298
 * @param string|int $r_rev revision timestamp, or empty string
1299
 * @param string $ns
1300
 * @param int $auth permission level
1301
 * @param bool $fromajax
1302
 */
1303
function media_file_diff($image, $l_rev, $r_rev, $ns, $auth, $fromajax){
1304
    global $lang;
1305
    global $INPUT;
1306
1307
    $l_meta = new JpegMeta(mediaFN($image, $l_rev));
1308
    $r_meta = new JpegMeta(mediaFN($image, $r_rev));
1309
1310
    $is_img = preg_match('/\.(jpe?g|gif|png)$/', $image);
1311
    if ($is_img) {
1312
        $l_size = media_image_preview_size($image, $l_rev, $l_meta);
1313
        $r_size = media_image_preview_size($image, $r_rev, $r_meta);
1314
        $is_img = ($l_size && $r_size && ($l_size[0] >= 30 || $r_size[0] >= 30));
1315
1316
        $difftype = $INPUT->str('difftype');
1317
1318
        if (!$fromajax) {
1319
            $form = new Doku_Form(array(
0 ignored issues
show
Deprecated Code introduced by
The class Doku_Form has been deprecated with message: 2019-07-14

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
1320
                'action' => media_managerURL(array(), '&'),
1321
                'method' => 'get',
1322
                'id' => 'mediamanager__form_diffview',
1323
                'class' => 'diffView'
1324
            ));
1325
            $form->addHidden('sectok', null);
1326
            $form->addElement('<input type="hidden" name="rev2[]" value="'.$l_rev.'" ></input>');
1327
            $form->addElement('<input type="hidden" name="rev2[]" value="'.$r_rev.'" ></input>');
1328
            $form->addHidden('mediado', 'diff');
1329
            $form->printForm();
1330
1331
            echo NL.'<div id="mediamanager__diff" >'.NL;
1332
        }
1333
1334
        if ($difftype == 'opacity' || $difftype == 'portions') {
1335
            media_image_diff($image, $l_rev, $r_rev, $l_size, $r_size, $difftype);
1336
            if (!$fromajax) echo '</div>';
1337
            return;
1338
        }
1339
    }
1340
1341
    list($l_head, $r_head) = html_diff_head($l_rev, $r_rev, $image, true);
1342
1343
    ?>
1344
    <div class="table">
1345
    <table>
1346
      <tr>
1347
        <th><?php echo $l_head; ?></th>
1348
        <th><?php echo $r_head; ?></th>
1349
      </tr>
1350
    <?php
1351
1352
    echo '<tr class="image">';
1353
    echo '<td>';
1354
    media_preview($image, $auth, $l_rev, $l_meta);
1355
    echo '</td>';
1356
1357
    echo '<td>';
1358
    media_preview($image, $auth, $r_rev, $r_meta);
1359
    echo '</td>';
1360
    echo '</tr>'.NL;
1361
1362
    echo '<tr class="actions">';
1363
    echo '<td>';
1364
    media_preview_buttons($image, $auth, $l_rev);
1365
    echo '</td>';
1366
1367
    echo '<td>';
1368
    media_preview_buttons($image, $auth, $r_rev);
1369
    echo '</td>';
1370
    echo '</tr>'.NL;
1371
1372
    $l_tags = media_file_tags($l_meta);
1373
    $r_tags = media_file_tags($r_meta);
1374
    // FIXME r_tags-only stuff
1375
    foreach ($l_tags as $key => $l_tag) {
1376
        if ($l_tag['value'] != $r_tags[$key]['value']) {
1377
            $r_tags[$key]['highlighted'] = true;
1378
            $l_tags[$key]['highlighted'] = true;
1379
        } else if (!$l_tag['value'] || !$r_tags[$key]['value']) {
1380
            unset($r_tags[$key]);
1381
            unset($l_tags[$key]);
1382
        }
1383
    }
1384
1385
    echo '<tr>';
1386
    foreach(array($l_tags,$r_tags) as $tags){
1387
        echo '<td>'.NL;
1388
1389
        echo '<dl class="img_tags">';
1390
        foreach($tags as $tag){
1391
            $value = cleanText($tag['value']);
1392
            if (!$value) $value = '-';
1393
            echo '<dt>'.$lang[$tag['tag'][1]].'</dt>';
1394
            echo '<dd>';
1395
            if ($tag['highlighted']) {
1396
                echo '<strong>';
1397
            }
1398
            if ($tag['tag'][2] == 'date') echo dformat($value);
1399
            else echo hsc($value);
1400
            if ($tag['highlighted']) {
1401
                echo '</strong>';
1402
            }
1403
            echo '</dd>';
1404
        }
1405
        echo '</dl>'.NL;
1406
1407
        echo '</td>';
1408
    }
1409
    echo '</tr>'.NL;
1410
1411
    echo '</table>'.NL;
1412
    echo '</div>'.NL;
1413
1414
    if ($is_img && !$fromajax) echo '</div>';
1415
}
1416
1417
/**
1418
 * Prints two images side by side
1419
 * and slider
1420
 *
1421
 * @author Kate Arzamastseva <[email protected]>
1422
 *
1423
 * @param string $image   image id
1424
 * @param int    $l_rev   revision timestamp, or empty string
1425
 * @param int    $r_rev   revision timestamp, or empty string
1426
 * @param array  $l_size  array with width and height
1427
 * @param array  $r_size  array with width and height
1428
 * @param string $type
1429
 */
1430
function media_image_diff($image, $l_rev, $r_rev, $l_size, $r_size, $type) {
1431
    if ($l_size != $r_size) {
1432
        if ($r_size[0] > $l_size[0]) {
1433
            $l_size = $r_size;
1434
        }
1435
    }
1436
1437
    $l_more = array('rev' => $l_rev, 'h' => $l_size[1], 'w' => $l_size[0]);
1438
    $r_more = array('rev' => $r_rev, 'h' => $l_size[1], 'w' => $l_size[0]);
1439
1440
    $l_src = ml($image, $l_more);
1441
    $r_src = ml($image, $r_more);
1442
1443
    // slider
1444
    echo '<div class="slider" style="max-width: '.($l_size[0]-20).'px;" ></div>'.NL;
1445
1446
    // two images in divs
1447
    echo '<div class="imageDiff ' . $type . '">'.NL;
1448
    echo '<div class="image1" style="max-width: '.$l_size[0].'px;">';
1449
    echo '<img src="'.$l_src.'" alt="" />';
1450
    echo '</div>'.NL;
1451
    echo '<div class="image2" style="max-width: '.$l_size[0].'px;">';
1452
    echo '<img src="'.$r_src.'" alt="" />';
1453
    echo '</div>'.NL;
1454
    echo '</div>'.NL;
1455
}
1456
1457
/**
1458
 * Restores an old revision of a media file
1459
 *
1460
 * @param string $image media id
1461
 * @param int    $rev   revision timestamp or empty string
1462
 * @param int    $auth
1463
 * @return string - file's id
1464
 *
1465
 * @author Kate Arzamastseva <[email protected]>
1466
 */
1467
function media_restore($image, $rev, $auth){
1468
    global $conf;
1469
    if ($auth < AUTH_UPLOAD || !$conf['mediarevisions']) return false;
1470
    $removed = (!file_exists(mediaFN($image)) && file_exists(mediaMetaFN($image, '.changes')));
1471
    if (!$image || (!file_exists(mediaFN($image)) && !$removed)) return false;
1472
    if (!$rev || !file_exists(mediaFN($image, $rev))) return false;
1473
    list(,$imime,) = mimetype($image);
1474
    $res = media_upload_finish(mediaFN($image, $rev),
1475
        mediaFN($image),
1476
        $image,
1477
        $imime,
1478
        true,
1479
        'copy');
1480
    if (is_array($res)) {
1481
        msg($res[0], $res[1]);
1482
        return false;
1483
    }
1484
    return $res;
1485
}
1486
1487
/**
1488
 * List all files found by the search request
1489
 *
1490
 * @author Tobias Sarnowski <[email protected]>
1491
 * @author Andreas Gohr <[email protected]>
1492
 * @author Kate Arzamastseva <[email protected]>
1493
 * @triggers MEDIA_SEARCH
1494
 *
1495
 * @param string $query
1496
 * @param string $ns
1497
 * @param null|int $auth
1498
 * @param bool $fullscreen
1499
 * @param string $sort
1500
 */
1501
function media_searchlist($query,$ns,$auth=null,$fullscreen=false,$sort='natural'){
1502
    global $conf;
1503
    global $lang;
1504
1505
    $ns = cleanID($ns);
1506
    $evdata = array(
1507
        'ns'    => $ns,
1508
        'data'  => array(),
1509
        'query' => $query
1510
    );
1511
    if (!blank($query)) {
1512
        $evt = new Event('MEDIA_SEARCH', $evdata);
1513
        if ($evt->advise_before()) {
1514
            $dir = utf8_encodeFN(str_replace(':','/',$evdata['ns']));
1515
            $quoted = preg_quote($evdata['query'],'/');
1516
            //apply globbing
1517
            $quoted = str_replace(array('\*', '\?'), array('.*', '.'), $quoted, $count);
1518
1519
            //if we use globbing file name must match entirely but may be preceded by arbitrary namespace
1520
            if ($count > 0) $quoted = '^([^:]*:)*'.$quoted.'$';
1521
1522
            $pattern = '/'.$quoted.'/i';
1523
            search($evdata['data'],
1524
                    $conf['mediadir'],
1525
                    'search_media',
1526
                    array('showmsg'=>false,'pattern'=>$pattern),
1527
                    $dir,
1528
                    1,
1529
                    $sort);
1530
        }
1531
        $evt->advise_after();
1532
        unset($evt);
1533
    }
1534
1535
    if (!$fullscreen) {
1536
        echo '<h1 id="media__ns">'.sprintf($lang['searchmedia_in'],hsc($ns).':*').'</h1>'.NL;
1537
        media_searchform($ns,$query);
1538
    }
1539
1540
    if(!count($evdata['data'])){
1541
        echo '<div class="nothing">'.$lang['nothingfound'].'</div>'.NL;
1542
    }else {
1543
        if ($fullscreen) {
1544
            echo '<ul class="' . _media_get_list_type() . '">';
1545
        }
1546
        foreach($evdata['data'] as $item){
1547
            if (!$fullscreen) media_printfile($item,$item['perm'],'',true);
1548
            else media_printfile_thumbs($item,$item['perm'],false,true);
1549
        }
1550
        if ($fullscreen) echo '</ul>'.NL;
1551
    }
1552
}
1553
1554
/**
1555
 * Formats and prints one file in the list
1556
 *
1557
 * @param array     $item
1558
 * @param int       $auth              permission level
1559
 * @param string    $jump              item id
1560
 * @param bool      $display_namespace
1561
 */
1562
function media_printfile($item,$auth,$jump,$display_namespace=false){
1563
    global $lang;
1564
1565
    // Prepare zebra coloring
1566
    // I always wanted to use this variable name :-D
1567
    static $twibble = 1;
1568
    $twibble *= -1;
1569
    $zebra = ($twibble == -1) ? 'odd' : 'even';
1570
1571
    // Automatically jump to recent action
1572
    if($jump == $item['id']) {
1573
        $jump = ' id="scroll__here" ';
1574
    }else{
1575
        $jump = '';
1576
    }
1577
1578
    // Prepare fileicons
1579
    list($ext) = mimetype($item['file'],false);
1580
    $class = preg_replace('/[^_\-a-z0-9]+/i','_',$ext);
1581
    $class = 'select mediafile mf_'.$class;
1582
1583
    // Prepare filename
1584
    $file = utf8_decodeFN($item['file']);
1585
1586
    // Prepare info
1587
    $info = '';
1588
    if($item['isimg']){
1589
        $info .= (int) $item['meta']->getField('File.Width');
1590
        $info .= '&#215;';
1591
        $info .= (int) $item['meta']->getField('File.Height');
1592
        $info .= ' ';
1593
    }
1594
    $info .= '<i>'.dformat($item['mtime']).'</i>';
1595
    $info .= ' ';
1596
    $info .= filesize_h($item['size']);
1597
1598
    // output
1599
    echo '<div class="'.$zebra.'"'.$jump.' title="'.hsc($item['id']).'">'.NL;
1600
    if (!$display_namespace) {
1601
        echo '<a id="h_:'.$item['id'].'" class="'.$class.'">'.hsc($file).'</a> ';
1602
    } else {
1603
        echo '<a id="h_:'.$item['id'].'" class="'.$class.'">'.hsc($item['id']).'</a><br/>';
1604
    }
1605
    echo '<span class="info">('.$info.')</span>'.NL;
1606
1607
    // view button
1608
    $link = ml($item['id'],'',true);
1609
    echo ' <a href="'.$link.'" target="_blank"><img src="'.DOKU_BASE.'lib/images/magnifier.png" '.
1610
        'alt="'.$lang['mediaview'].'" title="'.$lang['mediaview'].'" class="btn" /></a>';
1611
1612
    // mediamanager button
1613
    $link = wl('',array('do'=>'media','image'=>$item['id'],'ns'=>getNS($item['id'])));
1614
    echo ' <a href="'.$link.'" target="_blank"><img src="'.DOKU_BASE.'lib/images/mediamanager.png" '.
1615
        'alt="'.$lang['btn_media'].'" title="'.$lang['btn_media'].'" class="btn" /></a>';
1616
1617
    // delete button
1618
    if($item['writable'] && $auth >= AUTH_DELETE){
1619
        $link = DOKU_BASE.'lib/exe/mediamanager.php?delete='.rawurlencode($item['id']).
1620
            '&amp;sectok='.getSecurityToken();
1621
        echo ' <a href="'.$link.'" class="btn_media_delete" title="'.$item['id'].'">'.
1622
            '<img src="'.DOKU_BASE.'lib/images/trash.png" alt="'.$lang['btn_delete'].'" '.
1623
            'title="'.$lang['btn_delete'].'" class="btn" /></a>';
1624
    }
1625
1626
    echo '<div class="example" id="ex_'.str_replace(':','_',$item['id']).'">';
1627
    echo $lang['mediausage'].' <code>{{:'.$item['id'].'}}</code>';
1628
    echo '</div>';
1629
    if($item['isimg']) media_printimgdetail($item);
1630
    echo '<div class="clearer"></div>'.NL;
1631
    echo '</div>'.NL;
1632
}
1633
1634
/**
1635
 * Display a media icon
1636
 *
1637
 * @param string $filename media id
1638
 * @param string $size     the size subfolder, if not specified 16x16 is used
1639
 * @return string html
1640
 */
1641
function media_printicon($filename, $size=''){
1642
    list($ext) = mimetype(mediaFN($filename),false);
1643
1644
    if (file_exists(DOKU_INC.'lib/images/fileicons/'.$size.'/'.$ext.'.png')) {
1645
        $icon = DOKU_BASE.'lib/images/fileicons/'.$size.'/'.$ext.'.png';
1646
    } else {
1647
        $icon = DOKU_BASE.'lib/images/fileicons/'.$size.'/file.png';
1648
    }
1649
1650
    return '<img src="'.$icon.'" alt="'.$filename.'" class="icon" />';
1651
}
1652
1653
/**
1654
 * Formats and prints one file in the list in the thumbnails view
1655
 *
1656
 * @author Kate Arzamastseva <[email protected]>
1657
 *
1658
 * @param array       $item
1659
 * @param int         $auth              permission level
1660
 * @param bool|string $jump              item id
1661
 * @param bool        $display_namespace
1662
 */
1663
function media_printfile_thumbs($item,$auth,$jump=false,$display_namespace=false){
1664
1665
    // Prepare filename
1666
    $file = utf8_decodeFN($item['file']);
1667
1668
    // output
1669
    echo '<li><dl title="'.hsc($item['id']).'">'.NL;
1670
1671
        echo '<dt>';
1672
    if($item['isimg']) {
1673
        media_printimgdetail($item, true);
1674
1675
    } else {
1676
        echo '<a id="d_:'.$item['id'].'" class="image" title="'.$item['id'].'" href="'.
1677
            media_managerURL(array('image' => hsc($item['id']), 'ns' => getNS($item['id']),
1678
            'tab_details' => 'view')).'">';
1679
        echo media_printicon($item['id'], '32x32');
1680
        echo '</a>';
1681
    }
1682
    echo '</dt>'.NL;
1683
    if (!$display_namespace) {
1684
        $name = hsc($file);
1685
    } else {
1686
        $name = hsc($item['id']);
1687
    }
1688
    echo '<dd class="name"><a href="'.media_managerURL(array('image' => hsc($item['id']), 'ns' => getNS($item['id']),
1689
        'tab_details' => 'view')).'" id="h_:'.$item['id'].'">'.$name.'</a></dd>'.NL;
1690
1691
    if($item['isimg']){
1692
        $size = '';
1693
        $size .= (int) $item['meta']->getField('File.Width');
1694
        $size .= '&#215;';
1695
        $size .= (int) $item['meta']->getField('File.Height');
1696
        echo '<dd class="size">'.$size.'</dd>'.NL;
1697
    } else {
1698
        echo '<dd class="size">&#160;</dd>'.NL;
1699
    }
1700
    $date = dformat($item['mtime']);
1701
    echo '<dd class="date">'.$date.'</dd>'.NL;
1702
    $filesize = filesize_h($item['size']);
1703
    echo '<dd class="filesize">'.$filesize.'</dd>'.NL;
1704
    echo '</dl></li>'.NL;
1705
}
1706
1707
/**
1708
 * Prints a thumbnail and metainfo
1709
 *
1710
 * @param array $item
1711
 * @param bool  $fullscreen
1712
 */
1713
function media_printimgdetail($item, $fullscreen=false){
1714
    // prepare thumbnail
1715
    $size = $fullscreen ? 90 : 120;
1716
1717
    $w = (int) $item['meta']->getField('File.Width');
1718
    $h = (int) $item['meta']->getField('File.Height');
1719
    if($w>$size || $h>$size){
1720
        if (!$fullscreen) {
1721
            $ratio = $item['meta']->getResizeRatio($size);
1722
        } else {
1723
            $ratio = $item['meta']->getResizeRatio($size,$size);
1724
        }
1725
        $w = floor($w * $ratio);
1726
        $h = floor($h * $ratio);
1727
    }
1728
    $src = ml($item['id'],array('w'=>$w,'h'=>$h,'t'=>$item['mtime']));
1729
    $p = array();
1730
    if (!$fullscreen) {
1731
        // In fullscreen mediamanager view, image resizing is done via CSS.
1732
        $p['width']  = $w;
1733
        $p['height'] = $h;
1734
    }
1735
    $p['alt']    = $item['id'];
1736
    $att = buildAttributes($p);
1737
1738
    // output
1739
    if ($fullscreen) {
1740
        echo '<a id="l_:'.$item['id'].'" class="image thumb" href="'.
1741
            media_managerURL(['image' => hsc($item['id']), 'ns' => getNS($item['id']), 'tab_details' => 'view']).'">';
1742
        echo '<img src="'.$src.'" '.$att.' />';
1743
        echo '</a>';
1744
    }
1745
1746
    if ($fullscreen) return;
1747
1748
    echo '<div class="detail">';
1749
    echo '<div class="thumb">';
1750
    echo '<a id="d_:'.$item['id'].'" class="select">';
1751
    echo '<img src="'.$src.'" '.$att.' />';
1752
    echo '</a>';
1753
    echo '</div>';
1754
1755
    // read EXIF/IPTC data
1756
    $t = $item['meta']->getField(array('IPTC.Headline','xmp.dc:title'));
1757
    $d = $item['meta']->getField(array('IPTC.Caption','EXIF.UserComment',
1758
                'EXIF.TIFFImageDescription',
1759
                'EXIF.TIFFUserComment'));
1760
    if(\dokuwiki\Utf8\PhpString::strlen($d) > 250) $d = \dokuwiki\Utf8\PhpString::substr($d,0,250).'...';
1761
    $k = $item['meta']->getField(array('IPTC.Keywords','IPTC.Category','xmp.dc:subject'));
1762
1763
    // print EXIF/IPTC data
1764
    if($t || $d || $k ){
1765
        echo '<p>';
1766
        if($t) echo '<strong>'.hsc($t).'</strong><br />';
1767
        if($d) echo hsc($d).'<br />';
1768
        if($t) echo '<em>'.hsc($k).'</em>';
1769
        echo '</p>';
1770
    }
1771
    echo '</div>';
1772
}
1773
1774
/**
1775
 * Build link based on the current, adding/rewriting parameters
1776
 *
1777
 * @author Kate Arzamastseva <[email protected]>
1778
 *
1779
 * @param array|bool $params
1780
 * @param string     $amp           separator
1781
 * @param bool       $abs           absolute url?
1782
 * @param bool       $params_array  return the parmeters array?
1783
 * @return string|array - link or link parameters
1784
 */
1785
function media_managerURL($params=false, $amp='&amp;', $abs=false, $params_array=false) {
1786
    global $ID;
1787
    global $INPUT;
1788
1789
    $gets = array('do' => 'media');
1790
    $media_manager_params = array('tab_files', 'tab_details', 'image', 'ns', 'list', 'sort');
1791
    foreach ($media_manager_params as $x) {
1792
        if ($INPUT->has($x)) $gets[$x] = $INPUT->str($x);
1793
    }
1794
1795
    if ($params) {
1796
        $gets = $params + $gets;
1797
    }
1798
    unset($gets['id']);
1799
    if (isset($gets['delete'])) {
1800
        unset($gets['image']);
1801
        unset($gets['tab_details']);
1802
    }
1803
1804
    if ($params_array) return $gets;
1805
1806
    return wl($ID,$gets,$abs,$amp);
1807
}
1808
1809
/**
1810
 * Print the media upload form if permissions are correct
1811
 *
1812
 * @author Andreas Gohr <[email protected]>
1813
 * @author Kate Arzamastseva <[email protected]>
1814
 *
1815
 * @param string $ns
1816
 * @param int    $auth permission level
1817
 * @param bool  $fullscreen
1818
 */
1819
function media_uploadform($ns, $auth, $fullscreen = false){
1820
    global $lang;
1821
    global $conf;
1822
    global $INPUT;
1823
1824
    if($auth < AUTH_UPLOAD) {
1825
        echo '<div class="nothing">'.$lang['media_perm_upload'].'</div>'.NL;
1826
        return;
1827
    }
1828
    $auth_ow = (($conf['mediarevisions']) ? AUTH_UPLOAD : AUTH_DELETE);
1829
1830
    $update = false;
1831
    $id = '';
1832
    if ($auth >= $auth_ow && $fullscreen && $INPUT->str('mediado') == 'update') {
1833
        $update = true;
1834
        $id = cleanID($INPUT->str('image'));
1835
    }
1836
1837
    // The default HTML upload form
1838
    $params = array('id'      => 'dw__upload',
1839
                    'enctype' => 'multipart/form-data');
1840
    if (!$fullscreen) {
1841
        $params['action'] = DOKU_BASE.'lib/exe/mediamanager.php';
1842
    } else {
1843
        $params['action'] = media_managerURL(array('tab_files' => 'files',
1844
            'tab_details' => 'view'), '&');
1845
    }
1846
1847
    $form = new Doku_Form($params);
0 ignored issues
show
Deprecated Code introduced by
The class Doku_Form has been deprecated with message: 2019-07-14

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
1848
    if (!$fullscreen) echo '<div class="upload">' . $lang['mediaupload'] . '</div>';
1849
    $form->addElement(formSecurityToken());
1850
    $form->addHidden('ns', hsc($ns));
1851
    $form->addElement(form_makeOpenTag('p'));
1852
    $form->addElement(form_makeFileField('upload', $lang['txt_upload'], 'upload__file'));
1853
    $form->addElement(form_makeCloseTag('p'));
1854
    $form->addElement(form_makeOpenTag('p'));
1855
    $form->addElement(form_makeTextField('mediaid', noNS($id), $lang['txt_filename'], 'upload__name'));
1856
    $form->addElement(form_makeButton('submit', '', $lang['btn_upload']));
1857
    $form->addElement(form_makeCloseTag('p'));
1858
1859
    if($auth >= $auth_ow){
1860
        $form->addElement(form_makeOpenTag('p'));
1861
        $attrs = array();
1862
        if ($update) $attrs['checked'] = 'checked';
1863
        $form->addElement(form_makeCheckboxField('ow', 1, $lang['txt_overwrt'], 'dw__ow', 'check', $attrs));
1864
        $form->addElement(form_makeCloseTag('p'));
1865
    }
1866
1867
    echo NL.'<div id="mediamanager__uploader">'.NL;
1868
    html_form('upload', $form);
1869
1870
    echo '</div>'.NL;
1871
1872
    echo '<p class="maxsize">';
1873
    printf($lang['maxuploadsize'],filesize_h(media_getuploadsize()));
1874
    echo '</p>'.NL;
1875
1876
}
1877
1878
/**
1879
 * Returns the size uploaded files may have
1880
 *
1881
 * This uses a conservative approach using the lowest number found
1882
 * in any of the limiting ini settings
1883
 *
1884
 * @returns int size in bytes
1885
 */
1886
function media_getuploadsize(){
1887
    $okay = 0;
1888
1889
    $post = (int) php_to_byte(@ini_get('post_max_size'));
1890
    $suho = (int) php_to_byte(@ini_get('suhosin.post.max_value_length'));
1891
    $upld = (int) php_to_byte(@ini_get('upload_max_filesize'));
1892
1893
    if($post && ($post < $okay || $okay == 0)) $okay = $post;
1894
    if($suho && ($suho < $okay || $okay == 0)) $okay = $suho;
1895
    if($upld && ($upld < $okay || $okay == 0)) $okay = $upld;
1896
1897
    return $okay;
1898
}
1899
1900
/**
1901
 * Print the search field form
1902
 *
1903
 * @author Tobias Sarnowski <[email protected]>
1904
 * @author Kate Arzamastseva <[email protected]>
1905
 *
1906
 * @param string $ns
1907
 * @param string $query
1908
 * @param bool $fullscreen
1909
 */
1910
function media_searchform($ns,$query='',$fullscreen=false){
1911
    global $lang;
1912
1913
    // The default HTML search form
1914
    $params = array('id' => 'dw__mediasearch');
1915
    if (!$fullscreen) {
1916
        $params['action'] = DOKU_BASE.'lib/exe/mediamanager.php';
1917
    } else {
1918
        $params['action'] = media_managerURL(array(), '&');
1919
    }
1920
    $form = new Doku_Form($params);
0 ignored issues
show
Deprecated Code introduced by
The class Doku_Form has been deprecated with message: 2019-07-14

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
1921
    $form->addHidden('ns', $ns);
1922
    $form->addHidden($fullscreen ? 'mediado' : 'do', 'searchlist');
1923
1924
    $form->addElement(form_makeOpenTag('p'));
1925
    $form->addElement(
1926
        form_makeTextField(
1927
            'q',
1928
            $query,
1929
            $lang['searchmedia'],
1930
            '',
1931
            '',
1932
            array('title' => sprintf($lang['searchmedia_in'], hsc($ns) . ':*'))
1933
        )
1934
    );
1935
    $form->addElement(form_makeButton('submit', '', $lang['btn_search']));
1936
    $form->addElement(form_makeCloseTag('p'));
1937
    html_form('searchmedia', $form);
1938
}
1939
1940
/**
1941
 * Build a tree outline of available media namespaces
1942
 *
1943
 * @author Andreas Gohr <[email protected]>
1944
 *
1945
 * @param string $ns
1946
 */
1947
function media_nstree($ns){
1948
    global $conf;
1949
    global $lang;
1950
1951
    // currently selected namespace
1952
    $ns  = cleanID($ns);
1953
    if(empty($ns)){
1954
        global $ID;
1955
        $ns = (string)getNS($ID);
1956
    }
1957
1958
    $ns_dir  = utf8_encodeFN(str_replace(':','/',$ns));
1959
1960
    $data = array();
1961
    search($data,$conf['mediadir'],'search_index',array('ns' => $ns_dir, 'nofiles' => true));
1962
1963
    // wrap a list with the root level around the other namespaces
1964
    array_unshift($data, array('level' => 0, 'id' => '', 'open' =>'true',
1965
                               'label' => '['.$lang['mediaroot'].']'));
1966
1967
    // insert the current ns into the hierarchy if it isn't already part of it
1968
    $ns_parts = explode(':', $ns);
1969
    $tmp_ns = '';
1970
    $pos = 0;
1971
    foreach ($ns_parts as $level => $part) {
1972
        if ($tmp_ns) $tmp_ns .= ':'.$part;
1973
        else $tmp_ns = $part;
1974
1975
        // find the namespace parts or insert them
1976
        while ($data[$pos]['id'] != $tmp_ns) {
1977
            if (
1978
                $pos >= count($data) ||
1979
                (
1980
                    $data[$pos]['level'] <= $level+1 &&
1981
                    strnatcmp(utf8_encodeFN($data[$pos]['id']), utf8_encodeFN($tmp_ns)) > 0
1982
                )
1983
            ) {
1984
                array_splice($data, $pos, 0, array(array('level' => $level+1, 'id' => $tmp_ns, 'open' => 'true')));
1985
                break;
1986
            }
1987
            ++$pos;
1988
        }
1989
    }
1990
1991
    echo html_buildlist($data,'idx','media_nstree_item','media_nstree_li');
1992
}
1993
1994
/**
1995
 * Userfunction for html_buildlist
1996
 *
1997
 * Prints a media namespace tree item
1998
 *
1999
 * @author Andreas Gohr <[email protected]>
2000
 *
2001
 * @param array $item
2002
 * @return string html
2003
 */
2004
function media_nstree_item($item){
2005
    global $INPUT;
2006
    $pos   = strrpos($item['id'], ':');
2007
    $label = substr($item['id'], $pos > 0 ? $pos + 1 : 0);
2008
    if(empty($item['label'])) $item['label'] = $label;
2009
2010
    $ret  = '';
2011
    if (!($INPUT->str('do') == 'media'))
2012
    $ret .= '<a href="'.DOKU_BASE.'lib/exe/mediamanager.php?ns='.idfilter($item['id']).'" class="idx_dir">';
2013
    else $ret .= '<a href="'.media_managerURL(array('ns' => idfilter($item['id'], false), 'tab_files' => 'files'))
2014
        .'" class="idx_dir">';
2015
    $ret .= $item['label'];
2016
    $ret .= '</a>';
2017
    return $ret;
2018
}
2019
2020
/**
2021
 * Userfunction for html_buildlist
2022
 *
2023
 * Prints a media namespace tree item opener
2024
 *
2025
 * @author Andreas Gohr <[email protected]>
2026
 *
2027
 * @param array $item
2028
 * @return string html
2029
 */
2030
function media_nstree_li($item){
2031
    $class='media level'.$item['level'];
2032
    if($item['open']){
2033
        $class .= ' open';
2034
        $img   = DOKU_BASE.'lib/images/minus.gif';
2035
        $alt   = '−';
2036
    }else{
2037
        $class .= ' closed';
2038
        $img   = DOKU_BASE.'lib/images/plus.gif';
2039
        $alt   = '+';
2040
    }
2041
    // TODO: only deliver an image if it actually has a subtree...
2042
    return '<li class="'.$class.'">'.
2043
        '<img src="'.$img.'" alt="'.$alt.'" />';
2044
}
2045
2046
/**
2047
 * Resizes the given image to the given size
2048
 *
2049
 * @author  Andreas Gohr <[email protected]>
2050
 *
2051
 * @param string $file filename, path to file
2052
 * @param string $ext  extension
2053
 * @param int    $w    desired width
2054
 * @param int    $h    desired height
2055
 * @return string path to resized or original size if failed
2056
 */
2057
function media_resize_image($file, $ext, $w, $h=0){
2058
    global $conf;
2059
2060
    $info = @getimagesize($file); //get original size
2061
    if($info == false) return $file; // that's no image - it's a spaceship!
2062
2063
    if(!$h) $h = round(($w * $info[1]) / $info[0]);
2064
    if(!$w) $w = round(($h * $info[0]) / $info[1]);
2065
2066
    // we wont scale up to infinity
2067
    if($w > 2000 || $h > 2000) return $file;
2068
2069
    // resize necessary? - (w,h) = native dimensions
2070
    if(($w == $info[0]) && ($h == $info[1])) return $file;
2071
2072
    //cache
2073
    $local = getCacheName($file,'.media.'.$w.'x'.$h.'.'.$ext);
2074
    $mtime = @filemtime($local); // 0 if not exists
2075
2076
    if($mtime > filemtime($file) ||
2077
        media_resize_imageIM($ext, $file, $info[0], $info[1], $local, $w, $h) ||
2078
        media_resize_imageGD($ext, $file, $info[0], $info[1], $local, $w, $h)
2079
    ) {
2080
        if($conf['fperm']) @chmod($local, $conf['fperm']);
2081
        return $local;
2082
    }
2083
    //still here? resizing failed
2084
    return $file;
2085
}
2086
2087
/**
2088
 * Crops the given image to the wanted ratio, then calls media_resize_image to scale it
2089
 * to the wanted size
2090
 *
2091
 * Crops are centered horizontally but prefer the upper third of an vertical
2092
 * image because most pics are more interesting in that area (rule of thirds)
2093
 *
2094
 * @author  Andreas Gohr <[email protected]>
2095
 *
2096
 * @param string $file filename, path to file
2097
 * @param string $ext  extension
2098
 * @param int    $w    desired width
2099
 * @param int    $h    desired height
2100
 * @return string path to resized or original size if failed
2101
 */
2102
function media_crop_image($file, $ext, $w, $h=0){
2103
    global $conf;
2104
2105
    if(!$h) $h = $w;
2106
    $info = @getimagesize($file); //get original size
2107
    if($info == false) return $file; // that's no image - it's a spaceship!
2108
2109
    // calculate crop size
2110
    $fr = $info[0]/$info[1];
2111
    $tr = $w/$h;
2112
2113
    // check if the crop can be handled completely by resize,
2114
    // i.e. the specified width & height match the aspect ratio of the source image
2115
    if ($w == round($h*$fr)) {
2116
        return media_resize_image($file, $ext, $w);
2117
    }
2118
2119
    if($tr >= 1){
2120
        if($tr > $fr){
2121
            $cw = $info[0];
2122
            $ch = (int) ($info[0]/$tr);
2123
        }else{
2124
            $cw = (int) ($info[1]*$tr);
2125
            $ch = $info[1];
2126
        }
2127
    }else{
2128
        if($tr < $fr){
2129
            $cw = (int) ($info[1]*$tr);
2130
            $ch = $info[1];
2131
        }else{
2132
            $cw = $info[0];
2133
            $ch = (int) ($info[0]/$tr);
2134
        }
2135
    }
2136
    // calculate crop offset
2137
    $cx = (int) (($info[0]-$cw)/2);
2138
    $cy = (int) (($info[1]-$ch)/3);
2139
2140
    //cache
2141
    $local = getCacheName($file,'.media.'.$cw.'x'.$ch.'.crop.'.$ext);
2142
    $mtime = @filemtime($local); // 0 if not exists
2143
2144
    if( $mtime > @filemtime($file) ||
2145
            media_crop_imageIM($ext,$file,$info[0],$info[1],$local,$cw,$ch,$cx,$cy) ||
2146
            media_resize_imageGD($ext,$file,$cw,$ch,$local,$cw,$ch,$cx,$cy) ){
2147
        if($conf['fperm']) @chmod($local, $conf['fperm']);
2148
        return media_resize_image($local,$ext, $w, $h);
2149
    }
2150
2151
    //still here? cropping failed
2152
    return media_resize_image($file,$ext, $w, $h);
2153
}
2154
2155
/**
2156
 * Calculate a token to be used to verify fetch requests for resized or
2157
 * cropped images have been internally generated - and prevent external
2158
 * DDOS attacks via fetch
2159
 *
2160
 * @author Christopher Smith <[email protected]>
2161
 *
2162
 * @param string  $id    id of the image
2163
 * @param int     $w     resize/crop width
2164
 * @param int     $h     resize/crop height
2165
 * @return string token or empty string if no token required
2166
 */
2167
function media_get_token($id,$w,$h){
2168
    // token is only required for modified images
2169
    if ($w || $h || media_isexternal($id)) {
2170
        $token = $id;
2171
        if ($w) $token .= '.'.$w;
2172
        if ($h) $token .= '.'.$h;
2173
2174
        return substr(\dokuwiki\PassHash::hmac('md5', $token, auth_cookiesalt()),0,6);
2175
    }
2176
2177
    return '';
2178
}
2179
2180
/**
2181
 * Download a remote file and return local filename
2182
 *
2183
 * returns false if download fails. Uses cached file if available and
2184
 * wanted
2185
 *
2186
 * @author  Andreas Gohr <[email protected]>
2187
 * @author  Pavel Vitis <[email protected]>
2188
 *
2189
 * @param string $url
2190
 * @param string $ext   extension
2191
 * @param int    $cache cachetime in seconds
2192
 * @return false|string path to cached file
2193
 */
2194
function media_get_from_URL($url,$ext,$cache){
2195
    global $conf;
2196
2197
    // if no cache or fetchsize just redirect
2198
    if ($cache==0)           return false;
2199
    if (!$conf['fetchsize']) return false;
2200
2201
    $local = getCacheName(strtolower($url),".media.$ext");
2202
    $mtime = @filemtime($local); // 0 if not exists
2203
2204
    //decide if download needed:
2205
    if(($mtime == 0) || // cache does not exist
2206
        ($cache != -1 && $mtime < time() - $cache) // 'recache' and cache has expired
2207
    ) {
2208
        if(media_image_download($url, $local)) {
2209
            return $local;
2210
        } else {
2211
            return false;
2212
        }
2213
    }
2214
2215
    //if cache exists use it else
2216
    if($mtime) return $local;
2217
2218
    //else return false
2219
    return false;
2220
}
2221
2222
/**
2223
 * Download image files
2224
 *
2225
 * @author Andreas Gohr <[email protected]>
2226
 *
2227
 * @param string $url
2228
 * @param string $file path to file in which to put the downloaded content
2229
 * @return bool
2230
 */
2231
function media_image_download($url,$file){
2232
    global $conf;
2233
    $http = new DokuHTTPClient();
2234
    $http->keep_alive = false; // we do single ops here, no need for keep-alive
2235
2236
    $http->max_bodysize = $conf['fetchsize'];
2237
    $http->timeout = 25; //max. 25 sec
2238
    $http->header_regexp = '!\r\nContent-Type: image/(jpe?g|gif|png)!i';
2239
2240
    $data = $http->get($url);
2241
    if(!$data) return false;
2242
2243
    $fileexists = file_exists($file);
2244
    $fp = @fopen($file,"w");
2245
    if(!$fp) return false;
2246
    fwrite($fp,$data);
2247
    fclose($fp);
2248
    if(!$fileexists and $conf['fperm']) chmod($file, $conf['fperm']);
2249
2250
    // check if it is really an image
2251
    $info = @getimagesize($file);
2252
    if(!$info){
2253
        @unlink($file);
2254
        return false;
2255
    }
2256
2257
    return true;
2258
}
2259
2260
/**
2261
 * resize images using external ImageMagick convert program
2262
 *
2263
 * @author Pavel Vitis <[email protected]>
2264
 * @author Andreas Gohr <[email protected]>
2265
 *
2266
 * @param string $ext     extension
2267
 * @param string $from    filename path to file
2268
 * @param int    $from_w  original width
2269
 * @param int    $from_h  original height
2270
 * @param string $to      path to resized file
2271
 * @param int    $to_w    desired width
2272
 * @param int    $to_h    desired height
2273
 * @return bool
2274
 */
2275
function media_resize_imageIM($ext,$from,$from_w,$from_h,$to,$to_w,$to_h){
2276
    global $conf;
2277
2278
    // check if convert is configured
2279
    if(!$conf['im_convert']) return false;
2280
2281
    // prepare command
2282
    $cmd  = $conf['im_convert'];
2283
    $cmd .= ' -resize '.$to_w.'x'.$to_h.'!';
2284
    if ($ext == 'jpg' || $ext == 'jpeg') {
2285
        $cmd .= ' -quality '.$conf['jpg_quality'];
2286
    }
2287
    $cmd .= " $from $to";
2288
2289
    @exec($cmd,$out,$retval);
2290
    if ($retval == 0) return true;
2291
    return false;
2292
}
2293
2294
/**
2295
 * crop images using external ImageMagick convert program
2296
 *
2297
 * @author Andreas Gohr <[email protected]>
2298
 *
2299
 * @param string $ext     extension
2300
 * @param string $from    filename path to file
2301
 * @param int    $from_w  original width
2302
 * @param int    $from_h  original height
2303
 * @param string $to      path to resized file
2304
 * @param int    $to_w    desired width
2305
 * @param int    $to_h    desired height
2306
 * @param int    $ofs_x   offset of crop centre
2307
 * @param int    $ofs_y   offset of crop centre
2308
 * @return bool
2309
 */
2310
function media_crop_imageIM($ext,$from,$from_w,$from_h,$to,$to_w,$to_h,$ofs_x,$ofs_y){
2311
    global $conf;
2312
2313
    // check if convert is configured
2314
    if(!$conf['im_convert']) return false;
2315
2316
    // prepare command
2317
    $cmd  = $conf['im_convert'];
2318
    $cmd .= ' -crop '.$to_w.'x'.$to_h.'+'.$ofs_x.'+'.$ofs_y;
2319
    if ($ext == 'jpg' || $ext == 'jpeg') {
2320
        $cmd .= ' -quality '.$conf['jpg_quality'];
2321
    }
2322
    $cmd .= " $from $to";
2323
2324
    @exec($cmd,$out,$retval);
2325
    if ($retval == 0) return true;
2326
    return false;
2327
}
2328
2329
/**
2330
 * resize or crop images using PHP's libGD support
2331
 *
2332
 * @author Andreas Gohr <[email protected]>
2333
 * @author Sebastian Wienecke <[email protected]>
2334
 *
2335
 * @param string $ext     extension
2336
 * @param string $from    filename path to file
2337
 * @param int    $from_w  original width
2338
 * @param int    $from_h  original height
2339
 * @param string $to      path to resized file
2340
 * @param int    $to_w    desired width
2341
 * @param int    $to_h    desired height
2342
 * @param int    $ofs_x   offset of crop centre
2343
 * @param int    $ofs_y   offset of crop centre
2344
 * @return bool
2345
 */
2346
function media_resize_imageGD($ext,$from,$from_w,$from_h,$to,$to_w,$to_h,$ofs_x=0,$ofs_y=0){
2347
    global $conf;
2348
2349
    if($conf['gdlib'] < 1) return false; //no GDlib available or wanted
2350
2351
    // check available memory
2352
    if(!is_mem_available(($from_w * $from_h * 4) + ($to_w * $to_h * 4))){
2353
        return false;
2354
    }
2355
2356
    // create an image of the given filetype
2357
    $image = false;
2358
    if ($ext == 'jpg' || $ext == 'jpeg'){
2359
        if(!function_exists("imagecreatefromjpeg")) return false;
2360
        $image = @imagecreatefromjpeg($from);
2361
    }elseif($ext == 'png') {
2362
        if(!function_exists("imagecreatefrompng")) return false;
2363
        $image = @imagecreatefrompng($from);
2364
2365
    }elseif($ext == 'gif') {
2366
        if(!function_exists("imagecreatefromgif")) return false;
2367
        $image = @imagecreatefromgif($from);
2368
    }
2369
    if(!$image) return false;
2370
2371
    $newimg = false;
2372
    if(($conf['gdlib']>1) && function_exists("imagecreatetruecolor") && $ext != 'gif'){
2373
        $newimg = @imagecreatetruecolor ($to_w, $to_h);
2374
    }
2375
    if(!$newimg) $newimg = @imagecreate($to_w, $to_h);
2376
    if(!$newimg){
2377
        imagedestroy($image);
2378
        return false;
2379
    }
2380
2381
    //keep png alpha channel if possible
2382
    if($ext == 'png' && $conf['gdlib']>1 && function_exists('imagesavealpha')){
2383
        imagealphablending($newimg, false);
2384
        imagesavealpha($newimg,true);
2385
    }
2386
2387
    //keep gif transparent color if possible
2388
    if($ext == 'gif' && function_exists('imagefill') && function_exists('imagecolorallocate')) {
2389
        if(function_exists('imagecolorsforindex') && function_exists('imagecolortransparent')) {
2390
            $transcolorindex = @imagecolortransparent($image);
2391
            if($transcolorindex >= 0 ) { //transparent color exists
2392
                $transcolor = @imagecolorsforindex($image, $transcolorindex);
2393
                $transcolorindex = @imagecolorallocate(
2394
                    $newimg,
2395
                    $transcolor['red'],
2396
                    $transcolor['green'],
2397
                    $transcolor['blue']
2398
                );
2399
                @imagefill($newimg, 0, 0, $transcolorindex);
2400
                @imagecolortransparent($newimg, $transcolorindex);
2401
            }else{ //filling with white
2402
                $whitecolorindex = @imagecolorallocate($newimg, 255, 255, 255);
2403
                @imagefill($newimg, 0, 0, $whitecolorindex);
2404
            }
2405
        }else{ //filling with white
2406
            $whitecolorindex = @imagecolorallocate($newimg, 255, 255, 255);
2407
            @imagefill($newimg, 0, 0, $whitecolorindex);
2408
        }
2409
    }
2410
2411
    //try resampling first
2412
    if(function_exists("imagecopyresampled")){
2413
        if(!@imagecopyresampled($newimg, $image, 0, 0, $ofs_x, $ofs_y, $to_w, $to_h, $from_w, $from_h)) {
2414
            imagecopyresized($newimg, $image, 0, 0, $ofs_x, $ofs_y, $to_w, $to_h, $from_w, $from_h);
2415
        }
2416
    }else{
2417
        imagecopyresized($newimg, $image, 0, 0, $ofs_x, $ofs_y, $to_w, $to_h, $from_w, $from_h);
2418
    }
2419
2420
    $okay = false;
2421
    if ($ext == 'jpg' || $ext == 'jpeg'){
2422
        if(!function_exists('imagejpeg')){
2423
            $okay = false;
2424
        }else{
2425
            $okay = imagejpeg($newimg, $to, $conf['jpg_quality']);
2426
        }
2427
    }elseif($ext == 'png') {
2428
        if(!function_exists('imagepng')){
2429
            $okay = false;
2430
        }else{
2431
            $okay =  imagepng($newimg, $to);
2432
        }
2433
    }elseif($ext == 'gif') {
2434
        if(!function_exists('imagegif')){
2435
            $okay = false;
2436
        }else{
2437
            $okay = imagegif($newimg, $to);
2438
        }
2439
    }
2440
2441
    // destroy GD image ressources
2442
    if($image) imagedestroy($image);
2443
    if($newimg) imagedestroy($newimg);
2444
2445
    return $okay;
2446
}
2447
2448
/**
2449
 * Return other media files with the same base name
2450
 * but different extensions.
2451
 *
2452
 * @param string   $src     - ID of media file
2453
 * @param string[] $exts    - alternative extensions to find other files for
2454
 * @return array            - array(mime type => file ID)
2455
 *
2456
 * @author Anika Henke <[email protected]>
2457
 */
2458
function media_alternativefiles($src, $exts){
2459
2460
    $files = array();
2461
    list($srcExt, /* $srcMime */) = mimetype($src);
2462
    $filebase = substr($src, 0, -1 * (strlen($srcExt)+1));
2463
2464
    foreach($exts as $ext) {
2465
        $fileid = $filebase.'.'.$ext;
2466
        $file = mediaFN($fileid);
2467
        if(file_exists($file)) {
2468
            list(/* $fileExt */, $fileMime) = mimetype($file);
2469
            $files[$fileMime] = $fileid;
2470
        }
2471
    }
2472
    return $files;
2473
}
2474
2475
/**
2476
 * Check if video/audio is supported to be embedded.
2477
 *
2478
 * @param string $mime      - mimetype of media file
2479
 * @param string $type      - type of media files to check ('video', 'audio', or null for all)
2480
 * @return boolean
2481
 *
2482
 * @author Anika Henke <[email protected]>
2483
 */
2484
function media_supportedav($mime, $type=NULL){
2485
    $supportedAudio = array(
2486
        'ogg' => 'audio/ogg',
2487
        'mp3' => 'audio/mpeg',
2488
        'wav' => 'audio/wav',
2489
    );
2490
    $supportedVideo = array(
2491
        'webm' => 'video/webm',
2492
        'ogv' => 'video/ogg',
2493
        'mp4' => 'video/mp4',
2494
    );
2495
    if ($type == 'audio') {
2496
        $supportedAv = $supportedAudio;
2497
    } elseif ($type == 'video') {
2498
        $supportedAv = $supportedVideo;
2499
    } else {
2500
        $supportedAv = array_merge($supportedAudio, $supportedVideo);
2501
    }
2502
    return in_array($mime, $supportedAv);
2503
}
2504
2505
/**
2506
 * Return track media files with the same base name
2507
 * but extensions that indicate kind and lang.
2508
 * ie for foo.webm search foo.sub.lang.vtt, foo.cap.lang.vtt...
2509
 *
2510
 * @param string   $src     - ID of media file
2511
 * @return array            - array(mediaID => array( kind, srclang ))
2512
 *
2513
 * @author Schplurtz le Déboulonné <[email protected]>
2514
 */
2515
function media_trackfiles($src){
2516
    $kinds=array(
2517
        'sub' => 'subtitles',
2518
        'cap' => 'captions',
2519
        'des' => 'descriptions',
2520
        'cha' => 'chapters',
2521
        'met' => 'metadata'
2522
    );
2523
2524
    $files = array();
2525
    $re='/\\.(sub|cap|des|cha|met)\\.([^.]+)\\.vtt$/';
2526
    $baseid=pathinfo($src, PATHINFO_FILENAME);
2527
    $pattern=mediaFN($baseid).'.*.*.vtt';
2528
    $list=glob($pattern);
2529
    foreach($list as $track) {
2530
        if(preg_match($re, $track, $matches)){
2531
            $files[$baseid.'.'.$matches[1].'.'.$matches[2].'.vtt']=array(
2532
                $kinds[$matches[1]],
2533
                $matches[2],
2534
            );
2535
        }
2536
    }
2537
    return $files;
2538
}
2539
2540
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
2541