Failed Conditions
Pull Request — master (#3361)
by
unknown
03:07
created

media.php ➔ media_diff()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 4
dl 0
loc 3
rs 10
c 0
b 0
f 0
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;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, MediaChangeLog.

Let’s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let’s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
10
use dokuwiki\HTTP\DokuHTTPClient;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, DokuHTTPClient.

Let’s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let’s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
11
use dokuwiki\Subscriptions\MediaSubscriptionSender;
12
use dokuwiki\Extension\Event;
13
use dokuwiki\Form\Form;
14
use dokuwiki\Utf8\Sort;
15
16
/**
17
 * Lists pages which currently use a media file selected for deletion
18
 *
19
 * References uses the same visual as search results and share
20
 * their CSS tags except pagenames won't be links.
21
 *
22
 * @author Matthias Grimm <[email protected]>
23
 *
24
 * @param array $data
25
 * @param string $id
26
 */
27
function media_filesinuse($data,$id){
28
    global $lang;
29
    echo '<h1>'.$lang['reference'].' <code>'.hsc(noNS($id)).'</code></h1>';
30
    echo '<p>'.hsc($lang['ref_inuse']).'</p>';
31
32
    $hidden=0; //count of hits without read permission
33
    foreach($data as $row){
34
        if(auth_quickaclcheck($row) >= AUTH_READ && isVisiblePage($row)){
35
            echo '<div class="search_result">';
36
            echo '<span class="mediaref_ref">'.hsc($row).'</span>';
37
            echo '</div>';
38
        }else
39
            $hidden++;
40
    }
41
    if ($hidden){
42
        print '<div class="mediaref_hidden">'.$lang['ref_hidden'].'</div>';
43
    }
44
}
45
46
/**
47
 * Handles the saving of image meta data
48
 *
49
 * @author Andreas Gohr <[email protected]>
50
 * @author Kate Arzamastseva <[email protected]>
51
 *
52
 * @param string $id media id
53
 * @param int $auth permission level
54
 * @param array $data
55
 * @return false|string
56
 */
57
function media_metasave($id,$auth,$data){
58
    if($auth < AUTH_UPLOAD) return false;
59
    if(!checkSecurityToken()) return false;
60
    global $lang;
61
    global $conf;
62
    $src = mediaFN($id);
63
64
    $meta = new JpegMeta($src);
65
    $meta->_parseAll();
66
67
    foreach($data as $key => $val){
68
        $val=trim($val);
69
        if(empty($val)){
70
            $meta->deleteField($key);
71
        }else{
72
            $meta->setField($key,$val);
73
        }
74
    }
75
76
    $old = @filemtime($src);
77
    if(!file_exists(mediaFN($id, $old)) && file_exists($src)) {
78
        // add old revision to the attic
79
        media_saveOldRevision($id);
80
    }
81
    $filesize_old = filesize($src);
82
    if($meta->save()){
83
        if($conf['fperm']) chmod($src, $conf['fperm']);
84
        @clearstatcache(true, $src);
85
        $new = @filemtime($src);
86
        $filesize_new = filesize($src);
87
        $sizechange = $filesize_new - $filesize_old;
88
89
        // add a log entry to the media changelog
90
        addMediaLogEntry($new, $id, DOKU_CHANGE_TYPE_EDIT, $lang['media_meta_edited'], '', null, $sizechange);
91
92
        msg($lang['metasaveok'],1);
93
        return $id;
94
    }else{
95
        msg($lang['metasaveerr'],-1);
96
        return false;
97
    }
98
}
99
100
/**
101
 * check if a media is external source
102
 *
103
 * @author Gerrit Uitslag <[email protected]>
104
 *
105
 * @param string $id the media ID or URL
106
 * @return bool
107
 */
108
function media_isexternal($id){
109
    if (preg_match('#^(?:https?|ftp)://#i', $id)) return true;
110
    return false;
111
}
112
113
/**
114
 * Check if a media item is public (eg, external URL or readable by @ALL)
115
 *
116
 * @author Andreas Gohr <[email protected]>
117
 *
118
 * @param string $id  the media ID or URL
119
 * @return bool
120
 */
121
function media_ispublic($id){
122
    if(media_isexternal($id)) return true;
123
    $id = cleanID($id);
124
    if(auth_aclcheck(getNS($id).':*', '', array()) >= AUTH_READ) return true;
125
    return false;
126
}
127
128
/**
129
 * Display the form to edit image meta data
130
 *
131
 * @author Andreas Gohr <[email protected]>
132
 * @author Kate Arzamastseva <[email protected]>
133
 *
134
 * @param string $id media id
135
 * @param int $auth permission level
136
 * @return bool
137
 */
138
function media_metaform($id, $auth) {
139
    global $lang;
140
141
    if ($auth < AUTH_UPLOAD) {
142
        echo '<div class="nothing">'.$lang['media_perm_upload'].'</div>'.DOKU_LF;
143
        return false;
144
    }
145
146
    // load the field descriptions
147
    static $fields = null;
148
    if ($fields === null) {
149
        $config_files = getConfigFiles('mediameta');
150
        foreach ($config_files as $config_file) {
151
            if (file_exists($config_file)) include($config_file);
152
        }
153
    }
154
155
    $src = mediaFN($id);
156
157
    // output
158
    $form = new Form([
159
            'action' => media_managerURL(['tab_details' => 'view'], '&'),
160
            'class' => 'meta'
161
    ]);
162
    $form->addTagOpen('div')->addClass('no');
163
    $form->setHiddenField('img', $id);
164
    $form->setHiddenField('mediado', 'save');
165
    foreach ($fields as $key => $field) {
0 ignored issues
show
Bug introduced by
The expression $fields of type null is not traversable.
Loading history...
166
        // get current value
167
        if (empty($field[0])) continue;
168
        $tags = array($field[0]);
169
        if (is_array($field[3])) $tags = array_merge($tags, $field[3]);
170
        $value = tpl_img_getTag($tags, '', $src);
171
        $value = cleanText($value);
172
173
        // prepare attributes
174
        $p = array(
175
            'class' => 'edit',
176
            'id'    => 'meta__'.$key,
177
            'name'  => 'meta['.$field[0].']',
178
        );
179
180
        $form->addTagOpen('div')->addClass('row');
181
        if ($field[2] == 'text') {
182
            $form->addTextInput(
183
                $p['name'],
184
                ($lang[$field[1]] ? $lang[$field[1]] : $field[1] . ':')
185
            )->id($p['id'])->addClass($p['class'])->val($value);
186
        } else {
187
            $form->addTextarea($p['name'], $lang[$field[1]])->id($p['id'])
188
                ->val(formText($value))
189
                ->addClass($p['class'])
190
                ->attr('rows', '6')->attr('cols', '50');
191
        }
192
        $form->addTagClose('div');
193
    }
194
    $form->addTagOpen('div')->addClass('buttons');
195
    $form->addButton('mediado[save]', $lang['btn_save'])->attr('type', 'submit')
196
        ->attrs(['accesskey' => 's']);
197
    $form->addTagClose('div');
198
199
    $form->addTagClose('div');
200
    echo $form->toHTML();
201
    return true;
202
}
203
204
/**
205
 * Convenience function to check if a media file is still in use
206
 *
207
 * @author Michael Klier <[email protected]>
208
 *
209
 * @param string $id media id
210
 * @return array|bool
211
 */
212
function media_inuse($id) {
213
    global $conf;
214
215
    if($conf['refcheck']){
216
        $mediareferences = ft_mediause($id,true);
217
        if(!count($mediareferences)) {
218
            return false;
219
        } else {
220
            return $mediareferences;
221
        }
222
    } else {
223
        return false;
224
    }
225
}
226
227
/**
228
 * Handles media file deletions
229
 *
230
 * If configured, checks for media references before deletion
231
 *
232
 * @author             Andreas Gohr <[email protected]>
233
 *
234
 * @param string $id media id
235
 * @param int $auth no longer used
236
 * @return int One of: 0,
237
 *                     DOKU_MEDIA_DELETED,
238
 *                     DOKU_MEDIA_DELETED | DOKU_MEDIA_EMPTY_NS,
239
 *                     DOKU_MEDIA_NOT_AUTH,
240
 *                     DOKU_MEDIA_INUSE
241
 */
242
function media_delete($id,$auth){
0 ignored issues
show
Unused Code introduced by
The parameter $auth is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
243
    global $lang;
244
    $auth = auth_quickaclcheck(ltrim(getNS($id).':*', ':'));
245
    if($auth < AUTH_DELETE) return DOKU_MEDIA_NOT_AUTH;
246
    if(media_inuse($id)) return DOKU_MEDIA_INUSE;
247
248
    $file = mediaFN($id);
249
250
    // trigger an event - MEDIA_DELETE_FILE
251
    $data = array();
252
    $data['id']   = $id;
253
    $data['name'] = \dokuwiki\Utf8\PhpString::basename($file);
254
    $data['path'] = $file;
255
    $data['size'] = (file_exists($file)) ? filesize($file) : 0;
256
257
    $data['unl'] = false;
258
    $data['del'] = false;
259
    $evt = new Event('MEDIA_DELETE_FILE',$data);
260
    if ($evt->advise_before()) {
261
        $old = @filemtime($file);
262
        if(!file_exists(mediaFN($id, $old)) && file_exists($file)) {
263
            // add old revision to the attic
264
            media_saveOldRevision($id);
265
        }
266
267
        $data['unl'] = @unlink($file);
268
        if($data['unl']) {
269
            $sizechange = 0 - $data['size'];
270
            addMediaLogEntry(time(), $id, DOKU_CHANGE_TYPE_DELETE, $lang['deleted'], '', null, $sizechange);
271
272
            $data['del'] = io_sweepNS($id, 'mediadir');
273
        }
274
    }
275
    $evt->advise_after();
276
    unset($evt);
277
278
    if($data['unl'] && $data['del']){
279
        return DOKU_MEDIA_DELETED | DOKU_MEDIA_EMPTY_NS;
280
    }
281
282
    return $data['unl'] ? DOKU_MEDIA_DELETED : 0;
283
}
284
285
/**
286
 * Handle file uploads via XMLHttpRequest
287
 *
288
 * @param string $ns   target namespace
289
 * @param int    $auth current auth check result
290
 * @return false|string false on error, id of the new file on success
291
 */
292
function media_upload_xhr($ns,$auth){
293
    if(!checkSecurityToken()) return false;
294
    global $INPUT;
295
296
    $id = $INPUT->get->str('qqfile');
297
    list($ext,$mime) = mimetype($id);
298
    $input = fopen("php://input", "r");
299
    if (!($tmp = io_mktmpdir())) return false;
300
    $path = $tmp.'/'.md5($id);
301
    $target = fopen($path, "w");
302
    $realSize = stream_copy_to_stream($input, $target);
303
    fclose($target);
304
    fclose($input);
305
    if (isset($_SERVER["CONTENT_LENGTH"]) && ($realSize != (int)$_SERVER["CONTENT_LENGTH"])){
306
        unlink($path);
307
        return false;
308
    }
309
310
    $res = media_save(
311
        array('name' => $path,
312
            'mime' => $mime,
313
            'ext'  => $ext),
314
        $ns.':'.$id,
315
        (($INPUT->get->str('ow') == 'true') ? true : false),
316
        $auth,
317
        'copy'
318
    );
319
    unlink($path);
320
    if ($tmp) io_rmdir($tmp, true);
321
    if (is_array($res)) {
322
        msg($res[0], $res[1]);
323
        return false;
324
    }
325
    return $res;
326
}
327
328
/**
329
 * Handles media file uploads
330
 *
331
 * @author Andreas Gohr <[email protected]>
332
 * @author Michael Klier <[email protected]>
333
 *
334
 * @param string     $ns    target namespace
335
 * @param int        $auth  current auth check result
336
 * @param bool|array $file  $_FILES member, $_FILES['upload'] if false
337
 * @return false|string false on error, id of the new file on success
338
 */
339
function media_upload($ns,$auth,$file=false){
340
    if(!checkSecurityToken()) return false;
341
    global $lang;
342
    global $INPUT;
343
344
    // get file and id
345
    $id   = $INPUT->post->str('mediaid');
346
    if (!$file) $file = $_FILES['upload'];
347
    if(empty($id)) $id = $file['name'];
348
349
    // check for errors (messages are done in lib/exe/mediamanager.php)
350
    if($file['error']) return false;
351
352
    // check extensions
353
    list($fext,$fmime) = mimetype($file['name']);
354
    list($iext,$imime) = mimetype($id);
355
    if($fext && !$iext){
356
        // no extension specified in id - read original one
357
        $id   .= '.'.$fext;
358
        $imime = $fmime;
359
    }elseif($fext && $fext != $iext){
360
        // extension was changed, print warning
361
        msg(sprintf($lang['mediaextchange'],$fext,$iext));
362
    }
363
364
    $res = media_save(array('name' => $file['tmp_name'],
365
                            'mime' => $imime,
366
                            'ext'  => $iext), $ns.':'.$id,
367
                      $INPUT->post->bool('ow'), $auth, 'copy_uploaded_file');
368
    if (is_array($res)) {
369
        msg($res[0], $res[1]);
370
        return false;
371
    }
372
    return $res;
373
}
374
375
/**
376
 * An alternative to move_uploaded_file that copies
377
 *
378
 * Using copy, makes sure any setgid bits on the media directory are honored
379
 *
380
 * @see   move_uploaded_file()
381
 *
382
 * @param string $from
383
 * @param string $to
384
 * @return bool
385
 */
386
function copy_uploaded_file($from, $to){
387
    if(!is_uploaded_file($from)) return false;
388
    $ok = copy($from, $to);
389
    @unlink($from);
390
    return $ok;
391
}
392
393
/**
394
 * This generates an action event and delegates to _media_upload_action().
395
 * Action plugins are allowed to pre/postprocess the uploaded file.
396
 * (The triggered event is preventable.)
397
 *
398
 * Event data:
399
 * $data[0]     fn_tmp:    the temporary file name (read from $_FILES)
400
 * $data[1]     fn:        the file name of the uploaded file
401
 * $data[2]     id:        the future directory id of the uploaded file
402
 * $data[3]     imime:     the mimetype of the uploaded file
403
 * $data[4]     overwrite: if an existing file is going to be overwritten
404
 * $data[5]     move:      name of function that performs move/copy/..
405
 *
406
 * @triggers MEDIA_UPLOAD_FINISH
407
 *
408
 * @param array  $file
409
 * @param string $id   media id
410
 * @param bool   $ow   overwrite?
411
 * @param int    $auth permission level
412
 * @param string $move name of functions that performs move/copy/..
413
 * @return false|array|string
414
 */
415
function media_save($file, $id, $ow, $auth, $move) {
416
    if($auth < AUTH_UPLOAD) {
417
        return array("You don't have permissions to upload files.", -1);
418
    }
419
420
    if (!isset($file['mime']) || !isset($file['ext'])) {
421
        list($ext, $mime) = mimetype($id);
422
        if (!isset($file['mime'])) {
423
            $file['mime'] = $mime;
424
        }
425
        if (!isset($file['ext'])) {
426
            $file['ext'] = $ext;
427
        }
428
    }
429
430
    global $lang, $conf;
431
432
    // get filename
433
    $id   = cleanID($id);
434
    $fn   = mediaFN($id);
435
436
    // get filetype regexp
437
    $types = array_keys(getMimeTypes());
438
    $types = array_map(
439
        function ($q) {
440
            return preg_quote($q, "/");
441
        },
442
        $types
443
    );
444
    $regex = join('|',$types);
445
446
    // because a temp file was created already
447
    if(!preg_match('/\.('.$regex.')$/i',$fn)) {
448
        return array($lang['uploadwrong'],-1);
449
    }
450
451
    //check for overwrite
452
    $overwrite = file_exists($fn);
453
    $auth_ow = (($conf['mediarevisions']) ? AUTH_UPLOAD : AUTH_DELETE);
454
    if($overwrite && (!$ow || $auth < $auth_ow)) {
455
        return array($lang['uploadexist'], 0);
456
    }
457
    // check for valid content
458
    $ok = media_contentcheck($file['name'], $file['mime']);
459
    if($ok == -1){
460
        return array(sprintf($lang['uploadbadcontent'],'.' . $file['ext']),-1);
461
    }elseif($ok == -2){
462
        return array($lang['uploadspam'],-1);
463
    }elseif($ok == -3){
464
        return array($lang['uploadxss'],-1);
465
    }
466
467
    // prepare event data
468
    $data = array();
469
    $data[0] = $file['name'];
470
    $data[1] = $fn;
471
    $data[2] = $id;
472
    $data[3] = $file['mime'];
473
    $data[4] = $overwrite;
474
    $data[5] = $move;
475
476
    // trigger event
477
    return Event::createAndTrigger('MEDIA_UPLOAD_FINISH', $data, '_media_upload_action', true);
478
}
479
480
/**
481
 * Callback adapter for media_upload_finish() triggered by MEDIA_UPLOAD_FINISH
482
 *
483
 * @author Michael Klier <[email protected]>
484
 *
485
 * @param array $data event data
486
 * @return false|array|string
487
 */
488
function _media_upload_action($data) {
489
    // fixme do further sanity tests of given data?
490
    if(is_array($data) && count($data)===6) {
491
        return media_upload_finish($data[0], $data[1], $data[2], $data[3], $data[4], $data[5]);
492
    } else {
493
        return false; //callback error
494
    }
495
}
496
497
/**
498
 * Saves an uploaded media file
499
 *
500
 * @author Andreas Gohr <[email protected]>
501
 * @author Michael Klier <[email protected]>
502
 * @author Kate Arzamastseva <[email protected]>
503
 *
504
 * @param string $fn_tmp
505
 * @param string $fn
506
 * @param string $id        media id
507
 * @param string $imime     mime type
508
 * @param bool   $overwrite overwrite existing?
509
 * @param string $move      function name
510
 * @return array|string
511
 */
512
function media_upload_finish($fn_tmp, $fn, $id, $imime, $overwrite, $move = 'move_uploaded_file') {
513
    global $conf;
514
    global $lang;
515
    global $REV;
516
517
    $old = @filemtime($fn);
518
    if(!file_exists(mediaFN($id, $old)) && file_exists($fn)) {
519
        // add old revision to the attic if missing
520
        media_saveOldRevision($id);
521
    }
522
523
    // prepare directory
524
    io_createNamespace($id, 'media');
525
526
    $filesize_old = file_exists($fn) ? filesize($fn) : 0;
527
528
    if($move($fn_tmp, $fn)) {
529
        @clearstatcache(true,$fn);
530
        $new = @filemtime($fn);
531
        // Set the correct permission here.
532
        // Always chmod media because they may be saved with different permissions than expected from the php umask.
533
        // (Should normally chmod to $conf['fperm'] only if $conf['fperm'] is set.)
534
        chmod($fn, $conf['fmode']);
535
        msg($lang['uploadsucc'],1);
536
        media_notify($id,$fn,$imime,$old,$new);
537
        // add a log entry to the media changelog
538
        $filesize_new = filesize($fn);
539
        $sizechange = $filesize_new - $filesize_old;
540
        if($REV) {
541
            addMediaLogEntry(
542
                $new,
543
                $id,
544
                DOKU_CHANGE_TYPE_REVERT,
545
                sprintf($lang['restored'], dformat($REV)),
546
                $REV,
547
                null,
548
                $sizechange
549
            );
550
        } elseif($overwrite) {
551
            addMediaLogEntry($new, $id, DOKU_CHANGE_TYPE_EDIT, '', '', null, $sizechange);
552
        } else {
553
            addMediaLogEntry($new, $id, DOKU_CHANGE_TYPE_CREATE, $lang['created'], '', null, $sizechange);
554
        }
555
        return $id;
556
    }else{
557
        return array($lang['uploadfail'],-1);
558
    }
559
}
560
561
/**
562
 * Moves the current version of media file to the media_attic
563
 * directory
564
 *
565
 * @author Kate Arzamastseva <[email protected]>
566
 *
567
 * @param string $id
568
 * @return int - revision date
569
 */
570
function media_saveOldRevision($id){
571
    global $conf, $lang;
572
573
    $oldf = mediaFN($id);
574
    if(!file_exists($oldf)) return '';
575
    $date = filemtime($oldf);
576
    if (!$conf['mediarevisions']) return $date;
577
578
    $medialog = new MediaChangeLog($id);
579
    if (!$medialog->getRevisionInfo($date)) {
580
        // there was an external edit,
581
        // there is no log entry for current version of file
582
        $sizechange = filesize($oldf);
583
        if(!file_exists(mediaMetaFN($id, '.changes'))) {
584
            addMediaLogEntry($date, $id, DOKU_CHANGE_TYPE_CREATE, $lang['created'], '', null, $sizechange);
585
        } else {
586
            $oldRev = $medialog->getRevisions(-1, 1); // from changelog
587
            $oldRev = (int) (empty($oldRev) ? 0 : $oldRev[0]);
588
            $filesize_old = filesize(mediaFN($id, $oldRev));
589
            $sizechange = $sizechange - $filesize_old;
590
591
            addMediaLogEntry($date, $id, DOKU_CHANGE_TYPE_EDIT, '', '', null, $sizechange);
592
        }
593
    }
594
595
    $newf = mediaFN($id,$date);
596
    io_makeFileDir($newf);
597
    if(copy($oldf, $newf)) {
598
        // Set the correct permission here.
599
        // Always chmod media because they may be saved with different permissions than expected from the php umask.
600
        // (Should normally chmod to $conf['fperm'] only if $conf['fperm'] is set.)
601
        chmod($newf, $conf['fmode']);
602
    }
603
    return $date;
604
}
605
606
/**
607
 * This function checks if the uploaded content is really what the
608
 * mimetype says it is. We also do spam checking for text types here.
609
 *
610
 * We need to do this stuff because we can not rely on the browser
611
 * to do this check correctly. Yes, IE is broken as usual.
612
 *
613
 * @author Andreas Gohr <[email protected]>
614
 * @link   http://www.splitbrain.org/blog/2007-02/12-internet_explorer_facilitates_cross_site_scripting
615
 * @fixme  check all 26 magic IE filetypes here?
616
 *
617
 * @param string $file path to file
618
 * @param string $mime mimetype
619
 * @return int
620
 */
621
function media_contentcheck($file,$mime){
622
    global $conf;
623
    if($conf['iexssprotect']){
624
        $fh = @fopen($file, 'rb');
625
        if($fh){
626
            $bytes = fread($fh, 256);
627
            fclose($fh);
628
            if(preg_match('/<(script|a|img|html|body|iframe)[\s>]/i',$bytes)){
629
                return -3; //XSS: possibly malicious content
630
            }
631
        }
632
    }
633
    if(substr($mime,0,6) == 'image/'){
634
        $info = @getimagesize($file);
635
        if($mime == 'image/gif' && $info[2] != 1){
636
            return -1; // uploaded content did not match the file extension
637
        }elseif($mime == 'image/jpeg' && $info[2] != 2){
638
            return -1;
639
        }elseif($mime == 'image/png' && $info[2] != 3){
640
            return -1;
641
        }
642
        # fixme maybe check other images types as well
643
    }elseif(substr($mime,0,5) == 'text/'){
644
        global $TEXT;
645
        $TEXT = io_readFile($file);
646
        if(checkwordblock()){
647
            return -2; //blocked by the spam blacklist
648
        }
649
    }
650
    return 0;
651
}
652
653
/**
654
 * Send a notify mail on uploads
655
 *
656
 * @author Andreas Gohr <[email protected]>
657
 *
658
 * @param string   $id      media id
659
 * @param string   $file    path to file
660
 * @param string   $mime    mime type
661
 * @param bool|int $old_rev revision timestamp or false
662
 * @return bool
663
 */
664
function media_notify($id,$file,$mime,$old_rev=false,$current_rev=false){
0 ignored issues
show
Unused Code introduced by
The parameter $file is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $mime is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
665
    global $conf;
666
    if(empty($conf['notify'])) return false; //notify enabled?
667
668
    $subscription = new MediaSubscriptionSender();
669
    return $subscription->sendMediaDiff($conf['notify'], 'uploadmail', $id, $old_rev, $current_rev);
670
}
671
672
/**
673
 * List all files in a given Media namespace
674
 *
675
 * @param string      $ns             namespace
676
 * @param null|int    $auth           permission level
677
 * @param string      $jump           id
678
 * @param bool        $fullscreenview
679
 * @param bool|string $sort           sorting order, false skips sorting
680
 */
681
function media_filelist($ns,$auth=null,$jump='',$fullscreenview=false,$sort=false){
682
    global $conf;
683
    global $lang;
684
    $ns = cleanID($ns);
685
686
    // check auth our self if not given (needed for ajax calls)
687
    if(is_null($auth)) $auth = auth_quickaclcheck("$ns:*");
688
689
    if (!$fullscreenview) echo '<h1 id="media__ns">:'.hsc($ns).'</h1>'.NL;
690
691
    if($auth < AUTH_READ){
692
        // FIXME: print permission warning here instead?
693
        echo '<div class="nothing">'.$lang['nothingfound'].'</div>'.NL;
694
    }else{
695
        if (!$fullscreenview) {
696
            media_uploadform($ns, $auth);
697
            media_searchform($ns);
698
        }
699
700
        $dir = utf8_encodeFN(str_replace(':','/',$ns));
701
        $data = array();
702
        search($data,$conf['mediadir'],'search_media',
703
                array('showmsg'=>true,'depth'=>1),$dir,1,$sort);
704
705
        if(!count($data)){
706
            echo '<div class="nothing">'.$lang['nothingfound'].'</div>'.NL;
707
        }else {
708
            if ($fullscreenview) {
709
                echo '<ul class="' . _media_get_list_type() . '">';
710
            }
711
            foreach($data as $item){
712
                if (!$fullscreenview) {
713
                    media_printfile($item,$auth,$jump);
714
                } else {
715
                    media_printfile_thumbs($item,$auth,$jump);
716
                }
717
            }
718
            if ($fullscreenview) echo '</ul>'.NL;
719
        }
720
    }
721
}
722
723
/**
724
 * Prints tabs for files list actions
725
 *
726
 * @author Kate Arzamastseva <[email protected]>
727
 * @author Adrian Lang <[email protected]>
728
 *
729
 * @param string $selected_tab - opened tab
730
 */
731
732
function media_tabs_files($selected_tab = ''){
733
    global $lang;
734
    $tabs = array();
735
    foreach(array('files'  => 'mediaselect',
736
                  'upload' => 'media_uploadtab',
737
                  'search' => 'media_searchtab') as $tab => $caption) {
738
        $tabs[$tab] = array('href'    => media_managerURL(['tab_files' => $tab], '&'),
739
                            'caption' => $lang[$caption]);
740
    }
741
742
    html_tabs($tabs, $selected_tab);
743
}
744
745
/**
746
 * Prints tabs for files details actions
747
 *
748
 * @author Kate Arzamastseva <[email protected]>
749
 * @param string $image filename of the current image
750
 * @param string $selected_tab opened tab
751
 */
752
function media_tabs_details($image, $selected_tab = '') {
753
    global $lang, $conf;
754
755
    $tabs = array();
756
    $tabs['view'] = array('href'    => media_managerURL(['tab_details' => 'view'], '&'),
757
                          'caption' => $lang['media_viewtab']);
758
759
    list(, $mime) = mimetype($image);
760
    if ($mime == 'image/jpeg' && file_exists(mediaFN($image))) {
761
        $tabs['edit'] = array('href'    => media_managerURL(['tab_details' => 'edit'], '&'),
762
                              'caption' => $lang['media_edittab']);
763
    }
764
    if ($conf['mediarevisions']) {
765
        $tabs['history'] = array('href'    => media_managerURL(['tab_details' => 'history'], '&'),
766
                                 'caption' => $lang['media_historytab']);
767
    }
768
769
    html_tabs($tabs, $selected_tab);
770
}
771
772
/**
773
 * Prints options for the tab that displays a list of all files
774
 *
775
 * @author Kate Arzamastseva <[email protected]>
776
 */
777
function media_tab_files_options() {
778
    global $lang;
779
    global $INPUT;
780
    global $ID;
781
782
    $form = new Form([
783
            'method' => 'get',
784
            'action' => wl($ID),
785
            'class' => 'options'
786
    ]);
787
    $form->addTagOpen('div')->addClass('no');
788
    $form->setHiddenField('sectok', null);
789
    $media_manager_params = media_managerURL([], '', false, true);
790
    foreach ($media_manager_params as $pKey => $pVal) {
0 ignored issues
show
Bug introduced by
The expression $media_manager_params of type string|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
791
        $form->setHiddenField($pKey, $pVal);
792
    }
793
    if ($INPUT->has('q')) {
794
        $form->setHiddenField('q', $INPUT->str('q'));
795
    }
796
    $form->addHTML('<ul>'.NL);
797
    foreach (array('list' => array('listType', array('thumbs', 'rows')),
798
                  'sort' => array('sortBy', array('name', 'date')))
799
            as $group => $content) {
800
        $checked = "_media_get_${group}_type";
801
        $checked = $checked();
802
803
        $form->addHTML('<li class="'. $content[0] .'">');
804
        foreach ($content[1] as $option) {
805
            $attrs = array();
806
            if ($checked == $option) {
807
                $attrs['checked'] = 'checked';
808
            }
809
            $radio = $form->addRadioButton(
810
                $group.'_dwmedia',
811
                $lang['media_'.$group.'_'.$option]
812
            )->val($option)->id($content[0].'__'.$option)->addClass($option);
813
            $radio->attrs($attrs);
814
        }
815
        $form->addHTML('</li>'.NL);
816
    }
817
    $form->addHTML('<li>');
818
    $form->addButton('', $lang['btn_apply'])->attr('type', 'submit');
819
    $form->addHTML('</li>'.NL);
820
    $form->addHTML('</ul>'.NL);
821
    $form->addTagClose('div');
822
    print $form->toHTML();
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='') {
0 ignored issues
show
Unused Code introduced by
The parameter $jump is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
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
            (new dokuwiki\Ui\MediaDiff($image))->show(); //media_diff($image, $ns, $auth);
993
        } else {
994
            $first = $INPUT->int('first');
995
            (new dokuwiki\Ui\MediaRevisions($image))->show($first);
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) {
0 ignored issues
show
Unused Code introduced by
The parameter $auth is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1013
1014
    $size = media_image_preview_size($image, $rev, $meta);
1015
1016
    if ($size) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $size of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
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>';
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 int|string $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">';
1053
1054
    if ($auth >= AUTH_DELETE && !$rev && file_exists(mediaFN($image))) {
1055
1056
        // delete button
1057
        $form = new Form([
1058
            'id' => 'mediamanager__btn_delete',
1059
            'action' => media_managerURL(['delete' => $image], '&'),
1060
        ]);
1061
        $form->addTagOpen('div')->addClass('no');
1062
        $form->addButton('', $lang['btn_delete'])->attr('type', 'submit');
1063
        $form->addTagClose('div');
1064
        echo '<li>';
1065
        echo $form->toHTML();
1066
        echo '</li>';
1067
    }
1068
1069
    $auth_ow = (($conf['mediarevisions']) ? AUTH_UPLOAD : AUTH_DELETE);
1070
    if ($auth >= $auth_ow && !$rev) {
1071
1072
        // upload new version button
1073
        $form = new Form([
1074
            'id' => 'mediamanager__btn_update',
1075
            'action' => media_managerURL(['image' => $image, 'mediado' => 'update'], '&'),
1076
        ]);
1077
        $form->addTagOpen('div')->addClass('no');
1078
        $form->addButton('', $lang['media_update'])->attr('type', 'submit');
1079
        $form->addTagClose('div');
1080
        echo '<li>';
1081
        echo $form->toHTML();
1082
        echo '</li>';
1083
    }
1084
1085
    if ($auth >= AUTH_UPLOAD && $rev && $conf['mediarevisions'] && file_exists(mediaFN($image, $rev))) {
1086
1087
        // restore button
1088
        $form = new Form([
1089
            'id' => 'mediamanager__btn_restore',
1090
            'action'=>media_managerURL(['image' => $image], '&'),
1091
        ]);
1092
        $form->addTagOpen('div')->addClass('no');
1093
        $form->setHiddenField('mediado', 'restore');
1094
        $form->setHiddenField('rev', $rev);
1095
        $form->addButton('', $lang['media_restore'])->attr('type', 'submit');
1096
        $form->addTagClose('div');
1097
        echo '<li>';
1098
        echo $form->toHTML();
1099
        echo '</li>';
1100
    }
1101
1102
    echo '</ul>';
1103
}
1104
1105
/**
1106
 * Returns image width and height for mediamanager preview panel
1107
 *
1108
 * @author Kate Arzamastseva <[email protected]>
1109
 * @param string         $image
1110
 * @param int|string     $rev
1111
 * @param JpegMeta|bool  $meta
1112
 * @param int            $size
1113
 * @return array
1114
 */
1115
function media_image_preview_size($image, $rev, $meta = false, $size = 500) {
1116
    if (!preg_match("/\.(jpe?g|gif|png)$/", $image)
1117
      || !file_exists($filename = mediaFN($image, $rev))
1118
    ) return array();
1119
1120
    $info = getimagesize($filename);
1121
    $w = (int) $info[0];
1122
    $h = (int) $info[1];
1123
1124
    if ($meta && ($w > $size || $h > $size)) {
1125
        $ratio = $meta->getResizeRatio($size, $size);
0 ignored issues
show
Bug introduced by
It seems like $meta is not always an object, but can also be of type boolean. Maybe add an additional type check?

If a variable is not always an object, we recommend to add an additional type check to ensure your method call is safe:

function someFunction(A $objectMaybe = null)
{
    if ($objectMaybe instanceof A) {
        $objectMaybe->doSomething();
    }
}
Loading history...
1126
        $w = floor($w * $ratio);
1127
        $h = floor($h * $ratio);
1128
    }
1129
    return array($w, $h);
1130
}
1131
1132
/**
1133
 * Returns the requested EXIF/IPTC tag from the image meta
1134
 *
1135
 * @author Kate Arzamastseva <[email protected]>
1136
 *
1137
 * @param array    $tags array with tags, first existing is returned
1138
 * @param JpegMeta $meta
1139
 * @param string   $alt  alternative value
1140
 * @return string
1141
 */
1142
function media_getTag($tags, $meta = false, $alt = '') {
1143
    if (!$meta) return $alt;
1144
    $info = $meta->getField($tags);
1145
    if (!$info) return $alt;
1146
    return $info;
1147
}
1148
1149
/**
1150
 * Returns mediafile tags
1151
 *
1152
 * @author Kate Arzamastseva <[email protected]>
1153
 *
1154
 * @param JpegMeta $meta
1155
 * @return array list of tags of the mediafile
1156
 */
1157
function media_file_tags($meta) {
1158
    // load the field descriptions
1159
    static $fields = null;
1160
    if (is_null($fields)) {
1161
        $config_files = getConfigFiles('mediameta');
1162
        foreach ($config_files as $config_file) {
1163
            if (file_exists($config_file)) include($config_file);
1164
        }
1165
    }
1166
1167
    $tags = array();
1168
1169
    foreach ($fields as $key => $tag) {
0 ignored issues
show
Bug introduced by
The expression $fields of type null is not traversable.
Loading history...
1170
        $t = array();
1171
        if (!empty($tag[0])) $t = array($tag[0]);
1172
        if (isset($tag[3]) && is_array($tag[3])) $t = array_merge($t,$tag[3]);
1173
        $value = media_getTag($t, $meta);
1174
        $tags[] = array('tag' => $tag, 'value' => $value);
1175
    }
1176
1177
    return $tags;
1178
}
1179
1180
/**
1181
 * Prints mediafile tags
1182
 *
1183
 * @author Kate Arzamastseva <[email protected]>
1184
 *
1185
 * @param string        $image image id
1186
 * @param int           $auth  permission level
1187
 * @param string|int    $rev   revision timestamp, or empty string
1188
 * @param bool|JpegMeta $meta  image object, or create one if false
1189
 */
1190
function media_details($image, $auth, $rev='', $meta=false) {
0 ignored issues
show
Unused Code introduced by
The parameter $auth is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1191
    global $lang;
1192
1193
    if (!$meta) $meta = new JpegMeta(mediaFN($image, $rev));
1194
    $tags = media_file_tags($meta);
0 ignored issues
show
Bug introduced by
It seems like $meta defined by parameter $meta on line 1190 can also be of type boolean; however, media_file_tags() does only seem to accept object<JpegMeta>, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
1195
1196
    echo '<dl>'.NL;
1197
    foreach($tags as $tag){
1198
        if ($tag['value']) {
1199
            $value = cleanText($tag['value']);
1200
            echo '<dt>'.$lang[$tag['tag'][1]].'</dt><dd>';
1201
            if ($tag['tag'][2] == 'date') echo dformat($value);
1202
            else echo hsc($value);
1203
            echo '</dd>'.NL;
1204
        }
1205
    }
1206
    echo '</dl>'.NL;
1207
    echo '<dl>'.NL;
1208
    echo '<dt>'.$lang['reference'].':</dt>';
1209
    $media_usage = ft_mediause($image,true);
1210
    if(count($media_usage) > 0){
1211
        foreach($media_usage as $path){
1212
            echo '<dd>'.html_wikilink($path).'</dd>';
1213
        }
1214
    }else{
1215
        echo '<dd>'.$lang['nothingfound'].'</dd>';
1216
    }
1217
    echo '</dl>'.NL;
1218
1219
}
1220
1221
/**
1222
 * Shows difference between two revisions of file
1223
 *
1224
 * @author Kate Arzamastseva <[email protected]>
1225
 *
1226
 * @param string $image  image id
1227
 * @param string $ns
1228
 * @param int $auth permission level
1229
 * @param bool $fromajax
1230
 * @return false|null|string
1231
 * @deprecated 2020-12-31
1232
 */
1233
function media_diff($image, $ns, $auth, $fromajax = false) {
0 ignored issues
show
Unused Code introduced by
The parameter $image is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $ns is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $auth is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $fromajax is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1234
    dbg_deprecated('see '. \dokuwiki\Ui\MediaDiff::class .'::show()');
1235
}
1236
1237
/**
1238
 * Callback for media file diff
1239
 *
1240
 * @param array $data event data
1241
 * @return false|null
1242
 * @deprecated 2020-12-31
1243
 */
1244
function _media_file_diff($data) {
0 ignored issues
show
Unused Code introduced by
The parameter $data is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1245
    dbg_deprecated('see '. \dokuwiki\Ui\MediaDiff::class .'::show()');
1246
}
1247
1248
/**
1249
 * Shows difference between two revisions of image
1250
 *
1251
 * @author Kate Arzamastseva <[email protected]>
1252
 *
1253
 * @param string $image
1254
 * @param string|int $l_rev revision timestamp, or empty string
1255
 * @param string|int $r_rev revision timestamp, or empty string
1256
 * @param string $ns
1257
 * @param int $auth permission level
1258
 * @param bool $fromajax
1259
 * @deprecated 2020-12-31
1260
 */
1261
function media_file_diff($image, $l_rev, $r_rev, $ns, $auth, $fromajax) {
0 ignored issues
show
Unused Code introduced by
The parameter $image is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $l_rev is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $r_rev is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $ns is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $auth is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $fromajax is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1262
    dbg_deprecated('see '. \dokuwiki\Ui\MediaDiff::class .'::showFileDiff()');
1263
}
1264
1265
/**
1266
 * Prints two images side by side
1267
 * and slider
1268
 *
1269
 * @author Kate Arzamastseva <[email protected]>
1270
 *
1271
 * @param string $image   image id
1272
 * @param int    $l_rev   revision timestamp, or empty string
1273
 * @param int    $r_rev   revision timestamp, or empty string
1274
 * @param array  $l_size  array with width and height
1275
 * @param array  $r_size  array with width and height
1276
 * @param string $type
1277
 * @deprecated 2020-12-31
1278
 */
1279
function media_image_diff($image, $l_rev, $r_rev, $l_size, $r_size, $type) {
0 ignored issues
show
Unused Code introduced by
The parameter $image is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $l_rev is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $r_rev is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $l_size is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $r_size is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $type is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1280
    dbg_deprecated('see '. \dokuwiki\Ui\MediaDiff::class .'::showImageDiff()');
1281
}
1282
1283
/**
1284
 * Restores an old revision of a media file
1285
 *
1286
 * @param string $image media id
1287
 * @param int    $rev   revision timestamp or empty string
1288
 * @param int    $auth
1289
 * @return string - file's id
1290
 *
1291
 * @author Kate Arzamastseva <[email protected]>
1292
 */
1293
function media_restore($image, $rev, $auth){
1294
    global $conf;
1295
    if ($auth < AUTH_UPLOAD || !$conf['mediarevisions']) return false;
1296
    $removed = (!file_exists(mediaFN($image)) && file_exists(mediaMetaFN($image, '.changes')));
1297
    if (!$image || (!file_exists(mediaFN($image)) && !$removed)) return false;
1298
    if (!$rev || !file_exists(mediaFN($image, $rev))) return false;
1299
    list(,$imime,) = mimetype($image);
1300
    $res = media_upload_finish(mediaFN($image, $rev),
1301
        mediaFN($image),
1302
        $image,
1303
        $imime,
1304
        true,
1305
        'copy');
1306
    if (is_array($res)) {
1307
        msg($res[0], $res[1]);
1308
        return false;
1309
    }
1310
    return $res;
1311
}
1312
1313
/**
1314
 * List all files found by the search request
1315
 *
1316
 * @author Tobias Sarnowski <[email protected]>
1317
 * @author Andreas Gohr <[email protected]>
1318
 * @author Kate Arzamastseva <[email protected]>
1319
 * @triggers MEDIA_SEARCH
1320
 *
1321
 * @param string $query
1322
 * @param string $ns
1323
 * @param null|int $auth
1324
 * @param bool $fullscreen
1325
 * @param string $sort
1326
 */
1327
function media_searchlist($query,$ns,$auth=null,$fullscreen=false,$sort='natural'){
0 ignored issues
show
Unused Code introduced by
The parameter $auth is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1328
    global $conf;
1329
    global $lang;
1330
1331
    $ns = cleanID($ns);
1332
    $evdata = array(
1333
        'ns'    => $ns,
1334
        'data'  => array(),
1335
        'query' => $query
1336
    );
1337
    if (!blank($query)) {
1338
        $evt = new Event('MEDIA_SEARCH', $evdata);
1339
        if ($evt->advise_before()) {
1340
            $dir = utf8_encodeFN(str_replace(':','/',$evdata['ns']));
1341
            $quoted = preg_quote($evdata['query'],'/');
1342
            //apply globbing
1343
            $quoted = str_replace(array('\*', '\?'), array('.*', '.'), $quoted, $count);
1344
1345
            //if we use globbing file name must match entirely but may be preceded by arbitrary namespace
1346
            if ($count > 0) $quoted = '^([^:]*:)*'.$quoted.'$';
1347
1348
            $pattern = '/'.$quoted.'/i';
1349
            search($evdata['data'],
1350
                    $conf['mediadir'],
1351
                    'search_media',
1352
                    array('showmsg'=>false,'pattern'=>$pattern),
1353
                    $dir,
1354
                    1,
1355
                    $sort);
1356
        }
1357
        $evt->advise_after();
1358
        unset($evt);
1359
    }
1360
1361
    if (!$fullscreen) {
1362
        echo '<h1 id="media__ns">'.sprintf($lang['searchmedia_in'],hsc($ns).':*').'</h1>'.NL;
1363
        media_searchform($ns,$query);
1364
    }
1365
1366
    if(!count($evdata['data'])){
1367
        echo '<div class="nothing">'.$lang['nothingfound'].'</div>'.NL;
1368
    }else {
1369
        if ($fullscreen) {
1370
            echo '<ul class="' . _media_get_list_type() . '">';
1371
        }
1372
        foreach($evdata['data'] as $item){
1373
            if (!$fullscreen) media_printfile($item,$item['perm'],'',true);
1374
            else media_printfile_thumbs($item,$item['perm'],false,true);
1375
        }
1376
        if ($fullscreen) echo '</ul>'.NL;
1377
    }
1378
}
1379
1380
/**
1381
 * Formats and prints one file in the list
1382
 *
1383
 * @param array     $item
1384
 * @param int       $auth              permission level
1385
 * @param string    $jump              item id
1386
 * @param bool      $display_namespace
1387
 */
1388
function media_printfile($item,$auth,$jump,$display_namespace=false){
1389
    global $lang;
1390
1391
    // Prepare zebra coloring
1392
    // I always wanted to use this variable name :-D
1393
    static $twibble = 1;
1394
    $twibble *= -1;
1395
    $zebra = ($twibble == -1) ? 'odd' : 'even';
1396
1397
    // Automatically jump to recent action
1398
    if($jump == $item['id']) {
1399
        $jump = ' id="scroll__here" ';
1400
    }else{
1401
        $jump = '';
1402
    }
1403
1404
    // Prepare fileicons
1405
    list($ext) = mimetype($item['file'],false);
1406
    $class = preg_replace('/[^_\-a-z0-9]+/i','_',$ext);
1407
    $class = 'select mediafile mf_'.$class;
1408
1409
    // Prepare filename
1410
    $file = utf8_decodeFN($item['file']);
1411
1412
    // Prepare info
1413
    $info = '';
1414
    if($item['isimg']){
1415
        $info .= (int) $item['meta']->getField('File.Width');
1416
        $info .= '&#215;';
1417
        $info .= (int) $item['meta']->getField('File.Height');
1418
        $info .= ' ';
1419
    }
1420
    $info .= '<i>'.dformat($item['mtime']).'</i>';
1421
    $info .= ' ';
1422
    $info .= filesize_h($item['size']);
1423
1424
    // output
1425
    echo '<div class="'.$zebra.'"'.$jump.' title="'.hsc($item['id']).'">'.NL;
1426
    if (!$display_namespace) {
1427
        echo '<a id="h_:'.$item['id'].'" class="'.$class.'">'.hsc($file).'</a> ';
0 ignored issues
show
Security Bug introduced by
It seems like $file defined by utf8_decodeFN($item['file']) on line 1410 can also be of type false; however, hsc() does only seem to accept string, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
1428
    } else {
1429
        echo '<a id="h_:'.$item['id'].'" class="'.$class.'">'.hsc($item['id']).'</a><br/>';
1430
    }
1431
    echo '<span class="info">('.$info.')</span>'.NL;
1432
1433
    // view button
1434
    $link = ml($item['id'],'',true);
1435
    echo ' <a href="'.$link.'" target="_blank"><img src="'.DOKU_BASE.'lib/images/magnifier.png" '.
1436
        'alt="'.$lang['mediaview'].'" title="'.$lang['mediaview'].'" class="btn" /></a>';
1437
1438
    // mediamanager button
1439
    $link = wl('',array('do'=>'media','image'=>$item['id'],'ns'=>getNS($item['id'])));
1440
    echo ' <a href="'.$link.'" target="_blank"><img src="'.DOKU_BASE.'lib/images/mediamanager.png" '.
1441
        'alt="'.$lang['btn_media'].'" title="'.$lang['btn_media'].'" class="btn" /></a>';
1442
1443
    // delete button
1444
    if($item['writable'] && $auth >= AUTH_DELETE){
1445
        $link = DOKU_BASE.'lib/exe/mediamanager.php?delete='.rawurlencode($item['id']).
1446
            '&amp;sectok='.getSecurityToken();
1447
        echo ' <a href="'.$link.'" class="btn_media_delete" title="'.$item['id'].'">'.
1448
            '<img src="'.DOKU_BASE.'lib/images/trash.png" alt="'.$lang['btn_delete'].'" '.
1449
            'title="'.$lang['btn_delete'].'" class="btn" /></a>';
1450
    }
1451
1452
    echo '<div class="example" id="ex_'.str_replace(':','_',$item['id']).'">';
1453
    echo $lang['mediausage'].' <code>{{:'.$item['id'].'}}</code>';
1454
    echo '</div>';
1455
    if($item['isimg']) media_printimgdetail($item);
1456
    echo '<div class="clearer"></div>'.NL;
1457
    echo '</div>'.NL;
1458
}
1459
1460
/**
1461
 * Display a media icon
1462
 *
1463
 * @param string $filename media id
1464
 * @param string $size     the size subfolder, if not specified 16x16 is used
1465
 * @return string html
1466
 */
1467
function media_printicon($filename, $size=''){
1468
    list($ext) = mimetype(mediaFN($filename),false);
1469
1470
    if (file_exists(DOKU_INC.'lib/images/fileicons/'.$size.'/'.$ext.'.png')) {
1471
        $icon = DOKU_BASE.'lib/images/fileicons/'.$size.'/'.$ext.'.png';
1472
    } else {
1473
        $icon = DOKU_BASE.'lib/images/fileicons/'.$size.'/file.png';
1474
    }
1475
1476
    return '<img src="'.$icon.'" alt="'.$filename.'" class="icon" />';
1477
}
1478
1479
/**
1480
 * Formats and prints one file in the list in the thumbnails view
1481
 *
1482
 * @author Kate Arzamastseva <[email protected]>
1483
 *
1484
 * @param array       $item
1485
 * @param int         $auth              permission level
1486
 * @param bool|string $jump              item id
1487
 * @param bool        $display_namespace
1488
 */
1489
function media_printfile_thumbs($item,$auth,$jump=false,$display_namespace=false){
0 ignored issues
show
Unused Code introduced by
The parameter $auth is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $jump is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1490
1491
    // Prepare filename
1492
    $file = utf8_decodeFN($item['file']);
1493
1494
    // output
1495
    echo '<li><dl title="'.hsc($item['id']).'">'.NL;
1496
1497
        echo '<dt>';
1498
    if($item['isimg']) {
1499
        media_printimgdetail($item, true);
1500
1501
    } else {
1502
        echo '<a id="d_:'.$item['id'].'" class="image" title="'.$item['id'].'" href="'.
1503
            media_managerURL(['image' => hsc($item['id']), 'ns' => getNS($item['id']),
1504
            'tab_details' => 'view']).'">';
1505
        echo media_printicon($item['id'], '32x32');
1506
        echo '</a>';
1507
    }
1508
    echo '</dt>'.NL;
1509
    if (!$display_namespace) {
1510
        $name = hsc($file);
0 ignored issues
show
Security Bug introduced by
It seems like $file defined by utf8_decodeFN($item['file']) on line 1492 can also be of type false; however, hsc() does only seem to accept string, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
1511
    } else {
1512
        $name = hsc($item['id']);
1513
    }
1514
    echo '<dd class="name"><a href="'.media_managerURL(['image' => hsc($item['id']), 'ns' => getNS($item['id']),
1515
        'tab_details' => 'view']).'" id="h_:'.$item['id'].'">'.$name.'</a></dd>'.NL;
1516
1517
    if($item['isimg']){
1518
        $size = '';
1519
        $size .= (int) $item['meta']->getField('File.Width');
1520
        $size .= '&#215;';
1521
        $size .= (int) $item['meta']->getField('File.Height');
1522
        echo '<dd class="size">'.$size.'</dd>'.NL;
1523
    } else {
1524
        echo '<dd class="size">&#160;</dd>'.NL;
1525
    }
1526
    $date = dformat($item['mtime']);
1527
    echo '<dd class="date">'.$date.'</dd>'.NL;
1528
    $filesize = filesize_h($item['size']);
1529
    echo '<dd class="filesize">'.$filesize.'</dd>'.NL;
1530
    echo '</dl></li>'.NL;
1531
}
1532
1533
/**
1534
 * Prints a thumbnail and metainfo
1535
 *
1536
 * @param array $item
1537
 * @param bool  $fullscreen
1538
 */
1539
function media_printimgdetail($item, $fullscreen=false){
1540
    // prepare thumbnail
1541
    $size = $fullscreen ? 90 : 120;
1542
1543
    $w = (int) $item['meta']->getField('File.Width');
1544
    $h = (int) $item['meta']->getField('File.Height');
1545
    if($w>$size || $h>$size){
1546
        if (!$fullscreen) {
1547
            $ratio = $item['meta']->getResizeRatio($size);
1548
        } else {
1549
            $ratio = $item['meta']->getResizeRatio($size,$size);
1550
        }
1551
        $w = floor($w * $ratio);
1552
        $h = floor($h * $ratio);
1553
    }
1554
    $src = ml($item['id'],array('w'=>$w,'h'=>$h,'t'=>$item['mtime']));
1555
    $p = array();
1556
    if (!$fullscreen) {
1557
        // In fullscreen mediamanager view, image resizing is done via CSS.
1558
        $p['width']  = $w;
1559
        $p['height'] = $h;
1560
    }
1561
    $p['alt']    = $item['id'];
1562
    $att = buildAttributes($p);
1563
1564
    // output
1565
    if ($fullscreen) {
1566
        echo '<a id="l_:'.$item['id'].'" class="image thumb" href="'.
1567
            media_managerURL(['image' => hsc($item['id']), 'ns' => getNS($item['id']), 'tab_details' => 'view']).'">';
1568
        echo '<img src="'.$src.'" '.$att.' />';
1569
        echo '</a>';
1570
    }
1571
1572
    if ($fullscreen) return;
1573
1574
    echo '<div class="detail">';
1575
    echo '<div class="thumb">';
1576
    echo '<a id="d_:'.$item['id'].'" class="select">';
1577
    echo '<img src="'.$src.'" '.$att.' />';
1578
    echo '</a>';
1579
    echo '</div>';
1580
1581
    // read EXIF/IPTC data
1582
    $t = $item['meta']->getField(array('IPTC.Headline','xmp.dc:title'));
1583
    $d = $item['meta']->getField(array('IPTC.Caption','EXIF.UserComment',
1584
                'EXIF.TIFFImageDescription',
1585
                'EXIF.TIFFUserComment'));
1586
    if(\dokuwiki\Utf8\PhpString::strlen($d) > 250) $d = \dokuwiki\Utf8\PhpString::substr($d,0,250).'...';
1587
    $k = $item['meta']->getField(array('IPTC.Keywords','IPTC.Category','xmp.dc:subject'));
1588
1589
    // print EXIF/IPTC data
1590
    if($t || $d || $k ){
1591
        echo '<p>';
1592
        if($t) echo '<strong>'.hsc($t).'</strong><br />';
1593
        if($d) echo hsc($d).'<br />';
1594
        if($t) echo '<em>'.hsc($k).'</em>';
1595
        echo '</p>';
1596
    }
1597
    echo '</div>';
1598
}
1599
1600
/**
1601
 * Build link based on the current, adding/rewriting parameters
1602
 *
1603
 * @author Kate Arzamastseva <[email protected]>
1604
 *
1605
 * @param array|bool $params
1606
 * @param string     $amp           separator
1607
 * @param bool       $abs           absolute url?
1608
 * @param bool       $params_array  return the parmeters array?
1609
 * @return string|array - link or link parameters
1610
 */
1611
function media_managerURL($params = false, $amp = '&amp;', $abs = false, $params_array = false) {
1612
    global $ID;
1613
    global $INPUT;
1614
1615
    $gets = array('do' => 'media');
1616
    $media_manager_params = array('tab_files', 'tab_details', 'image', 'ns', 'list', 'sort');
1617
    foreach ($media_manager_params as $x) {
1618
        if ($INPUT->has($x)) $gets[$x] = $INPUT->str($x);
1619
    }
1620
1621
    if ($params) {
1622
        $gets = $params + $gets;
1623
    }
1624
    unset($gets['id']);
1625
    if (isset($gets['delete'])) {
1626
        unset($gets['image']);
1627
        unset($gets['tab_details']);
1628
    }
1629
1630
    if ($params_array) return $gets;
1631
1632
    return wl($ID,$gets,$abs,$amp);
1633
}
1634
1635
/**
1636
 * Print the media upload form if permissions are correct
1637
 *
1638
 * @author Andreas Gohr <[email protected]>
1639
 * @author Kate Arzamastseva <[email protected]>
1640
 *
1641
 * @param string $ns
1642
 * @param int    $auth permission level
1643
 * @param bool  $fullscreen
1644
 */
1645
function media_uploadform($ns, $auth, $fullscreen = false) {
1646
    global $lang;
1647
    global $conf;
1648
    global $INPUT;
1649
1650
    if ($auth < AUTH_UPLOAD) {
1651
        echo '<div class="nothing">'.$lang['media_perm_upload'].'</div>'.NL;
1652
        return;
1653
    }
1654
    $auth_ow = (($conf['mediarevisions']) ? AUTH_UPLOAD : AUTH_DELETE);
1655
1656
    $update = false;
1657
    $id = '';
1658
    if ($auth >= $auth_ow && $fullscreen && $INPUT->str('mediado') == 'update') {
1659
        $update = true;
1660
        $id = cleanID($INPUT->str('image'));
1661
    }
1662
1663
    // The default HTML upload form
1664
    $form = new Form([
1665
        'id' => 'dw__upload',
1666
        'enctype' => 'multipart/form-data',
1667
        'action' => ($fullscreen)
1668
                    ? media_managerURL(['tab_files' => 'files', 'tab_details' => 'view'], '&')
1669
                    : DOKU_BASE.'lib/exe/mediamanager.php',
1670
    ]);
1671
    $form->addTagOpen('div')->addClass('no');
1672
    $form->setHiddenField('ns', hsc($ns));  // FIXME hsc required?
1673
    $form->addTagOpen('p');
1674
    $form->addTextInput('upload', $lang['txt_upload'])->id('upload__file')
1675
            ->attrs(['type' => 'file']);
1676
    $form->addTagClose('p');
1677
    $form->addTagOpen('p');
1678
    $form->addTextInput('mediaid', $lang['txt_filename'])->id('upload__name')
1679
            ->val(noNS($id));
1680
    $form->addButton('', $lang['btn_upload'])->attr('type', 'submit');
1681
    $form->addTagClose('p');
1682
    if ($auth >= $auth_ow){
1683
        $form->addTagOpen('p');
1684
        $attrs = array();
1685
        if ($update) $attrs['checked'] = 'checked';
1686
        $form->addCheckbox('ow', $lang['txt_overwrt'])->id('dw__ow')->val('1')
1687
            ->addClass('check')->attrs($attrs);
1688
        $form->addTagClose('p');
1689
    }
1690
    $form->addTagClose('div');
1691
1692
    if (!$fullscreen) {
1693
        echo '<div class="upload">'. $lang['mediaupload'] .'</div>'.DOKU_LF;
1694
    } else {
1695
        echo DOKU_LF;
1696
    }
1697
1698
    echo '<div id="mediamanager__uploader">'.DOKU_LF;
1699
    echo $form->toHTML('Upload');
1700
    echo '</div>'.DOKU_LF;
1701
1702
    echo '<p class="maxsize">';
1703
    printf($lang['maxuploadsize'], filesize_h(media_getuploadsize()));
1704
    echo ' <a class="allowedmime" href="#">'. $lang['allowedmime'] .'</a>';
1705
    echo ' <span>'. implode(', ', array_keys(getMimeTypes())) .'</span>';
1706
    echo '</p>'.DOKU_LF;
1707
}
1708
1709
/**
1710
 * Returns the size uploaded files may have
1711
 *
1712
 * This uses a conservative approach using the lowest number found
1713
 * in any of the limiting ini settings
1714
 *
1715
 * @returns int size in bytes
1716
 */
1717
function media_getuploadsize(){
1718
    $okay = 0;
1719
1720
    $post = (int) php_to_byte(@ini_get('post_max_size'));
1721
    $suho = (int) php_to_byte(@ini_get('suhosin.post.max_value_length'));
1722
    $upld = (int) php_to_byte(@ini_get('upload_max_filesize'));
1723
1724
    if($post && ($post < $okay || $okay == 0)) $okay = $post;
1725
    if($suho && ($suho < $okay || $okay == 0)) $okay = $suho;
1726
    if($upld && ($upld < $okay || $okay == 0)) $okay = $upld;
1727
1728
    return $okay;
1729
}
1730
1731
/**
1732
 * Print the search field form
1733
 *
1734
 * @author Tobias Sarnowski <[email protected]>
1735
 * @author Kate Arzamastseva <[email protected]>
1736
 *
1737
 * @param string $ns
1738
 * @param string $query
1739
 * @param bool $fullscreen
1740
 */
1741
function media_searchform($ns, $query = '', $fullscreen = false) {
1742
    global $lang;
1743
1744
    // The default HTML search form
1745
    $form = new Form([
1746
        'id'     => 'dw__mediasearch',
1747
        'action' => ($fullscreen)
1748
                    ? media_managerURL([], '&')
1749
                    : DOKU_BASE.'lib/exe/mediamanager.php',
1750
    ]);
1751
    $form->addTagOpen('div')->addClass('no');
1752
    $form->setHiddenField('ns', $ns);
1753
    $form->setHiddenField($fullscreen ? 'mediado' : 'do', 'searchlist');
1754
1755
    $form->addTagOpen('p');
1756
    $form->addTextInput('q', $lang['searchmedia'])
1757
            ->attr('title', sprintf($lang['searchmedia_in'], hsc($ns) .':*'))
1758
            ->val($query);
1759
    $form->addHTML(' ');
1760
    $form->addButton('', $lang['btn_search'])->attr('type', 'submit');
1761
    $form->addTagClose('p');
1762
    $form->addTagClose('div');
1763
    print $form->toHTML('SearchMedia');
1764
}
1765
1766
/**
1767
 * Build a tree outline of available media namespaces
1768
 *
1769
 * @author Andreas Gohr <[email protected]>
1770
 *
1771
 * @param string $ns
1772
 */
1773
function media_nstree($ns){
1774
    global $conf;
1775
    global $lang;
1776
1777
    // currently selected namespace
1778
    $ns  = cleanID($ns);
1779
    if(empty($ns)){
1780
        global $ID;
1781
        $ns = (string)getNS($ID);
1782
    }
1783
1784
    $ns_dir  = utf8_encodeFN(str_replace(':','/',$ns));
1785
1786
    $data = array();
1787
    search($data,$conf['mediadir'],'search_index',array('ns' => $ns_dir, 'nofiles' => true));
1788
1789
    // wrap a list with the root level around the other namespaces
1790
    array_unshift($data, array('level' => 0, 'id' => '', 'open' =>'true',
1791
                               'label' => '['.$lang['mediaroot'].']'));
1792
1793
    // insert the current ns into the hierarchy if it isn't already part of it
1794
    $ns_parts = explode(':', $ns);
1795
    $tmp_ns = '';
1796
    $pos = 0;
1797
    foreach ($ns_parts as $level => $part) {
1798
        if ($tmp_ns) $tmp_ns .= ':'.$part;
1799
        else $tmp_ns = $part;
1800
1801
        // find the namespace parts or insert them
1802
        while ($data[$pos]['id'] != $tmp_ns) {
1803
            if (
1804
                $pos >= count($data) ||
1805
                ($data[$pos]['level'] <= $level+1 && Sort::strcmp($data[$pos]['id'], $tmp_ns) > 0)
1806
            ) {
1807
                array_splice($data, $pos, 0, array(array('level' => $level+1, 'id' => $tmp_ns, 'open' => 'true')));
1808
                break;
1809
            }
1810
            ++$pos;
1811
        }
1812
    }
1813
1814
    echo html_buildlist($data,'idx','media_nstree_item','media_nstree_li');
1815
}
1816
1817
/**
1818
 * Userfunction for html_buildlist
1819
 *
1820
 * Prints a media namespace tree item
1821
 *
1822
 * @author Andreas Gohr <[email protected]>
1823
 *
1824
 * @param array $item
1825
 * @return string html
1826
 */
1827
function media_nstree_item($item){
1828
    global $INPUT;
1829
    $pos   = strrpos($item['id'], ':');
1830
    $label = substr($item['id'], $pos > 0 ? $pos + 1 : 0);
1831
    if(empty($item['label'])) $item['label'] = $label;
1832
1833
    $ret  = '';
1834
    if (!($INPUT->str('do') == 'media'))
1835
    $ret .= '<a href="'.DOKU_BASE.'lib/exe/mediamanager.php?ns='.idfilter($item['id']).'" class="idx_dir">';
1836
    else $ret .= '<a href="'.media_managerURL(['ns' => idfilter($item['id'], false), 'tab_files' => 'files'])
1837
        .'" class="idx_dir">';
1838
    $ret .= $item['label'];
1839
    $ret .= '</a>';
1840
    return $ret;
1841
}
1842
1843
/**
1844
 * Userfunction for html_buildlist
1845
 *
1846
 * Prints a media namespace tree item opener
1847
 *
1848
 * @author Andreas Gohr <[email protected]>
1849
 *
1850
 * @param array $item
1851
 * @return string html
1852
 */
1853
function media_nstree_li($item){
1854
    $class='media level'.$item['level'];
1855
    if($item['open']){
1856
        $class .= ' open';
1857
        $img   = DOKU_BASE.'lib/images/minus.gif';
1858
        $alt   = '−';
1859
    }else{
1860
        $class .= ' closed';
1861
        $img   = DOKU_BASE.'lib/images/plus.gif';
1862
        $alt   = '+';
1863
    }
1864
    // TODO: only deliver an image if it actually has a subtree...
1865
    return '<li class="'.$class.'">'.
1866
        '<img src="'.$img.'" alt="'.$alt.'" />';
1867
}
1868
1869
/**
1870
 * Resizes the given image to the given size
1871
 *
1872
 * @author  Andreas Gohr <[email protected]>
1873
 *
1874
 * @param string $file filename, path to file
1875
 * @param string $ext  extension
1876
 * @param int    $w    desired width
1877
 * @param int    $h    desired height
1878
 * @return string path to resized or original size if failed
1879
 */
1880
function media_resize_image($file, $ext, $w, $h=0){
1881
    global $conf;
1882
    if(!$h) $h = $w;
1883
    // we wont scale up to infinity
1884
    if($w > 2000 || $h > 2000) return $file;
1885
1886
    //cache
1887
    $local = getCacheName($file,'.media.'.$w.'x'.$h.'.'.$ext);
1888
    $mtime = (int) @filemtime($local); // 0 if not exists
1889
1890
    $options = [
1891
        'quality' => $conf['jpg_quality'],
1892
        'imconvert' => $conf['im_convert'],
1893
    ];
1894
1895
    if( $mtime <= (int) @filemtime($file) ) {
1896
        try {
1897
            \splitbrain\slika\Slika::run($file, $options)
1898
                                   ->autorotate()
1899
                                   ->resize($w, $h)
1900
                                   ->save($local, $ext);
1901
            if($conf['fperm']) @chmod($local, $conf['fperm']);
1902
        } catch (\splitbrain\slika\Exception $e) {
1903
            dbglog($e->getMessage());
0 ignored issues
show
Deprecated Code introduced by
The function dbglog() has been deprecated with message: 2020-08-13

This function 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 function will be removed from the class and what other function to use instead.

Loading history...
1904
            return $file;
1905
        }
1906
    }
1907
1908
    return $local;
1909
}
1910
1911
/**
1912
 * Center crops the given image to the wanted size
1913
 *
1914
 * @author  Andreas Gohr <[email protected]>
1915
 *
1916
 * @param string $file filename, path to file
1917
 * @param string $ext  extension
1918
 * @param int    $w    desired width
1919
 * @param int    $h    desired height
1920
 * @return string path to resized or original size if failed
1921
 */
1922
function media_crop_image($file, $ext, $w, $h=0){
1923
    global $conf;
1924
    if(!$h) $h = $w;
1925
    // we wont scale up to infinity
1926
    if($w > 2000 || $h > 2000) return $file;
1927
1928
    //cache
1929
    $local = getCacheName($file,'.media.'.$w.'x'.$h.'.crop.'.$ext);
1930
    $mtime = (int) @filemtime($local); // 0 if not exists
1931
1932
    $options = [
1933
        'quality' => $conf['jpg_quality'],
1934
        'imconvert' => $conf['im_convert'],
1935
    ];
1936
1937
    if( $mtime <= (int) @filemtime($file) ) {
1938
        try {
1939
            \splitbrain\slika\Slika::run($file, $options)
1940
                                   ->autorotate()
1941
                                    ->crop($w, $h)
1942
                                    ->save($local, $ext);
1943
            if($conf['fperm']) @chmod($local, $conf['fperm']);
1944
        } catch (\splitbrain\slika\Exception $e) {
1945
            dbglog($e->getMessage());
0 ignored issues
show
Deprecated Code introduced by
The function dbglog() has been deprecated with message: 2020-08-13

This function 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 function will be removed from the class and what other function to use instead.

Loading history...
1946
            return $file;
1947
        }
1948
    }
1949
1950
    return $local;
1951
}
1952
1953
/**
1954
 * Calculate a token to be used to verify fetch requests for resized or
1955
 * cropped images have been internally generated - and prevent external
1956
 * DDOS attacks via fetch
1957
 *
1958
 * @author Christopher Smith <[email protected]>
1959
 *
1960
 * @param string  $id    id of the image
1961
 * @param int     $w     resize/crop width
1962
 * @param int     $h     resize/crop height
1963
 * @return string token or empty string if no token required
1964
 */
1965
function media_get_token($id,$w,$h){
1966
    // token is only required for modified images
1967
    if ($w || $h || media_isexternal($id)) {
1968
        $token = $id;
1969
        if ($w) $token .= '.'.$w;
1970
        if ($h) $token .= '.'.$h;
1971
1972
        return substr(\dokuwiki\PassHash::hmac('md5', $token, auth_cookiesalt()),0,6);
0 ignored issues
show
Bug introduced by
It seems like auth_cookiesalt() can also be of type boolean; however, dokuwiki\PassHash::hmac() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
1973
    }
1974
1975
    return '';
1976
}
1977
1978
/**
1979
 * Download a remote file and return local filename
1980
 *
1981
 * returns false if download fails. Uses cached file if available and
1982
 * wanted
1983
 *
1984
 * @author  Andreas Gohr <[email protected]>
1985
 * @author  Pavel Vitis <[email protected]>
1986
 *
1987
 * @param string $url
1988
 * @param string $ext   extension
1989
 * @param int    $cache cachetime in seconds
1990
 * @return false|string path to cached file
1991
 */
1992
function media_get_from_URL($url,$ext,$cache){
1993
    global $conf;
1994
1995
    // if no cache or fetchsize just redirect
1996
    if ($cache==0)           return false;
1997
    if (!$conf['fetchsize']) return false;
1998
1999
    $local = getCacheName(strtolower($url),".media.$ext");
2000
    $mtime = @filemtime($local); // 0 if not exists
2001
2002
    //decide if download needed:
2003
    if(($mtime == 0) || // cache does not exist
2004
        ($cache != -1 && $mtime < time() - $cache) // 'recache' and cache has expired
2005
    ) {
2006
        if(media_image_download($url, $local)) {
2007
            return $local;
2008
        } else {
2009
            return false;
2010
        }
2011
    }
2012
2013
    //if cache exists use it else
2014
    if($mtime) return $local;
2015
2016
    //else return false
2017
    return false;
2018
}
2019
2020
/**
2021
 * Download image files
2022
 *
2023
 * @author Andreas Gohr <[email protected]>
2024
 *
2025
 * @param string $url
2026
 * @param string $file path to file in which to put the downloaded content
2027
 * @return bool
2028
 */
2029
function media_image_download($url,$file){
2030
    global $conf;
2031
    $http = new DokuHTTPClient();
2032
    $http->keep_alive = false; // we do single ops here, no need for keep-alive
2033
2034
    $http->max_bodysize = $conf['fetchsize'];
2035
    $http->timeout = 25; //max. 25 sec
2036
    $http->header_regexp = '!\r\nContent-Type: image/(jpe?g|gif|png)!i';
2037
2038
    $data = $http->get($url);
2039
    if(!$data) return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression $data of type false|string is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
2040
2041
    $fileexists = file_exists($file);
2042
    $fp = @fopen($file,"w");
2043
    if(!$fp) return false;
2044
    fwrite($fp,$data);
2045
    fclose($fp);
2046
    if(!$fileexists and $conf['fperm']) chmod($file, $conf['fperm']);
2047
2048
    // check if it is really an image
2049
    $info = @getimagesize($file);
2050
    if(!$info){
2051
        @unlink($file);
2052
        return false;
2053
    }
2054
2055
    return true;
2056
}
2057
2058
/**
2059
 * resize images using external ImageMagick convert program
2060
 *
2061
 * @author Pavel Vitis <[email protected]>
2062
 * @author Andreas Gohr <[email protected]>
2063
 *
2064
 * @param string $ext     extension
2065
 * @param string $from    filename path to file
2066
 * @param int    $from_w  original width
2067
 * @param int    $from_h  original height
2068
 * @param string $to      path to resized file
2069
 * @param int    $to_w    desired width
2070
 * @param int    $to_h    desired height
2071
 * @return bool
2072
 */
2073
function media_resize_imageIM($ext,$from,$from_w,$from_h,$to,$to_w,$to_h){
0 ignored issues
show
Unused Code introduced by
The parameter $from_w is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $from_h is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
2074
    global $conf;
2075
2076
    // check if convert is configured
2077
    if(!$conf['im_convert']) return false;
2078
2079
    // prepare command
2080
    $cmd  = $conf['im_convert'];
2081
    $cmd .= ' -resize '.$to_w.'x'.$to_h.'!';
2082
    if ($ext == 'jpg' || $ext == 'jpeg') {
2083
        $cmd .= ' -quality '.$conf['jpg_quality'];
2084
    }
2085
    $cmd .= " $from $to";
2086
2087
    @exec($cmd,$out,$retval);
2088
    if ($retval == 0) return true;
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $retval of type integer|null to 0; this is ambiguous as not only 0 == 0 is true, but null == 0 is true, too. Consider using a strict comparison ===.
Loading history...
2089
    return false;
2090
}
2091
2092
/**
2093
 * crop images using external ImageMagick convert program
2094
 *
2095
 * @author Andreas Gohr <[email protected]>
2096
 *
2097
 * @param string $ext     extension
2098
 * @param string $from    filename path to file
2099
 * @param int    $from_w  original width
2100
 * @param int    $from_h  original height
2101
 * @param string $to      path to resized file
2102
 * @param int    $to_w    desired width
2103
 * @param int    $to_h    desired height
2104
 * @param int    $ofs_x   offset of crop centre
2105
 * @param int    $ofs_y   offset of crop centre
2106
 * @return bool
2107
 * @deprecated 2020-09-01
2108
 */
2109
function media_crop_imageIM($ext,$from,$from_w,$from_h,$to,$to_w,$to_h,$ofs_x,$ofs_y){
0 ignored issues
show
Unused Code introduced by
The parameter $from_w is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $from_h is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
2110
    global $conf;
2111
    dbg_deprecated('splitbrain\\Slika');
2112
2113
    // check if convert is configured
2114
    if(!$conf['im_convert']) return false;
2115
2116
    // prepare command
2117
    $cmd  = $conf['im_convert'];
2118
    $cmd .= ' -crop '.$to_w.'x'.$to_h.'+'.$ofs_x.'+'.$ofs_y;
2119
    if ($ext == 'jpg' || $ext == 'jpeg') {
2120
        $cmd .= ' -quality '.$conf['jpg_quality'];
2121
    }
2122
    $cmd .= " $from $to";
2123
2124
    @exec($cmd,$out,$retval);
2125
    if ($retval == 0) return true;
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $retval of type integer|null to 0; this is ambiguous as not only 0 == 0 is true, but null == 0 is true, too. Consider using a strict comparison ===.
Loading history...
2126
    return false;
2127
}
2128
2129
/**
2130
 * resize or crop images using PHP's libGD support
2131
 *
2132
 * @author Andreas Gohr <[email protected]>
2133
 * @author Sebastian Wienecke <[email protected]>
2134
 *
2135
 * @param string $ext     extension
2136
 * @param string $from    filename path to file
2137
 * @param int    $from_w  original width
2138
 * @param int    $from_h  original height
2139
 * @param string $to      path to resized file
2140
 * @param int    $to_w    desired width
2141
 * @param int    $to_h    desired height
2142
 * @param int    $ofs_x   offset of crop centre
2143
 * @param int    $ofs_y   offset of crop centre
2144
 * @return bool
2145
 * @deprecated 2020-09-01
2146
 */
2147
function media_resize_imageGD($ext,$from,$from_w,$from_h,$to,$to_w,$to_h,$ofs_x=0,$ofs_y=0){
2148
    global $conf;
2149
    dbg_deprecated('splitbrain\\Slika');
2150
2151
    if($conf['gdlib'] < 1) return false; //no GDlib available or wanted
2152
2153
    // check available memory
2154
    if(!is_mem_available(($from_w * $from_h * 4) + ($to_w * $to_h * 4))){
2155
        return false;
2156
    }
2157
2158
    // create an image of the given filetype
2159
    $image = false;
2160
    if ($ext == 'jpg' || $ext == 'jpeg'){
2161
        if(!function_exists("imagecreatefromjpeg")) return false;
2162
        $image = @imagecreatefromjpeg($from);
2163
    }elseif($ext == 'png') {
2164
        if(!function_exists("imagecreatefrompng")) return false;
2165
        $image = @imagecreatefrompng($from);
2166
2167
    }elseif($ext == 'gif') {
2168
        if(!function_exists("imagecreatefromgif")) return false;
2169
        $image = @imagecreatefromgif($from);
2170
    }
2171
    if(!$image) return false;
2172
2173
    $newimg = false;
2174
    if(($conf['gdlib']>1) && function_exists("imagecreatetruecolor") && $ext != 'gif'){
2175
        $newimg = @imagecreatetruecolor ($to_w, $to_h);
2176
    }
2177
    if(!$newimg) $newimg = @imagecreate($to_w, $to_h);
2178
    if(!$newimg){
2179
        imagedestroy($image);
2180
        return false;
2181
    }
2182
2183
    //keep png alpha channel if possible
2184
    if($ext == 'png' && $conf['gdlib']>1 && function_exists('imagesavealpha')){
2185
        imagealphablending($newimg, false);
2186
        imagesavealpha($newimg,true);
2187
    }
2188
2189
    //keep gif transparent color if possible
2190
    if($ext == 'gif' && function_exists('imagefill') && function_exists('imagecolorallocate')) {
2191
        if(function_exists('imagecolorsforindex') && function_exists('imagecolortransparent')) {
2192
            $transcolorindex = @imagecolortransparent($image);
2193
            if($transcolorindex >= 0 ) { //transparent color exists
2194
                $transcolor = @imagecolorsforindex($image, $transcolorindex);
2195
                $transcolorindex = @imagecolorallocate(
2196
                    $newimg,
2197
                    $transcolor['red'],
2198
                    $transcolor['green'],
2199
                    $transcolor['blue']
2200
                );
2201
                @imagefill($newimg, 0, 0, $transcolorindex);
2202
                @imagecolortransparent($newimg, $transcolorindex);
2203
            }else{ //filling with white
2204
                $whitecolorindex = @imagecolorallocate($newimg, 255, 255, 255);
2205
                @imagefill($newimg, 0, 0, $whitecolorindex);
2206
            }
2207
        }else{ //filling with white
2208
            $whitecolorindex = @imagecolorallocate($newimg, 255, 255, 255);
2209
            @imagefill($newimg, 0, 0, $whitecolorindex);
2210
        }
2211
    }
2212
2213
    //try resampling first
2214
    if(function_exists("imagecopyresampled")){
2215
        if(!@imagecopyresampled($newimg, $image, 0, 0, $ofs_x, $ofs_y, $to_w, $to_h, $from_w, $from_h)) {
2216
            imagecopyresized($newimg, $image, 0, 0, $ofs_x, $ofs_y, $to_w, $to_h, $from_w, $from_h);
2217
        }
2218
    }else{
2219
        imagecopyresized($newimg, $image, 0, 0, $ofs_x, $ofs_y, $to_w, $to_h, $from_w, $from_h);
2220
    }
2221
2222
    $okay = false;
2223
    if ($ext == 'jpg' || $ext == 'jpeg'){
2224
        if(!function_exists('imagejpeg')){
2225
            $okay = false;
2226
        }else{
2227
            $okay = imagejpeg($newimg, $to, $conf['jpg_quality']);
2228
        }
2229
    }elseif($ext == 'png') {
2230
        if(!function_exists('imagepng')){
2231
            $okay = false;
2232
        }else{
2233
            $okay =  imagepng($newimg, $to);
2234
        }
2235
    }elseif($ext == 'gif') {
2236
        if(!function_exists('imagegif')){
2237
            $okay = false;
2238
        }else{
2239
            $okay = imagegif($newimg, $to);
2240
        }
2241
    }
2242
2243
    // destroy GD image ressources
2244
    if($image) imagedestroy($image);
2245
    if($newimg) imagedestroy($newimg);
2246
2247
    return $okay;
2248
}
2249
2250
/**
2251
 * Return other media files with the same base name
2252
 * but different extensions.
2253
 *
2254
 * @param string   $src     - ID of media file
2255
 * @param string[] $exts    - alternative extensions to find other files for
2256
 * @return array            - array(mime type => file ID)
2257
 *
2258
 * @author Anika Henke <[email protected]>
2259
 */
2260
function media_alternativefiles($src, $exts){
2261
2262
    $files = array();
2263
    list($srcExt, /* $srcMime */) = mimetype($src);
2264
    $filebase = substr($src, 0, -1 * (strlen($srcExt)+1));
2265
2266
    foreach($exts as $ext) {
2267
        $fileid = $filebase.'.'.$ext;
2268
        $file = mediaFN($fileid);
2269
        if(file_exists($file)) {
2270
            list(/* $fileExt */, $fileMime) = mimetype($file);
2271
            $files[$fileMime] = $fileid;
2272
        }
2273
    }
2274
    return $files;
2275
}
2276
2277
/**
2278
 * Check if video/audio is supported to be embedded.
2279
 *
2280
 * @param string $mime      - mimetype of media file
2281
 * @param string $type      - type of media files to check ('video', 'audio', or null for all)
2282
 * @return boolean
2283
 *
2284
 * @author Anika Henke <[email protected]>
2285
 */
2286
function media_supportedav($mime, $type=NULL){
2287
    $supportedAudio = array(
2288
        'ogg' => 'audio/ogg',
2289
        'mp3' => 'audio/mpeg',
2290
        'wav' => 'audio/wav',
2291
    );
2292
    $supportedVideo = array(
2293
        'webm' => 'video/webm',
2294
        'ogv' => 'video/ogg',
2295
        'mp4' => 'video/mp4',
2296
    );
2297
    if ($type == 'audio') {
2298
        $supportedAv = $supportedAudio;
2299
    } elseif ($type == 'video') {
2300
        $supportedAv = $supportedVideo;
2301
    } else {
2302
        $supportedAv = array_merge($supportedAudio, $supportedVideo);
2303
    }
2304
    return in_array($mime, $supportedAv);
2305
}
2306
2307
/**
2308
 * Return track media files with the same base name
2309
 * but extensions that indicate kind and lang.
2310
 * ie for foo.webm search foo.sub.lang.vtt, foo.cap.lang.vtt...
2311
 *
2312
 * @param string   $src     - ID of media file
2313
 * @return array            - array(mediaID => array( kind, srclang ))
2314
 *
2315
 * @author Schplurtz le Déboulonné <[email protected]>
2316
 */
2317
function media_trackfiles($src){
2318
    $kinds=array(
2319
        'sub' => 'subtitles',
2320
        'cap' => 'captions',
2321
        'des' => 'descriptions',
2322
        'cha' => 'chapters',
2323
        'met' => 'metadata'
2324
    );
2325
2326
    $files = array();
2327
    $re='/\\.(sub|cap|des|cha|met)\\.([^.]+)\\.vtt$/';
2328
    $baseid=pathinfo($src, PATHINFO_FILENAME);
2329
    $pattern=mediaFN($baseid).'.*.*.vtt';
2330
    $list=glob($pattern);
2331
    foreach($list as $track) {
2332
        if(preg_match($re, $track, $matches)){
2333
            $files[$baseid.'.'.$matches[1].'.'.$matches[2].'.vtt']=array(
2334
                $kinds[$matches[1]],
2335
                $matches[2],
2336
            );
2337
        }
2338
    }
2339
    return $files;
2340
}
2341
2342
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
2343