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