Failed Conditions
Pull Request — master (#3361)
by
unknown
03:02
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) {
1017
        global $lang;
1018
        echo '<div class="image">';
1019
1020
        $more = array();
1021
        if ($rev) {
1022
            $more['rev'] = $rev;
1023
        } else {
1024
            $t = @filemtime(mediaFN($image));
1025
            $more['t'] = $t;
1026
        }
1027
1028
        $more['w'] = $size[0];
1029
        $more['h'] = $size[1];
1030
        $src = ml($image, $more);
1031
1032
        echo '<a href="'.$src.'" target="_blank" title="'.$lang['mediaview'].'">';
1033
        echo '<img src="'.$src.'" alt="" style="max-width: '.$size[0].'px;" />';
1034
        echo '</a>';
1035
1036
        echo '</div>'.NL;
1037
    }
1038
}
1039
1040
/**
1041
 * Prints mediafile action buttons
1042
 *
1043
 * @author Kate Arzamastseva <[email protected]>
1044
 *
1045
 * @param string     $image media id
1046
 * @param int        $auth  permission level
1047
 * @param string|int $rev   revision timestamp, or empty string
1048
 */
1049
function media_preview_buttons($image, $auth, $rev = '') {
1050
    global $lang, $conf;
1051
1052
    echo '<ul class="actions">'.DOKU_LF;
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>'.DOKU_LF;
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>'.DOKU_LF;
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>'.DOKU_LF;
1100
    }
1101
1102
    echo '</ul>'.DOKU_LF;
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|false
1114
 */
1115
function media_image_preview_size($image, $rev, $meta, $size = 500) {
1116
    if (!preg_match("/\.(jpe?g|gif|png)$/", $image) || !file_exists(mediaFN($image, $rev))) return false;
1117
1118
    $info = getimagesize(mediaFN($image, $rev));
1119
    $w = (int) $info[0];
1120
    $h = (int) $info[1];
1121
1122
    if($meta && ($w > $size || $h > $size)){
1123
        $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...
1124
        $w = floor($w * $ratio);
1125
        $h = floor($h * $ratio);
1126
    }
1127
    return array($w, $h);
1128
}
1129
1130
/**
1131
 * Returns the requested EXIF/IPTC tag from the image meta
1132
 *
1133
 * @author Kate Arzamastseva <[email protected]>
1134
 *
1135
 * @param array    $tags array with tags, first existing is returned
1136
 * @param JpegMeta $meta
1137
 * @param string   $alt  alternative value
1138
 * @return string
1139
 */
1140
function media_getTag($tags,$meta,$alt=''){
1141
    if($meta === false) return $alt;
1142
    $info = $meta->getField($tags);
1143
    if($info == false) return $alt;
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $info of type false|array|string against false; this is ambiguous if the string can be empty. Consider using a strict comparison === instead.
Loading history...
1144
    return $info;
1145
}
1146
1147
/**
1148
 * Returns mediafile tags
1149
 *
1150
 * @author Kate Arzamastseva <[email protected]>
1151
 *
1152
 * @param JpegMeta $meta
1153
 * @return array list of tags of the mediafile
1154
 */
1155
function media_file_tags($meta) {
1156
    // load the field descriptions
1157
    static $fields = null;
1158
    if(is_null($fields)){
1159
        $config_files = getConfigFiles('mediameta');
1160
        foreach ($config_files as $config_file) {
1161
            if(file_exists($config_file)) include($config_file);
1162
        }
1163
    }
1164
1165
    $tags = array();
1166
1167
    foreach($fields as $key => $tag){
0 ignored issues
show
Bug introduced by
The expression $fields of type null is not traversable.
Loading history...
1168
        $t = array();
1169
        if (!empty($tag[0])) $t = array($tag[0]);
1170
        if(isset($tag[3]) && is_array($tag[3])) $t = array_merge($t,$tag[3]);
1171
        $value = media_getTag($t, $meta);
1172
        $tags[] = array('tag' => $tag, 'value' => $value);
1173
    }
1174
1175
    return $tags;
1176
}
1177
1178
/**
1179
 * Prints mediafile tags
1180
 *
1181
 * @author Kate Arzamastseva <[email protected]>
1182
 *
1183
 * @param string        $image image id
1184
 * @param int           $auth  permission level
1185
 * @param string|int    $rev   revision timestamp, or empty string
1186
 * @param bool|JpegMeta $meta  image object, or create one if false
1187
 */
1188
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...
1189
    global $lang;
1190
1191
    if (!$meta) $meta = new JpegMeta(mediaFN($image, $rev));
1192
    $tags = media_file_tags($meta);
0 ignored issues
show
Bug introduced by
It seems like $meta defined by parameter $meta on line 1188 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...
1193
1194
    echo '<dl>'.NL;
1195
    foreach($tags as $tag){
1196
        if ($tag['value']) {
1197
            $value = cleanText($tag['value']);
1198
            echo '<dt>'.$lang[$tag['tag'][1]].'</dt><dd>';
1199
            if ($tag['tag'][2] == 'date') echo dformat($value);
1200
            else echo hsc($value);
1201
            echo '</dd>'.NL;
1202
        }
1203
    }
1204
    echo '</dl>'.NL;
1205
    echo '<dl>'.NL;
1206
    echo '<dt>'.$lang['reference'].':</dt>';
1207
    $media_usage = ft_mediause($image,true);
1208
    if(count($media_usage) > 0){
1209
        foreach($media_usage as $path){
1210
            echo '<dd>'.html_wikilink($path).'</dd>';
1211
        }
1212
    }else{
1213
        echo '<dd>'.$lang['nothingfound'].'</dd>';
1214
    }
1215
    echo '</dl>'.NL;
1216
1217
}
1218
1219
/**
1220
 * Shows difference between two revisions of file
1221
 *
1222
 * @author Kate Arzamastseva <[email protected]>
1223
 *
1224
 * @param string $image  image id
1225
 * @param string $ns
1226
 * @param int $auth permission level
1227
 * @param bool $fromajax
1228
 * @return false|null|string
1229
 * @deprecated 2020-12-31
1230
 */
1231
function media_diff($image, $ns, $auth, $fromajax = false) {
0 ignored issues
show
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 $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 $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...
1232
    dbg_deprecated('see '. \dokuwiki\Ui\MediaDiff::class .'::show()');
1233
}
1234
1235
/**
1236
 * Callback for media file diff
1237
 *
1238
 * @param array $data event data
1239
 * @return false|null
1240
 * @deprecated 2020-12-31
1241
 */
1242
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...
1243
    dbg_deprecated('see '. \dokuwiki\Ui\MediaDiff::class .'::show()');
1244
}
1245
1246
/**
1247
 * Shows difference between two revisions of image
1248
 *
1249
 * @author Kate Arzamastseva <[email protected]>
1250
 *
1251
 * @param string $image
1252
 * @param string|int $l_rev revision timestamp, or empty string
1253
 * @param string|int $r_rev revision timestamp, or empty string
1254
 * @param string $ns
1255
 * @param int $auth permission level
1256
 * @param bool $fromajax
1257
 * @deprecated 2020-12-31
1258
 */
1259
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...
1260
    dbg_deprecated('see '. \dokuwiki\Ui\MediaDiff::class .'::showFileDiff()');
1261
}
1262
1263
/**
1264
 * Prints two images side by side
1265
 * and slider
1266
 *
1267
 * @author Kate Arzamastseva <[email protected]>
1268
 *
1269
 * @param string $image   image id
1270
 * @param int    $l_rev   revision timestamp, or empty string
1271
 * @param int    $r_rev   revision timestamp, or empty string
1272
 * @param array  $l_size  array with width and height
1273
 * @param array  $r_size  array with width and height
1274
 * @param string $type
1275
 * @deprecated 2020-12-31
1276
 */
1277
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...
1278
    dbg_deprecated('see '. \dokuwiki\Ui\MediaDiff::class .'::showImageDiff()');
1279
}
1280
1281
/**
1282
 * Restores an old revision of a media file
1283
 *
1284
 * @param string $image media id
1285
 * @param int    $rev   revision timestamp or empty string
1286
 * @param int    $auth
1287
 * @return string - file's id
1288
 *
1289
 * @author Kate Arzamastseva <[email protected]>
1290
 */
1291
function media_restore($image, $rev, $auth){
1292
    global $conf;
1293
    if ($auth < AUTH_UPLOAD || !$conf['mediarevisions']) return false;
1294
    $removed = (!file_exists(mediaFN($image)) && file_exists(mediaMetaFN($image, '.changes')));
1295
    if (!$image || (!file_exists(mediaFN($image)) && !$removed)) return false;
1296
    if (!$rev || !file_exists(mediaFN($image, $rev))) return false;
1297
    list(,$imime,) = mimetype($image);
1298
    $res = media_upload_finish(mediaFN($image, $rev),
1299
        mediaFN($image),
1300
        $image,
1301
        $imime,
1302
        true,
1303
        'copy');
1304
    if (is_array($res)) {
1305
        msg($res[0], $res[1]);
1306
        return false;
1307
    }
1308
    return $res;
1309
}
1310
1311
/**
1312
 * List all files found by the search request
1313
 *
1314
 * @author Tobias Sarnowski <[email protected]>
1315
 * @author Andreas Gohr <[email protected]>
1316
 * @author Kate Arzamastseva <[email protected]>
1317
 * @triggers MEDIA_SEARCH
1318
 *
1319
 * @param string $query
1320
 * @param string $ns
1321
 * @param null|int $auth
1322
 * @param bool $fullscreen
1323
 * @param string $sort
1324
 */
1325
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...
1326
    global $conf;
1327
    global $lang;
1328
1329
    $ns = cleanID($ns);
1330
    $evdata = array(
1331
        'ns'    => $ns,
1332
        'data'  => array(),
1333
        'query' => $query
1334
    );
1335
    if (!blank($query)) {
1336
        $evt = new Event('MEDIA_SEARCH', $evdata);
1337
        if ($evt->advise_before()) {
1338
            $dir = utf8_encodeFN(str_replace(':','/',$evdata['ns']));
1339
            $quoted = preg_quote($evdata['query'],'/');
1340
            //apply globbing
1341
            $quoted = str_replace(array('\*', '\?'), array('.*', '.'), $quoted, $count);
1342
1343
            //if we use globbing file name must match entirely but may be preceded by arbitrary namespace
1344
            if ($count > 0) $quoted = '^([^:]*:)*'.$quoted.'$';
1345
1346
            $pattern = '/'.$quoted.'/i';
1347
            search($evdata['data'],
1348
                    $conf['mediadir'],
1349
                    'search_media',
1350
                    array('showmsg'=>false,'pattern'=>$pattern),
1351
                    $dir,
1352
                    1,
1353
                    $sort);
1354
        }
1355
        $evt->advise_after();
1356
        unset($evt);
1357
    }
1358
1359
    if (!$fullscreen) {
1360
        echo '<h1 id="media__ns">'.sprintf($lang['searchmedia_in'],hsc($ns).':*').'</h1>'.NL;
1361
        media_searchform($ns,$query);
1362
    }
1363
1364
    if(!count($evdata['data'])){
1365
        echo '<div class="nothing">'.$lang['nothingfound'].'</div>'.NL;
1366
    }else {
1367
        if ($fullscreen) {
1368
            echo '<ul class="' . _media_get_list_type() . '">';
1369
        }
1370
        foreach($evdata['data'] as $item){
1371
            if (!$fullscreen) media_printfile($item,$item['perm'],'',true);
1372
            else media_printfile_thumbs($item,$item['perm'],false,true);
1373
        }
1374
        if ($fullscreen) echo '</ul>'.NL;
1375
    }
1376
}
1377
1378
/**
1379
 * Formats and prints one file in the list
1380
 *
1381
 * @param array     $item
1382
 * @param int       $auth              permission level
1383
 * @param string    $jump              item id
1384
 * @param bool      $display_namespace
1385
 */
1386
function media_printfile($item,$auth,$jump,$display_namespace=false){
1387
    global $lang;
1388
1389
    // Prepare zebra coloring
1390
    // I always wanted to use this variable name :-D
1391
    static $twibble = 1;
1392
    $twibble *= -1;
1393
    $zebra = ($twibble == -1) ? 'odd' : 'even';
1394
1395
    // Automatically jump to recent action
1396
    if($jump == $item['id']) {
1397
        $jump = ' id="scroll__here" ';
1398
    }else{
1399
        $jump = '';
1400
    }
1401
1402
    // Prepare fileicons
1403
    list($ext) = mimetype($item['file'],false);
1404
    $class = preg_replace('/[^_\-a-z0-9]+/i','_',$ext);
1405
    $class = 'select mediafile mf_'.$class;
1406
1407
    // Prepare filename
1408
    $file = utf8_decodeFN($item['file']);
1409
1410
    // Prepare info
1411
    $info = '';
1412
    if($item['isimg']){
1413
        $info .= (int) $item['meta']->getField('File.Width');
1414
        $info .= '&#215;';
1415
        $info .= (int) $item['meta']->getField('File.Height');
1416
        $info .= ' ';
1417
    }
1418
    $info .= '<i>'.dformat($item['mtime']).'</i>';
1419
    $info .= ' ';
1420
    $info .= filesize_h($item['size']);
1421
1422
    // output
1423
    echo '<div class="'.$zebra.'"'.$jump.' title="'.hsc($item['id']).'">'.NL;
1424
    if (!$display_namespace) {
1425
        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 1408 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...
1426
    } else {
1427
        echo '<a id="h_:'.$item['id'].'" class="'.$class.'">'.hsc($item['id']).'</a><br/>';
1428
    }
1429
    echo '<span class="info">('.$info.')</span>'.NL;
1430
1431
    // view button
1432
    $link = ml($item['id'],'',true);
1433
    echo ' <a href="'.$link.'" target="_blank"><img src="'.DOKU_BASE.'lib/images/magnifier.png" '.
1434
        'alt="'.$lang['mediaview'].'" title="'.$lang['mediaview'].'" class="btn" /></a>';
1435
1436
    // mediamanager button
1437
    $link = wl('',array('do'=>'media','image'=>$item['id'],'ns'=>getNS($item['id'])));
1438
    echo ' <a href="'.$link.'" target="_blank"><img src="'.DOKU_BASE.'lib/images/mediamanager.png" '.
1439
        'alt="'.$lang['btn_media'].'" title="'.$lang['btn_media'].'" class="btn" /></a>';
1440
1441
    // delete button
1442
    if($item['writable'] && $auth >= AUTH_DELETE){
1443
        $link = DOKU_BASE.'lib/exe/mediamanager.php?delete='.rawurlencode($item['id']).
1444
            '&amp;sectok='.getSecurityToken();
1445
        echo ' <a href="'.$link.'" class="btn_media_delete" title="'.$item['id'].'">'.
1446
            '<img src="'.DOKU_BASE.'lib/images/trash.png" alt="'.$lang['btn_delete'].'" '.
1447
            'title="'.$lang['btn_delete'].'" class="btn" /></a>';
1448
    }
1449
1450
    echo '<div class="example" id="ex_'.str_replace(':','_',$item['id']).'">';
1451
    echo $lang['mediausage'].' <code>{{:'.$item['id'].'}}</code>';
1452
    echo '</div>';
1453
    if($item['isimg']) media_printimgdetail($item);
1454
    echo '<div class="clearer"></div>'.NL;
1455
    echo '</div>'.NL;
1456
}
1457
1458
/**
1459
 * Display a media icon
1460
 *
1461
 * @param string $filename media id
1462
 * @param string $size     the size subfolder, if not specified 16x16 is used
1463
 * @return string html
1464
 */
1465
function media_printicon($filename, $size=''){
1466
    list($ext) = mimetype(mediaFN($filename),false);
1467
1468
    if (file_exists(DOKU_INC.'lib/images/fileicons/'.$size.'/'.$ext.'.png')) {
1469
        $icon = DOKU_BASE.'lib/images/fileicons/'.$size.'/'.$ext.'.png';
1470
    } else {
1471
        $icon = DOKU_BASE.'lib/images/fileicons/'.$size.'/file.png';
1472
    }
1473
1474
    return '<img src="'.$icon.'" alt="'.$filename.'" class="icon" />';
1475
}
1476
1477
/**
1478
 * Formats and prints one file in the list in the thumbnails view
1479
 *
1480
 * @author Kate Arzamastseva <[email protected]>
1481
 *
1482
 * @param array       $item
1483
 * @param int         $auth              permission level
1484
 * @param bool|string $jump              item id
1485
 * @param bool        $display_namespace
1486
 */
1487
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...
1488
1489
    // Prepare filename
1490
    $file = utf8_decodeFN($item['file']);
1491
1492
    // output
1493
    echo '<li><dl title="'.hsc($item['id']).'">'.NL;
1494
1495
        echo '<dt>';
1496
    if($item['isimg']) {
1497
        media_printimgdetail($item, true);
1498
1499
    } else {
1500
        echo '<a id="d_:'.$item['id'].'" class="image" title="'.$item['id'].'" href="'.
1501
            media_managerURL(['image' => hsc($item['id']), 'ns' => getNS($item['id']),
1502
            'tab_details' => 'view']).'">';
1503
        echo media_printicon($item['id'], '32x32');
1504
        echo '</a>';
1505
    }
1506
    echo '</dt>'.NL;
1507
    if (!$display_namespace) {
1508
        $name = hsc($file);
0 ignored issues
show
Security Bug introduced by
It seems like $file defined by utf8_decodeFN($item['file']) on line 1490 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...
1509
    } else {
1510
        $name = hsc($item['id']);
1511
    }
1512
    echo '<dd class="name"><a href="'.media_managerURL(['image' => hsc($item['id']), 'ns' => getNS($item['id']),
1513
        'tab_details' => 'view']).'" id="h_:'.$item['id'].'">'.$name.'</a></dd>'.NL;
1514
1515
    if($item['isimg']){
1516
        $size = '';
1517
        $size .= (int) $item['meta']->getField('File.Width');
1518
        $size .= '&#215;';
1519
        $size .= (int) $item['meta']->getField('File.Height');
1520
        echo '<dd class="size">'.$size.'</dd>'.NL;
1521
    } else {
1522
        echo '<dd class="size">&#160;</dd>'.NL;
1523
    }
1524
    $date = dformat($item['mtime']);
1525
    echo '<dd class="date">'.$date.'</dd>'.NL;
1526
    $filesize = filesize_h($item['size']);
1527
    echo '<dd class="filesize">'.$filesize.'</dd>'.NL;
1528
    echo '</dl></li>'.NL;
1529
}
1530
1531
/**
1532
 * Prints a thumbnail and metainfo
1533
 *
1534
 * @param array $item
1535
 * @param bool  $fullscreen
1536
 */
1537
function media_printimgdetail($item, $fullscreen=false){
1538
    // prepare thumbnail
1539
    $size = $fullscreen ? 90 : 120;
1540
1541
    $w = (int) $item['meta']->getField('File.Width');
1542
    $h = (int) $item['meta']->getField('File.Height');
1543
    if($w>$size || $h>$size){
1544
        if (!$fullscreen) {
1545
            $ratio = $item['meta']->getResizeRatio($size);
1546
        } else {
1547
            $ratio = $item['meta']->getResizeRatio($size,$size);
1548
        }
1549
        $w = floor($w * $ratio);
1550
        $h = floor($h * $ratio);
1551
    }
1552
    $src = ml($item['id'],array('w'=>$w,'h'=>$h,'t'=>$item['mtime']));
1553
    $p = array();
1554
    if (!$fullscreen) {
1555
        // In fullscreen mediamanager view, image resizing is done via CSS.
1556
        $p['width']  = $w;
1557
        $p['height'] = $h;
1558
    }
1559
    $p['alt']    = $item['id'];
1560
    $att = buildAttributes($p);
1561
1562
    // output
1563
    if ($fullscreen) {
1564
        echo '<a id="l_:'.$item['id'].'" class="image thumb" href="'.
1565
            media_managerURL(['image' => hsc($item['id']), 'ns' => getNS($item['id']), 'tab_details' => 'view']).'">';
1566
        echo '<img src="'.$src.'" '.$att.' />';
1567
        echo '</a>';
1568
    }
1569
1570
    if ($fullscreen) return;
1571
1572
    echo '<div class="detail">';
1573
    echo '<div class="thumb">';
1574
    echo '<a id="d_:'.$item['id'].'" class="select">';
1575
    echo '<img src="'.$src.'" '.$att.' />';
1576
    echo '</a>';
1577
    echo '</div>';
1578
1579
    // read EXIF/IPTC data
1580
    $t = $item['meta']->getField(array('IPTC.Headline','xmp.dc:title'));
1581
    $d = $item['meta']->getField(array('IPTC.Caption','EXIF.UserComment',
1582
                'EXIF.TIFFImageDescription',
1583
                'EXIF.TIFFUserComment'));
1584
    if(\dokuwiki\Utf8\PhpString::strlen($d) > 250) $d = \dokuwiki\Utf8\PhpString::substr($d,0,250).'...';
1585
    $k = $item['meta']->getField(array('IPTC.Keywords','IPTC.Category','xmp.dc:subject'));
1586
1587
    // print EXIF/IPTC data
1588
    if($t || $d || $k ){
1589
        echo '<p>';
1590
        if($t) echo '<strong>'.hsc($t).'</strong><br />';
1591
        if($d) echo hsc($d).'<br />';
1592
        if($t) echo '<em>'.hsc($k).'</em>';
1593
        echo '</p>';
1594
    }
1595
    echo '</div>';
1596
}
1597
1598
/**
1599
 * Build link based on the current, adding/rewriting parameters
1600
 *
1601
 * @author Kate Arzamastseva <[email protected]>
1602
 *
1603
 * @param array|bool $params
1604
 * @param string     $amp           separator
1605
 * @param bool       $abs           absolute url?
1606
 * @param bool       $params_array  return the parmeters array?
1607
 * @return string|array - link or link parameters
1608
 */
1609
function media_managerURL($params = false, $amp = '&amp;', $abs = false, $params_array = false) {
1610
    global $ID;
1611
    global $INPUT;
1612
1613
    $gets = array('do' => 'media');
1614
    $media_manager_params = array('tab_files', 'tab_details', 'image', 'ns', 'list', 'sort');
1615
    foreach ($media_manager_params as $x) {
1616
        if ($INPUT->has($x)) $gets[$x] = $INPUT->str($x);
1617
    }
1618
1619
    if ($params) {
1620
        $gets = $params + $gets;
1621
    }
1622
    unset($gets['id']);
1623
    if (isset($gets['delete'])) {
1624
        unset($gets['image']);
1625
        unset($gets['tab_details']);
1626
    }
1627
1628
    if ($params_array) return $gets;
1629
1630
    return wl($ID,$gets,$abs,$amp);
1631
}
1632
1633
/**
1634
 * Print the media upload form if permissions are correct
1635
 *
1636
 * @author Andreas Gohr <[email protected]>
1637
 * @author Kate Arzamastseva <[email protected]>
1638
 *
1639
 * @param string $ns
1640
 * @param int    $auth permission level
1641
 * @param bool  $fullscreen
1642
 */
1643
function media_uploadform($ns, $auth, $fullscreen = false) {
1644
    global $lang;
1645
    global $conf;
1646
    global $INPUT;
1647
1648
    if ($auth < AUTH_UPLOAD) {
1649
        echo '<div class="nothing">'.$lang['media_perm_upload'].'</div>'.NL;
1650
        return;
1651
    }
1652
    $auth_ow = (($conf['mediarevisions']) ? AUTH_UPLOAD : AUTH_DELETE);
1653
1654
    $update = false;
1655
    $id = '';
1656
    if ($auth >= $auth_ow && $fullscreen && $INPUT->str('mediado') == 'update') {
1657
        $update = true;
1658
        $id = cleanID($INPUT->str('image'));
1659
    }
1660
1661
    // The default HTML upload form
1662
    $form = new Form([
1663
        'id' => 'dw__upload',
1664
        'enctype' => 'multipart/form-data',
1665
        'action' => ($fullscreen)
1666
                    ? media_managerURL(['tab_files' => 'files', 'tab_details' => 'view'], '&')
1667
                    : DOKU_BASE.'lib/exe/mediamanager.php',
1668
    ]);
1669
    $form->addTagOpen('div')->addClass('no');
1670
    $form->setHiddenField('ns', hsc($ns));  // FIXME hsc required?
1671
    $form->addTagOpen('p');
1672
    $form->addTextInput('upload', $lang['txt_upload'])->id('upload__file')
1673
            ->attrs(['type' => 'file']);
1674
    $form->addTagClose('p');
1675
    $form->addTagOpen('p');
1676
    $form->addTextInput('mediaid', $lang['txt_filename'])->id('upload__name')
1677
            ->val(noNS($id));
1678
    $form->addButton('', $lang['btn_upload'])->attr('type', 'submit');
1679
    $form->addTagClose('p');
1680
    if ($auth >= $auth_ow){
1681
        $form->addTagOpen('p');
1682
        $attrs = array();
1683
        if ($update) $attrs['checked'] = 'checked';
1684
        $form->addCheckbox('ow', $lang['txt_overwrt'])->id('dw__ow')->val('1')
1685
            ->addClass('check')->attrs($attrs);
1686
        $form->addTagClose('p');
1687
    }
1688
    $form->addTagClose('div');
1689
1690
    if (!$fullscreen) {
1691
        echo '<div class="upload">'. $lang['mediaupload'] .'</div>'.DOKU_LF;
1692
    } else {
1693
        echo DOKU_LF;
1694
    }
1695
1696
    echo '<div id="mediamanager__uploader">'.DOKU_LF;
1697
    echo $form->toHTML('Upload');
1698
    echo '</div>'.DOKU_LF;
1699
1700
    echo '<p class="maxsize">';
1701
    printf($lang['maxuploadsize'], filesize_h(media_getuploadsize()));
1702
    echo ' <a class="allowedmime" href="#">'. $lang['allowedmime'] .'</a>';
1703
    echo ' <span>'. implode(', ', array_keys(getMimeTypes())) .'</span>';
1704
    echo '</p>'.DOKU_LF;
1705
}
1706
1707
/**
1708
 * Returns the size uploaded files may have
1709
 *
1710
 * This uses a conservative approach using the lowest number found
1711
 * in any of the limiting ini settings
1712
 *
1713
 * @returns int size in bytes
1714
 */
1715
function media_getuploadsize(){
1716
    $okay = 0;
1717
1718
    $post = (int) php_to_byte(@ini_get('post_max_size'));
1719
    $suho = (int) php_to_byte(@ini_get('suhosin.post.max_value_length'));
1720
    $upld = (int) php_to_byte(@ini_get('upload_max_filesize'));
1721
1722
    if($post && ($post < $okay || $okay == 0)) $okay = $post;
1723
    if($suho && ($suho < $okay || $okay == 0)) $okay = $suho;
1724
    if($upld && ($upld < $okay || $okay == 0)) $okay = $upld;
1725
1726
    return $okay;
1727
}
1728
1729
/**
1730
 * Print the search field form
1731
 *
1732
 * @author Tobias Sarnowski <[email protected]>
1733
 * @author Kate Arzamastseva <[email protected]>
1734
 *
1735
 * @param string $ns
1736
 * @param string $query
1737
 * @param bool $fullscreen
1738
 */
1739
function media_searchform($ns, $query = '', $fullscreen = false) {
1740
    global $lang;
1741
1742
    // The default HTML search form
1743
    $form = new Form([
1744
        'id'     => 'dw__mediasearch',
1745
        'action' => ($fullscreen)
1746
                    ? media_managerURL([], '&')
1747
                    : DOKU_BASE.'lib/exe/mediamanager.php',
1748
    ]);
1749
    $form->addTagOpen('div')->addClass('no');
1750
    $form->setHiddenField('ns', $ns);
1751
    $form->setHiddenField($fullscreen ? 'mediado' : 'do', 'searchlist');
1752
1753
    $form->addTagOpen('p');
1754
    $form->addTextInput('q', $lang['searchmedia'])
1755
            ->attr('title', sprintf($lang['searchmedia_in'], hsc($ns) .':*'))
1756
            ->val($query);
1757
    $form->addHTML(' ');
1758
    $form->addButton('', $lang['btn_search'])->attr('type', 'submit');
1759
    $form->addTagClose('p');
1760
    $form->addTagClose('div');
1761
    print $form->toHTML('SearchMedia');
1762
}
1763
1764
/**
1765
 * Build a tree outline of available media namespaces
1766
 *
1767
 * @author Andreas Gohr <[email protected]>
1768
 *
1769
 * @param string $ns
1770
 */
1771
function media_nstree($ns){
1772
    global $conf;
1773
    global $lang;
1774
1775
    // currently selected namespace
1776
    $ns  = cleanID($ns);
1777
    if(empty($ns)){
1778
        global $ID;
1779
        $ns = (string)getNS($ID);
1780
    }
1781
1782
    $ns_dir  = utf8_encodeFN(str_replace(':','/',$ns));
1783
1784
    $data = array();
1785
    search($data,$conf['mediadir'],'search_index',array('ns' => $ns_dir, 'nofiles' => true));
1786
1787
    // wrap a list with the root level around the other namespaces
1788
    array_unshift($data, array('level' => 0, 'id' => '', 'open' =>'true',
1789
                               'label' => '['.$lang['mediaroot'].']'));
1790
1791
    // insert the current ns into the hierarchy if it isn't already part of it
1792
    $ns_parts = explode(':', $ns);
1793
    $tmp_ns = '';
1794
    $pos = 0;
1795
    foreach ($ns_parts as $level => $part) {
1796
        if ($tmp_ns) $tmp_ns .= ':'.$part;
1797
        else $tmp_ns = $part;
1798
1799
        // find the namespace parts or insert them
1800
        while ($data[$pos]['id'] != $tmp_ns) {
1801
            if (
1802
                $pos >= count($data) ||
1803
                ($data[$pos]['level'] <= $level+1 && Sort::strcmp($data[$pos]['id'], $tmp_ns) > 0)
1804
            ) {
1805
                array_splice($data, $pos, 0, array(array('level' => $level+1, 'id' => $tmp_ns, 'open' => 'true')));
1806
                break;
1807
            }
1808
            ++$pos;
1809
        }
1810
    }
1811
1812
    echo html_buildlist($data,'idx','media_nstree_item','media_nstree_li');
1813
}
1814
1815
/**
1816
 * Userfunction for html_buildlist
1817
 *
1818
 * Prints a media namespace tree item
1819
 *
1820
 * @author Andreas Gohr <[email protected]>
1821
 *
1822
 * @param array $item
1823
 * @return string html
1824
 */
1825
function media_nstree_item($item){
1826
    global $INPUT;
1827
    $pos   = strrpos($item['id'], ':');
1828
    $label = substr($item['id'], $pos > 0 ? $pos + 1 : 0);
1829
    if(empty($item['label'])) $item['label'] = $label;
1830
1831
    $ret  = '';
1832
    if (!($INPUT->str('do') == 'media'))
1833
    $ret .= '<a href="'.DOKU_BASE.'lib/exe/mediamanager.php?ns='.idfilter($item['id']).'" class="idx_dir">';
1834
    else $ret .= '<a href="'.media_managerURL(['ns' => idfilter($item['id'], false), 'tab_files' => 'files'])
1835
        .'" class="idx_dir">';
1836
    $ret .= $item['label'];
1837
    $ret .= '</a>';
1838
    return $ret;
1839
}
1840
1841
/**
1842
 * Userfunction for html_buildlist
1843
 *
1844
 * Prints a media namespace tree item opener
1845
 *
1846
 * @author Andreas Gohr <[email protected]>
1847
 *
1848
 * @param array $item
1849
 * @return string html
1850
 */
1851
function media_nstree_li($item){
1852
    $class='media level'.$item['level'];
1853
    if($item['open']){
1854
        $class .= ' open';
1855
        $img   = DOKU_BASE.'lib/images/minus.gif';
1856
        $alt   = '−';
1857
    }else{
1858
        $class .= ' closed';
1859
        $img   = DOKU_BASE.'lib/images/plus.gif';
1860
        $alt   = '+';
1861
    }
1862
    // TODO: only deliver an image if it actually has a subtree...
1863
    return '<li class="'.$class.'">'.
1864
        '<img src="'.$img.'" alt="'.$alt.'" />';
1865
}
1866
1867
/**
1868
 * Resizes the given image to the given size
1869
 *
1870
 * @author  Andreas Gohr <[email protected]>
1871
 *
1872
 * @param string $file filename, path to file
1873
 * @param string $ext  extension
1874
 * @param int    $w    desired width
1875
 * @param int    $h    desired height
1876
 * @return string path to resized or original size if failed
1877
 */
1878
function media_resize_image($file, $ext, $w, $h=0){
1879
    global $conf;
1880
    if(!$h) $h = $w;
1881
    // we wont scale up to infinity
1882
    if($w > 2000 || $h > 2000) return $file;
1883
1884
    //cache
1885
    $local = getCacheName($file,'.media.'.$w.'x'.$h.'.'.$ext);
1886
    $mtime = (int) @filemtime($local); // 0 if not exists
1887
1888
    $options = [
1889
        'quality' => $conf['jpg_quality'],
1890
        'imconvert' => $conf['im_convert'],
1891
    ];
1892
1893
    if( $mtime <= (int) @filemtime($file) ) {
1894
        try {
1895
            \splitbrain\slika\Slika::run($file, $options)
1896
                                   ->autorotate()
1897
                                   ->resize($w, $h)
1898
                                   ->save($local, $ext);
1899
            if($conf['fperm']) @chmod($local, $conf['fperm']);
1900
        } catch (\splitbrain\slika\Exception $e) {
1901
            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...
1902
            return $file;
1903
        }
1904
    }
1905
1906
    return $local;
1907
}
1908
1909
/**
1910
 * Center crops the given image to the wanted size
1911
 *
1912
 * @author  Andreas Gohr <[email protected]>
1913
 *
1914
 * @param string $file filename, path to file
1915
 * @param string $ext  extension
1916
 * @param int    $w    desired width
1917
 * @param int    $h    desired height
1918
 * @return string path to resized or original size if failed
1919
 */
1920
function media_crop_image($file, $ext, $w, $h=0){
1921
    global $conf;
1922
    if(!$h) $h = $w;
1923
    // we wont scale up to infinity
1924
    if($w > 2000 || $h > 2000) return $file;
1925
1926
    //cache
1927
    $local = getCacheName($file,'.media.'.$w.'x'.$h.'.crop.'.$ext);
1928
    $mtime = (int) @filemtime($local); // 0 if not exists
1929
1930
    $options = [
1931
        'quality' => $conf['jpg_quality'],
1932
        'imconvert' => $conf['im_convert'],
1933
    ];
1934
1935
    if( $mtime <= (int) @filemtime($file) ) {
1936
        try {
1937
            \splitbrain\slika\Slika::run($file, $options)
1938
                                   ->autorotate()
1939
                                    ->crop($w, $h)
1940
                                    ->save($local, $ext);
1941
            if($conf['fperm']) @chmod($local, $conf['fperm']);
1942
        } catch (\splitbrain\slika\Exception $e) {
1943
            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...
1944
            return $file;
1945
        }
1946
    }
1947
1948
    return $local;
1949
}
1950
1951
/**
1952
 * Calculate a token to be used to verify fetch requests for resized or
1953
 * cropped images have been internally generated - and prevent external
1954
 * DDOS attacks via fetch
1955
 *
1956
 * @author Christopher Smith <[email protected]>
1957
 *
1958
 * @param string  $id    id of the image
1959
 * @param int     $w     resize/crop width
1960
 * @param int     $h     resize/crop height
1961
 * @return string token or empty string if no token required
1962
 */
1963
function media_get_token($id,$w,$h){
1964
    // token is only required for modified images
1965
    if ($w || $h || media_isexternal($id)) {
1966
        $token = $id;
1967
        if ($w) $token .= '.'.$w;
1968
        if ($h) $token .= '.'.$h;
1969
1970
        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...
1971
    }
1972
1973
    return '';
1974
}
1975
1976
/**
1977
 * Download a remote file and return local filename
1978
 *
1979
 * returns false if download fails. Uses cached file if available and
1980
 * wanted
1981
 *
1982
 * @author  Andreas Gohr <[email protected]>
1983
 * @author  Pavel Vitis <[email protected]>
1984
 *
1985
 * @param string $url
1986
 * @param string $ext   extension
1987
 * @param int    $cache cachetime in seconds
1988
 * @return false|string path to cached file
1989
 */
1990
function media_get_from_URL($url,$ext,$cache){
1991
    global $conf;
1992
1993
    // if no cache or fetchsize just redirect
1994
    if ($cache==0)           return false;
1995
    if (!$conf['fetchsize']) return false;
1996
1997
    $local = getCacheName(strtolower($url),".media.$ext");
1998
    $mtime = @filemtime($local); // 0 if not exists
1999
2000
    //decide if download needed:
2001
    if(($mtime == 0) || // cache does not exist
2002
        ($cache != -1 && $mtime < time() - $cache) // 'recache' and cache has expired
2003
    ) {
2004
        if(media_image_download($url, $local)) {
2005
            return $local;
2006
        } else {
2007
            return false;
2008
        }
2009
    }
2010
2011
    //if cache exists use it else
2012
    if($mtime) return $local;
2013
2014
    //else return false
2015
    return false;
2016
}
2017
2018
/**
2019
 * Download image files
2020
 *
2021
 * @author Andreas Gohr <[email protected]>
2022
 *
2023
 * @param string $url
2024
 * @param string $file path to file in which to put the downloaded content
2025
 * @return bool
2026
 */
2027
function media_image_download($url,$file){
2028
    global $conf;
2029
    $http = new DokuHTTPClient();
2030
    $http->keep_alive = false; // we do single ops here, no need for keep-alive
2031
2032
    $http->max_bodysize = $conf['fetchsize'];
2033
    $http->timeout = 25; //max. 25 sec
2034
    $http->header_regexp = '!\r\nContent-Type: image/(jpe?g|gif|png)!i';
2035
2036
    $data = $http->get($url);
2037
    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...
2038
2039
    $fileexists = file_exists($file);
2040
    $fp = @fopen($file,"w");
2041
    if(!$fp) return false;
2042
    fwrite($fp,$data);
2043
    fclose($fp);
2044
    if(!$fileexists and $conf['fperm']) chmod($file, $conf['fperm']);
2045
2046
    // check if it is really an image
2047
    $info = @getimagesize($file);
2048
    if(!$info){
2049
        @unlink($file);
2050
        return false;
2051
    }
2052
2053
    return true;
2054
}
2055
2056
/**
2057
 * resize images using external ImageMagick convert program
2058
 *
2059
 * @author Pavel Vitis <[email protected]>
2060
 * @author Andreas Gohr <[email protected]>
2061
 *
2062
 * @param string $ext     extension
2063
 * @param string $from    filename path to file
2064
 * @param int    $from_w  original width
2065
 * @param int    $from_h  original height
2066
 * @param string $to      path to resized file
2067
 * @param int    $to_w    desired width
2068
 * @param int    $to_h    desired height
2069
 * @return bool
2070
 */
2071
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...
2072
    global $conf;
2073
2074
    // check if convert is configured
2075
    if(!$conf['im_convert']) return false;
2076
2077
    // prepare command
2078
    $cmd  = $conf['im_convert'];
2079
    $cmd .= ' -resize '.$to_w.'x'.$to_h.'!';
2080
    if ($ext == 'jpg' || $ext == 'jpeg') {
2081
        $cmd .= ' -quality '.$conf['jpg_quality'];
2082
    }
2083
    $cmd .= " $from $to";
2084
2085
    @exec($cmd,$out,$retval);
2086
    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...
2087
    return false;
2088
}
2089
2090
/**
2091
 * crop images using external ImageMagick convert program
2092
 *
2093
 * @author Andreas Gohr <[email protected]>
2094
 *
2095
 * @param string $ext     extension
2096
 * @param string $from    filename path to file
2097
 * @param int    $from_w  original width
2098
 * @param int    $from_h  original height
2099
 * @param string $to      path to resized file
2100
 * @param int    $to_w    desired width
2101
 * @param int    $to_h    desired height
2102
 * @param int    $ofs_x   offset of crop centre
2103
 * @param int    $ofs_y   offset of crop centre
2104
 * @return bool
2105
 * @deprecated 2020-09-01
2106
 */
2107
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...
2108
    global $conf;
2109
    dbg_deprecated('splitbrain\\Slika');
2110
2111
    // check if convert is configured
2112
    if(!$conf['im_convert']) return false;
2113
2114
    // prepare command
2115
    $cmd  = $conf['im_convert'];
2116
    $cmd .= ' -crop '.$to_w.'x'.$to_h.'+'.$ofs_x.'+'.$ofs_y;
2117
    if ($ext == 'jpg' || $ext == 'jpeg') {
2118
        $cmd .= ' -quality '.$conf['jpg_quality'];
2119
    }
2120
    $cmd .= " $from $to";
2121
2122
    @exec($cmd,$out,$retval);
2123
    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...
2124
    return false;
2125
}
2126
2127
/**
2128
 * resize or crop images using PHP's libGD support
2129
 *
2130
 * @author Andreas Gohr <[email protected]>
2131
 * @author Sebastian Wienecke <[email protected]>
2132
 *
2133
 * @param string $ext     extension
2134
 * @param string $from    filename path to file
2135
 * @param int    $from_w  original width
2136
 * @param int    $from_h  original height
2137
 * @param string $to      path to resized file
2138
 * @param int    $to_w    desired width
2139
 * @param int    $to_h    desired height
2140
 * @param int    $ofs_x   offset of crop centre
2141
 * @param int    $ofs_y   offset of crop centre
2142
 * @return bool
2143
 * @deprecated 2020-09-01
2144
 */
2145
function media_resize_imageGD($ext,$from,$from_w,$from_h,$to,$to_w,$to_h,$ofs_x=0,$ofs_y=0){
2146
    global $conf;
2147
    dbg_deprecated('splitbrain\\Slika');
2148
2149
    if($conf['gdlib'] < 1) return false; //no GDlib available or wanted
2150
2151
    // check available memory
2152
    if(!is_mem_available(($from_w * $from_h * 4) + ($to_w * $to_h * 4))){
2153
        return false;
2154
    }
2155
2156
    // create an image of the given filetype
2157
    $image = false;
2158
    if ($ext == 'jpg' || $ext == 'jpeg'){
2159
        if(!function_exists("imagecreatefromjpeg")) return false;
2160
        $image = @imagecreatefromjpeg($from);
2161
    }elseif($ext == 'png') {
2162
        if(!function_exists("imagecreatefrompng")) return false;
2163
        $image = @imagecreatefrompng($from);
2164
2165
    }elseif($ext == 'gif') {
2166
        if(!function_exists("imagecreatefromgif")) return false;
2167
        $image = @imagecreatefromgif($from);
2168
    }
2169
    if(!$image) return false;
2170
2171
    $newimg = false;
2172
    if(($conf['gdlib']>1) && function_exists("imagecreatetruecolor") && $ext != 'gif'){
2173
        $newimg = @imagecreatetruecolor ($to_w, $to_h);
2174
    }
2175
    if(!$newimg) $newimg = @imagecreate($to_w, $to_h);
2176
    if(!$newimg){
2177
        imagedestroy($image);
2178
        return false;
2179
    }
2180
2181
    //keep png alpha channel if possible
2182
    if($ext == 'png' && $conf['gdlib']>1 && function_exists('imagesavealpha')){
2183
        imagealphablending($newimg, false);
2184
        imagesavealpha($newimg,true);
2185
    }
2186
2187
    //keep gif transparent color if possible
2188
    if($ext == 'gif' && function_exists('imagefill') && function_exists('imagecolorallocate')) {
2189
        if(function_exists('imagecolorsforindex') && function_exists('imagecolortransparent')) {
2190
            $transcolorindex = @imagecolortransparent($image);
2191
            if($transcolorindex >= 0 ) { //transparent color exists
2192
                $transcolor = @imagecolorsforindex($image, $transcolorindex);
2193
                $transcolorindex = @imagecolorallocate(
2194
                    $newimg,
2195
                    $transcolor['red'],
2196
                    $transcolor['green'],
2197
                    $transcolor['blue']
2198
                );
2199
                @imagefill($newimg, 0, 0, $transcolorindex);
2200
                @imagecolortransparent($newimg, $transcolorindex);
2201
            }else{ //filling with white
2202
                $whitecolorindex = @imagecolorallocate($newimg, 255, 255, 255);
2203
                @imagefill($newimg, 0, 0, $whitecolorindex);
2204
            }
2205
        }else{ //filling with white
2206
            $whitecolorindex = @imagecolorallocate($newimg, 255, 255, 255);
2207
            @imagefill($newimg, 0, 0, $whitecolorindex);
2208
        }
2209
    }
2210
2211
    //try resampling first
2212
    if(function_exists("imagecopyresampled")){
2213
        if(!@imagecopyresampled($newimg, $image, 0, 0, $ofs_x, $ofs_y, $to_w, $to_h, $from_w, $from_h)) {
2214
            imagecopyresized($newimg, $image, 0, 0, $ofs_x, $ofs_y, $to_w, $to_h, $from_w, $from_h);
2215
        }
2216
    }else{
2217
        imagecopyresized($newimg, $image, 0, 0, $ofs_x, $ofs_y, $to_w, $to_h, $from_w, $from_h);
2218
    }
2219
2220
    $okay = false;
2221
    if ($ext == 'jpg' || $ext == 'jpeg'){
2222
        if(!function_exists('imagejpeg')){
2223
            $okay = false;
2224
        }else{
2225
            $okay = imagejpeg($newimg, $to, $conf['jpg_quality']);
2226
        }
2227
    }elseif($ext == 'png') {
2228
        if(!function_exists('imagepng')){
2229
            $okay = false;
2230
        }else{
2231
            $okay =  imagepng($newimg, $to);
2232
        }
2233
    }elseif($ext == 'gif') {
2234
        if(!function_exists('imagegif')){
2235
            $okay = false;
2236
        }else{
2237
            $okay = imagegif($newimg, $to);
2238
        }
2239
    }
2240
2241
    // destroy GD image ressources
2242
    if($image) imagedestroy($image);
2243
    if($newimg) imagedestroy($newimg);
2244
2245
    return $okay;
2246
}
2247
2248
/**
2249
 * Return other media files with the same base name
2250
 * but different extensions.
2251
 *
2252
 * @param string   $src     - ID of media file
2253
 * @param string[] $exts    - alternative extensions to find other files for
2254
 * @return array            - array(mime type => file ID)
2255
 *
2256
 * @author Anika Henke <[email protected]>
2257
 */
2258
function media_alternativefiles($src, $exts){
2259
2260
    $files = array();
2261
    list($srcExt, /* $srcMime */) = mimetype($src);
2262
    $filebase = substr($src, 0, -1 * (strlen($srcExt)+1));
2263
2264
    foreach($exts as $ext) {
2265
        $fileid = $filebase.'.'.$ext;
2266
        $file = mediaFN($fileid);
2267
        if(file_exists($file)) {
2268
            list(/* $fileExt */, $fileMime) = mimetype($file);
2269
            $files[$fileMime] = $fileid;
2270
        }
2271
    }
2272
    return $files;
2273
}
2274
2275
/**
2276
 * Check if video/audio is supported to be embedded.
2277
 *
2278
 * @param string $mime      - mimetype of media file
2279
 * @param string $type      - type of media files to check ('video', 'audio', or null for all)
2280
 * @return boolean
2281
 *
2282
 * @author Anika Henke <[email protected]>
2283
 */
2284
function media_supportedav($mime, $type=NULL){
2285
    $supportedAudio = array(
2286
        'ogg' => 'audio/ogg',
2287
        'mp3' => 'audio/mpeg',
2288
        'wav' => 'audio/wav',
2289
    );
2290
    $supportedVideo = array(
2291
        'webm' => 'video/webm',
2292
        'ogv' => 'video/ogg',
2293
        'mp4' => 'video/mp4',
2294
    );
2295
    if ($type == 'audio') {
2296
        $supportedAv = $supportedAudio;
2297
    } elseif ($type == 'video') {
2298
        $supportedAv = $supportedVideo;
2299
    } else {
2300
        $supportedAv = array_merge($supportedAudio, $supportedVideo);
2301
    }
2302
    return in_array($mime, $supportedAv);
2303
}
2304
2305
/**
2306
 * Return track media files with the same base name
2307
 * but extensions that indicate kind and lang.
2308
 * ie for foo.webm search foo.sub.lang.vtt, foo.cap.lang.vtt...
2309
 *
2310
 * @param string   $src     - ID of media file
2311
 * @return array            - array(mediaID => array( kind, srclang ))
2312
 *
2313
 * @author Schplurtz le Déboulonné <[email protected]>
2314
 */
2315
function media_trackfiles($src){
2316
    $kinds=array(
2317
        'sub' => 'subtitles',
2318
        'cap' => 'captions',
2319
        'des' => 'descriptions',
2320
        'cha' => 'chapters',
2321
        'met' => 'metadata'
2322
    );
2323
2324
    $files = array();
2325
    $re='/\\.(sub|cap|des|cha|met)\\.([^.]+)\\.vtt$/';
2326
    $baseid=pathinfo($src, PATHINFO_FILENAME);
2327
    $pattern=mediaFN($baseid).'.*.*.vtt';
2328
    $list=glob($pattern);
2329
    foreach($list as $track) {
2330
        if(preg_match($re, $track, $matches)){
2331
            $files[$baseid.'.'.$matches[1].'.'.$matches[2].'.vtt']=array(
2332
                $kinds[$matches[1]],
2333
                $matches[2],
2334
            );
2335
        }
2336
    }
2337
    return $files;
2338
}
2339
2340
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
2341