Failed Conditions
Pull Request — master (#3361)
by
unknown
03:00
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_mediafiles',
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
                    //FIXME old call: media_printfile($item,$auth,$jump);
714
                    $display = new \dokuwiki\Ui\Media\DisplayRow($item);
715
                    $display->scrollIntoView($jump == $item->getID());
716
                    $display->show();
717
                } else {
718
                    //FIXME old call: media_printfile_thumbs($item,$auth,$jump);
719
                    echo '<li>';
720
                    $display = new \dokuwiki\Ui\Media\DisplayTile($item);
721
                    $display->scrollIntoView($jump == $item->getID());
722
                    $display->show();
723
                    echo '</li>';
724
                }
725
            }
726
            if ($fullscreenview) echo '</ul>'.NL;
727
        }
728
    }
729
}
730
731
/**
732
 * Prints tabs for files list actions
733
 *
734
 * @author Kate Arzamastseva <[email protected]>
735
 * @author Adrian Lang <[email protected]>
736
 *
737
 * @param string $selected_tab - opened tab
738
 */
739
740
function media_tabs_files($selected_tab = ''){
741
    global $lang;
742
    $tabs = array();
743
    foreach(array('files'  => 'mediaselect',
744
                  'upload' => 'media_uploadtab',
745
                  'search' => 'media_searchtab') as $tab => $caption) {
746
        $tabs[$tab] = array('href'    => media_managerURL(['tab_files' => $tab], '&'),
747
                            'caption' => $lang[$caption]);
748
    }
749
750
    html_tabs($tabs, $selected_tab);
751
}
752
753
/**
754
 * Prints tabs for files details actions
755
 *
756
 * @author Kate Arzamastseva <[email protected]>
757
 * @param string $image filename of the current image
758
 * @param string $selected_tab opened tab
759
 */
760
function media_tabs_details($image, $selected_tab = '') {
761
    global $lang, $conf;
762
763
    $tabs = array();
764
    $tabs['view'] = array('href'    => media_managerURL(['tab_details' => 'view'], '&'),
765
                          'caption' => $lang['media_viewtab']);
766
767
    list(, $mime) = mimetype($image);
768
    if ($mime == 'image/jpeg' && file_exists(mediaFN($image))) {
769
        $tabs['edit'] = array('href'    => media_managerURL(['tab_details' => 'edit'], '&'),
770
                              'caption' => $lang['media_edittab']);
771
    }
772
    if ($conf['mediarevisions']) {
773
        $tabs['history'] = array('href'    => media_managerURL(['tab_details' => 'history'], '&'),
774
                                 'caption' => $lang['media_historytab']);
775
    }
776
777
    html_tabs($tabs, $selected_tab);
778
}
779
780
/**
781
 * Prints options for the tab that displays a list of all files
782
 *
783
 * @author Kate Arzamastseva <[email protected]>
784
 */
785
function media_tab_files_options() {
786
    global $lang;
787
    global $INPUT;
788
    global $ID;
789
790
    $form = new Form([
791
            'method' => 'get',
792
            'action' => wl($ID),
793
            'class' => 'options'
794
    ]);
795
    $form->addTagOpen('div')->addClass('no');
796
    $form->setHiddenField('sectok', null);
797
    $media_manager_params = media_managerURL([], '', false, true);
798
    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...
799
        $form->setHiddenField($pKey, $pVal);
800
    }
801
    if ($INPUT->has('q')) {
802
        $form->setHiddenField('q', $INPUT->str('q'));
803
    }
804
    $form->addHTML('<ul>'.NL);
805
    foreach (array('list' => array('listType', array('thumbs', 'rows')),
806
                  'sort' => array('sortBy', array('name', 'date')))
807
            as $group => $content) {
808
        $checked = "_media_get_${group}_type";
809
        $checked = $checked();
810
811
        $form->addHTML('<li class="'. $content[0] .'">');
812
        foreach ($content[1] as $option) {
813
            $attrs = array();
814
            if ($checked == $option) {
815
                $attrs['checked'] = 'checked';
816
            }
817
            $radio = $form->addRadioButton(
818
                $group.'_dwmedia',
819
                $lang['media_'.$group.'_'.$option]
820
            )->val($option)->id($content[0].'__'.$option)->addClass($option);
821
            $radio->attrs($attrs);
822
        }
823
        $form->addHTML('</li>'.NL);
824
    }
825
    $form->addHTML('<li>');
826
    $form->addButton('', $lang['btn_apply'])->attr('type', 'submit');
827
    $form->addHTML('</li>'.NL);
828
    $form->addHTML('</ul>'.NL);
829
    $form->addTagClose('div');
830
    print $form->toHTML();
831
}
832
833
/**
834
 * Returns type of sorting for the list of files in media manager
835
 *
836
 * @author Kate Arzamastseva <[email protected]>
837
 *
838
 * @return string - sort type
839
 */
840
function _media_get_sort_type() {
841
    return _media_get_display_param('sort', array('default' => 'name', 'date'));
842
}
843
844
/**
845
 * Returns type of listing for the list of files in media manager
846
 *
847
 * @author Kate Arzamastseva <[email protected]>
848
 *
849
 * @return string - list type
850
 */
851
function _media_get_list_type() {
852
    return _media_get_display_param('list', array('default' => 'thumbs', 'rows'));
853
}
854
855
/**
856
 * Get display parameters
857
 *
858
 * @param string $param   name of parameter
859
 * @param array  $values  allowed values, where default value has index key 'default'
860
 * @return string the parameter value
861
 */
862
function _media_get_display_param($param, $values) {
863
    global $INPUT;
864
    if (in_array($INPUT->str($param), $values)) {
865
        // FIXME: Set cookie
866
        return $INPUT->str($param);
867
    } else {
868
        $val = get_doku_pref($param, $values['default']);
869
        if (!in_array($val, $values)) {
870
            $val = $values['default'];
871
        }
872
        return $val;
873
    }
874
}
875
876
/**
877
 * Prints tab that displays a list of all files
878
 *
879
 * @author Kate Arzamastseva <[email protected]>
880
 *
881
 * @param string    $ns
882
 * @param null|int  $auth permission level
883
 * @param string    $jump item id
884
 */
885
function media_tab_files($ns,$auth=null,$jump='') {
886
    global $lang;
887
    if(is_null($auth)) $auth = auth_quickaclcheck("$ns:*");
888
889
    if($auth < AUTH_READ){
890
        echo '<div class="nothing">'.$lang['media_perm_read'].'</div>'.NL;
891
    }else{
892
        media_filelist($ns,$auth,$jump,true,_media_get_sort_type());
893
    }
894
}
895
896
/**
897
 * Prints tab that displays uploading form
898
 *
899
 * @author Kate Arzamastseva <[email protected]>
900
 *
901
 * @param string   $ns
902
 * @param null|int $auth permission level
903
 * @param string   $jump item id
904
 */
905
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...
906
    global $lang;
907
    if(is_null($auth)) $auth = auth_quickaclcheck("$ns:*");
908
909
    echo '<div class="upload">'.NL;
910
    if ($auth >= AUTH_UPLOAD) {
911
        echo '<p>' . $lang['mediaupload'] . '</p>';
912
    }
913
    media_uploadform($ns, $auth, true);
914
    echo '</div>'.NL;
915
}
916
917
/**
918
 * Prints tab that displays search form
919
 *
920
 * @author Kate Arzamastseva <[email protected]>
921
 *
922
 * @param string $ns
923
 * @param null|int $auth permission level
924
 */
925
function media_tab_search($ns,$auth=null) {
926
    global $INPUT;
927
928
    $do = $INPUT->str('mediado');
929
    $query = $INPUT->str('q');
930
    echo '<div class="search">'.NL;
931
932
    media_searchform($ns, $query, true);
933
    if ($do == 'searchlist' || $query) {
934
        media_searchlist($query,$ns,$auth,true,_media_get_sort_type());
935
    }
936
    echo '</div>'.NL;
937
}
938
939
/**
940
 * Prints tab that displays mediafile details
941
 *
942
 * @author Kate Arzamastseva <[email protected]>
943
 *
944
 * @param string     $image media id
945
 * @param string     $ns
946
 * @param null|int   $auth  permission level
947
 * @param string|int $rev   revision timestamp or empty string
948
 */
949
function media_tab_view($image, $ns, $auth=null, $rev='') {
950
    global $lang;
951
    if(is_null($auth)) $auth = auth_quickaclcheck("$ns:*");
952
953
    if ($image && $auth >= AUTH_READ) {
954
        $meta = new JpegMeta(mediaFN($image, $rev));
955
        media_preview($image, $auth, $rev, $meta);
956
        media_preview_buttons($image, $auth, $rev);
957
        media_details($image, $auth, $rev, $meta);
958
959
    } else {
960
        echo '<div class="nothing">'.$lang['media_perm_read'].'</div>'.NL;
961
    }
962
}
963
964
/**
965
 * Prints tab that displays form for editing mediafile metadata
966
 *
967
 * @author Kate Arzamastseva <[email protected]>
968
 *
969
 * @param string     $image media id
970
 * @param string     $ns
971
 * @param null|int   $auth permission level
972
 */
973
function media_tab_edit($image, $ns, $auth=null) {
974
    if(is_null($auth)) $auth = auth_quickaclcheck("$ns:*");
975
976
    if ($image) {
977
        list(, $mime) = mimetype($image);
978
        if ($mime == 'image/jpeg') media_metaform($image,$auth);
979
    }
980
}
981
982
/**
983
 * Prints tab that displays mediafile revisions
984
 *
985
 * @author Kate Arzamastseva <[email protected]>
986
 *
987
 * @param string     $image media id
988
 * @param string     $ns
989
 * @param null|int   $auth permission level
990
 */
991
function media_tab_history($image, $ns, $auth=null) {
992
    global $lang;
993
    global $INPUT;
994
995
    if(is_null($auth)) $auth = auth_quickaclcheck("$ns:*");
996
    $do = $INPUT->str('mediado');
997
998
    if ($auth >= AUTH_READ && $image) {
999
        if ($do == 'diff'){
1000
            (new dokuwiki\Ui\MediaDiff($image))->show(); //media_diff($image, $ns, $auth);
1001
        } else {
1002
            $first = $INPUT->int('first');
1003
            (new dokuwiki\Ui\MediaRevisions($image))->show($first);
1004
        }
1005
    } else {
1006
        echo '<div class="nothing">'.$lang['media_perm_read'].'</div>'.NL;
1007
    }
1008
}
1009
1010
/**
1011
 * Prints mediafile details
1012
 *
1013
 * @param string         $image media id
1014
 * @param int            $auth permission level
1015
 * @param int|string     $rev revision timestamp or empty string
1016
 * @param JpegMeta|bool  $meta
1017
 *
1018
 * @author Kate Arzamastseva <[email protected]>
1019
 */
1020
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...
1021
1022
    $size = media_image_preview_size($image, $rev, $meta);
1023
1024
    if ($size) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $size of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

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

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

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