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

media.php ➔ media_preview_buttons()   C

Complexity

Conditions 11
Paths 16

Size

Total Lines 55

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 11
nc 16
nop 3
dl 0
loc 55
rs 6.8351
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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
15
/**
16
 * Lists pages which currently use a media file selected for deletion
17
 *
18
 * References uses the same visual as search results and share
19
 * their CSS tags except pagenames won't be links.
20
 *
21
 * @author Matthias Grimm <[email protected]>
22
 *
23
 * @param array $data
24
 * @param string $id
25
 */
26
function media_filesinuse($data,$id){
27
    global $lang;
28
    echo '<h1>'.$lang['reference'].' <code>'.hsc(noNS($id)).'</code></h1>';
29
    echo '<p>'.hsc($lang['ref_inuse']).'</p>';
30
31
    $hidden=0; //count of hits without read permission
32
    foreach($data as $row){
33
        if(auth_quickaclcheck($row) >= AUTH_READ && isVisiblePage($row)){
34
            echo '<div class="search_result">';
35
            echo '<span class="mediaref_ref">'.hsc($row).'</span>';
36
            echo '</div>';
37
        }else
38
            $hidden++;
39
    }
40
    if ($hidden){
41
        print '<div class="mediaref_hidden">'.$lang['ref_hidden'].'</div>';
42
    }
43
}
44
45
/**
46
 * Handles the saving of image meta data
47
 *
48
 * @author Andreas Gohr <[email protected]>
49
 * @author Kate Arzamastseva <[email protected]>
50
 *
51
 * @param string $id media id
52
 * @param int $auth permission level
53
 * @param array $data
54
 * @return false|string
55
 */
56
function media_metasave($id,$auth,$data){
57
    if($auth < AUTH_UPLOAD) return false;
58
    if(!checkSecurityToken()) return false;
59
    global $lang;
60
    global $conf;
61
    $src = mediaFN($id);
62
63
    $meta = new JpegMeta($src);
64
    $meta->_parseAll();
65
66
    foreach($data as $key => $val){
67
        $val=trim($val);
68
        if(empty($val)){
69
            $meta->deleteField($key);
70
        }else{
71
            $meta->setField($key,$val);
72
        }
73
    }
74
75
    $old = @filemtime($src);
76
    if(!file_exists(mediaFN($id, $old)) && file_exists($src)) {
77
        // add old revision to the attic
78
        media_saveOldRevision($id);
79
    }
80
    $filesize_old = filesize($src);
81
    if($meta->save()){
82
        if($conf['fperm']) chmod($src, $conf['fperm']);
83
        @clearstatcache(true, $src);
84
        $new = @filemtime($src);
85
        $filesize_new = filesize($src);
86
        $sizechange = $filesize_new - $filesize_old;
87
88
        // add a log entry to the media changelog
89
        addMediaLogEntry($new, $id, DOKU_CHANGE_TYPE_EDIT, $lang['media_meta_edited'], '', null, $sizechange);
90
91
        msg($lang['metasaveok'],1);
92
        return $id;
93
    }else{
94
        msg($lang['metasaveerr'],-1);
95
        return false;
96
    }
97
}
98
99
/**
100
 * check if a media is external source
101
 *
102
 * @author Gerrit Uitslag <[email protected]>
103
 *
104
 * @param string $id the media ID or URL
105
 * @return bool
106
 */
107
function media_isexternal($id){
108
    if (preg_match('#^(?:https?|ftp)://#i', $id)) return true;
109
    return false;
110
}
111
112
/**
113
 * Check if a media item is public (eg, external URL or readable by @ALL)
114
 *
115
 * @author Andreas Gohr <[email protected]>
116
 *
117
 * @param string $id  the media ID or URL
118
 * @return bool
119
 */
120
function media_ispublic($id){
121
    if(media_isexternal($id)) return true;
122
    $id = cleanID($id);
123
    if(auth_aclcheck(getNS($id).':*', '', array()) >= AUTH_READ) return true;
124
    return false;
125
}
126
127
/**
128
 * Display the form to edit image meta data
129
 *
130
 * @author Andreas Gohr <[email protected]>
131
 * @author Kate Arzamastseva <[email protected]>
132
 *
133
 * @param string $id media id
134
 * @param int $auth permission level
135
 * @return bool
136
 */
137
function media_metaform($id, $auth) {
138
    global $lang;
139
140
    if ($auth < AUTH_UPLOAD) {
141
        echo '<div class="nothing">'.$lang['media_perm_upload'].'</div>'.DOKU_LF;
142
        return false;
143
    }
144
145
    // load the field descriptions
146
    static $fields = null;
147
    if ($fields === null) {
148
        $config_files = getConfigFiles('mediameta');
149
        foreach ($config_files as $config_file) {
150
            if (file_exists($config_file)) include($config_file);
151
        }
152
    }
153
154
    $src = mediaFN($id);
155
156
    // output
157
    $form = new Form([
158
            'action' => media_managerURL(['tab_details' => 'view'], '&'),
159
            'class' => 'meta'
160
    ]);
161
    $form->addTagOpen('div')->addClass('no');
162
    $form->setHiddenField('img', $id);
163
    $form->setHiddenField('mediado', 'save');
164
    foreach ($fields as $key => $field) {
0 ignored issues
show
Bug introduced by
The expression $fields of type null is not traversable.
Loading history...
165
        // get current value
166
        if (empty($field[0])) continue;
167
        $tags = array($field[0]);
168
        if (is_array($field[3])) $tags = array_merge($tags, $field[3]);
169
        $value = tpl_img_getTag($tags, '', $src);
170
        $value = cleanText($value);
171
172
        // prepare attributes
173
        $p = array(
174
            'class' => 'edit',
175
            'id'    => 'meta__'.$key,
176
            'name'  => 'meta['.$field[0].']',
177
        );
178
179
        $form->addTagOpen('div')->addClass('row');
180
        if ($field[2] == 'text') {
181
            $form->addTextInput(
182
                $p['name'],
183
                ($lang[$field[1]] ? $lang[$field[1]] : $field[1] . ':')
184
            )->id($p['id'])->addClass($p['class'])->val($value);
185
        } else {
186
            $form->addTextarea($p['name'], $lang[$field[1]])->id($p['id'])
187
                ->val(formText($value))
188
                ->addClass($p['class'])
189
                ->attr('rows', '6')->attr('cols', '50');
190
        }
191
        $form->addTagClose('div');
192
    }
193
    $form->addTagOpen('div')->addClass('buttons');
194
    $form->addButton('mediado[save]', $lang['btn_save'])->attr('type', 'submit')
195
        ->attrs(['accesskey' => 's']);
196
    $form->addTagClose('div');
197
198
    $form->addTagClose('div');
199
    echo $form->toHTML();
200
    return true;
201
}
202
203
/**
204
 * Convenience function to check if a media file is still in use
205
 *
206
 * @author Michael Klier <[email protected]>
207
 *
208
 * @param string $id media id
209
 * @return array|bool
210
 */
211
function media_inuse($id) {
212
    global $conf;
213
214
    if($conf['refcheck']){
215
        $mediareferences = ft_mediause($id,true);
216
        if(!count($mediareferences)) {
217
            return false;
218
        } else {
219
            return $mediareferences;
220
        }
221
    } else {
222
        return false;
223
    }
224
}
225
226
/**
227
 * Handles media file deletions
228
 *
229
 * If configured, checks for media references before deletion
230
 *
231
 * @author             Andreas Gohr <[email protected]>
232
 *
233
 * @param string $id media id
234
 * @param int $auth no longer used
235
 * @return int One of: 0,
236
 *                     DOKU_MEDIA_DELETED,
237
 *                     DOKU_MEDIA_DELETED | DOKU_MEDIA_EMPTY_NS,
238
 *                     DOKU_MEDIA_NOT_AUTH,
239
 *                     DOKU_MEDIA_INUSE
240
 */
241
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...
242
    global $lang;
243
    $auth = auth_quickaclcheck(ltrim(getNS($id).':*', ':'));
244
    if($auth < AUTH_DELETE) return DOKU_MEDIA_NOT_AUTH;
245
    if(media_inuse($id)) return DOKU_MEDIA_INUSE;
246
247
    $file = mediaFN($id);
248
249
    // trigger an event - MEDIA_DELETE_FILE
250
    $data = array();
251
    $data['id']   = $id;
252
    $data['name'] = \dokuwiki\Utf8\PhpString::basename($file);
253
    $data['path'] = $file;
254
    $data['size'] = (file_exists($file)) ? filesize($file) : 0;
255
256
    $data['unl'] = false;
257
    $data['del'] = false;
258
    $evt = new Event('MEDIA_DELETE_FILE',$data);
259
    if ($evt->advise_before()) {
260
        $old = @filemtime($file);
261
        if(!file_exists(mediaFN($id, $old)) && file_exists($file)) {
262
            // add old revision to the attic
263
            media_saveOldRevision($id);
264
        }
265
266
        $data['unl'] = @unlink($file);
267
        if($data['unl']) {
268
            $sizechange = 0 - $data['size'];
269
            addMediaLogEntry(time(), $id, DOKU_CHANGE_TYPE_DELETE, $lang['deleted'], '', null, $sizechange);
270
271
            $data['del'] = io_sweepNS($id, 'mediadir');
272
        }
273
    }
274
    $evt->advise_after();
275
    unset($evt);
276
277
    if($data['unl'] && $data['del']){
278
        return DOKU_MEDIA_DELETED | DOKU_MEDIA_EMPTY_NS;
279
    }
280
281
    return $data['unl'] ? DOKU_MEDIA_DELETED : 0;
282
}
283
284
/**
285
 * Handle file uploads via XMLHttpRequest
286
 *
287
 * @param string $ns   target namespace
288
 * @param int    $auth current auth check result
289
 * @return false|string false on error, id of the new file on success
290
 */
291
function media_upload_xhr($ns,$auth){
292
    if(!checkSecurityToken()) return false;
293
    global $INPUT;
294
295
    $id = $INPUT->get->str('qqfile');
296
    list($ext,$mime) = mimetype($id);
297
    $input = fopen("php://input", "r");
298
    if (!($tmp = io_mktmpdir())) return false;
299
    $path = $tmp.'/'.md5($id);
300
    $target = fopen($path, "w");
301
    $realSize = stream_copy_to_stream($input, $target);
302
    fclose($target);
303
    fclose($input);
304
    if (isset($_SERVER["CONTENT_LENGTH"]) && ($realSize != (int)$_SERVER["CONTENT_LENGTH"])){
305
        unlink($path);
306
        return false;
307
    }
308
309
    $res = media_save(
310
        array('name' => $path,
311
            'mime' => $mime,
312
            'ext'  => $ext),
313
        $ns.':'.$id,
314
        (($INPUT->get->str('ow') == 'true') ? true : false),
315
        $auth,
316
        'copy'
317
    );
318
    unlink($path);
319
    if ($tmp) io_rmdir($tmp, true);
320
    if (is_array($res)) {
321
        msg($res[0], $res[1]);
322
        return false;
323
    }
324
    return $res;
325
}
326
327
/**
328
 * Handles media file uploads
329
 *
330
 * @author Andreas Gohr <[email protected]>
331
 * @author Michael Klier <[email protected]>
332
 *
333
 * @param string     $ns    target namespace
334
 * @param int        $auth  current auth check result
335
 * @param bool|array $file  $_FILES member, $_FILES['upload'] if false
336
 * @return false|string false on error, id of the new file on success
337
 */
338
function media_upload($ns,$auth,$file=false){
339
    if(!checkSecurityToken()) return false;
340
    global $lang;
341
    global $INPUT;
342
343
    // get file and id
344
    $id   = $INPUT->post->str('mediaid');
345
    if (!$file) $file = $_FILES['upload'];
346
    if(empty($id)) $id = $file['name'];
347
348
    // check for errors (messages are done in lib/exe/mediamanager.php)
349
    if($file['error']) return false;
350
351
    // check extensions
352
    list($fext,$fmime) = mimetype($file['name']);
353
    list($iext,$imime) = mimetype($id);
354
    if($fext && !$iext){
355
        // no extension specified in id - read original one
356
        $id   .= '.'.$fext;
357
        $imime = $fmime;
358
    }elseif($fext && $fext != $iext){
359
        // extension was changed, print warning
360
        msg(sprintf($lang['mediaextchange'],$fext,$iext));
361
    }
362
363
    $res = media_save(array('name' => $file['tmp_name'],
364
                            'mime' => $imime,
365
                            'ext'  => $iext), $ns.':'.$id,
366
                      $INPUT->post->bool('ow'), $auth, 'copy_uploaded_file');
367
    if (is_array($res)) {
368
        msg($res[0], $res[1]);
369
        return false;
370
    }
371
    return $res;
372
}
373
374
/**
375
 * An alternative to move_uploaded_file that copies
376
 *
377
 * Using copy, makes sure any setgid bits on the media directory are honored
378
 *
379
 * @see   move_uploaded_file()
380
 *
381
 * @param string $from
382
 * @param string $to
383
 * @return bool
384
 */
385
function copy_uploaded_file($from, $to){
386
    if(!is_uploaded_file($from)) return false;
387
    $ok = copy($from, $to);
388
    @unlink($from);
389
    return $ok;
390
}
391
392
/**
393
 * This generates an action event and delegates to _media_upload_action().
394
 * Action plugins are allowed to pre/postprocess the uploaded file.
395
 * (The triggered event is preventable.)
396
 *
397
 * Event data:
398
 * $data[0]     fn_tmp:    the temporary file name (read from $_FILES)
399
 * $data[1]     fn:        the file name of the uploaded file
400
 * $data[2]     id:        the future directory id of the uploaded file
401
 * $data[3]     imime:     the mimetype of the uploaded file
402
 * $data[4]     overwrite: if an existing file is going to be overwritten
403
 * $data[5]     move:      name of function that performs move/copy/..
404
 *
405
 * @triggers MEDIA_UPLOAD_FINISH
406
 *
407
 * @param array  $file
408
 * @param string $id   media id
409
 * @param bool   $ow   overwrite?
410
 * @param int    $auth permission level
411
 * @param string $move name of functions that performs move/copy/..
412
 * @return false|array|string
413
 */
414
function media_save($file, $id, $ow, $auth, $move) {
415
    if($auth < AUTH_UPLOAD) {
416
        return array("You don't have permissions to upload files.", -1);
417
    }
418
419
    if (!isset($file['mime']) || !isset($file['ext'])) {
420
        list($ext, $mime) = mimetype($id);
421
        if (!isset($file['mime'])) {
422
            $file['mime'] = $mime;
423
        }
424
        if (!isset($file['ext'])) {
425
            $file['ext'] = $ext;
426
        }
427
    }
428
429
    global $lang, $conf;
430
431
    // get filename
432
    $id   = cleanID($id);
433
    $fn   = mediaFN($id);
434
435
    // get filetype regexp
436
    $types = array_keys(getMimeTypes());
437
    $types = array_map(
438
        function ($q) {
439
            return preg_quote($q, "/");
440
        },
441
        $types
442
    );
443
    $regex = join('|',$types);
444
445
    // because a temp file was created already
446
    if(!preg_match('/\.('.$regex.')$/i',$fn)) {
447
        return array($lang['uploadwrong'],-1);
448
    }
449
450
    //check for overwrite
451
    $overwrite = file_exists($fn);
452
    $auth_ow = (($conf['mediarevisions']) ? AUTH_UPLOAD : AUTH_DELETE);
453
    if($overwrite && (!$ow || $auth < $auth_ow)) {
454
        return array($lang['uploadexist'], 0);
455
    }
456
    // check for valid content
457
    $ok = media_contentcheck($file['name'], $file['mime']);
458
    if($ok == -1){
459
        return array(sprintf($lang['uploadbadcontent'],'.' . $file['ext']),-1);
460
    }elseif($ok == -2){
461
        return array($lang['uploadspam'],-1);
462
    }elseif($ok == -3){
463
        return array($lang['uploadxss'],-1);
464
    }
465
466
    // prepare event data
467
    $data = array();
468
    $data[0] = $file['name'];
469
    $data[1] = $fn;
470
    $data[2] = $id;
471
    $data[3] = $file['mime'];
472
    $data[4] = $overwrite;
473
    $data[5] = $move;
474
475
    // trigger event
476
    return Event::createAndTrigger('MEDIA_UPLOAD_FINISH', $data, '_media_upload_action', true);
477
}
478
479
/**
480
 * Callback adapter for media_upload_finish() triggered by MEDIA_UPLOAD_FINISH
481
 *
482
 * @author Michael Klier <[email protected]>
483
 *
484
 * @param array $data event data
485
 * @return false|array|string
486
 */
487
function _media_upload_action($data) {
488
    // fixme do further sanity tests of given data?
489
    if(is_array($data) && count($data)===6) {
490
        return media_upload_finish($data[0], $data[1], $data[2], $data[3], $data[4], $data[5]);
491
    } else {
492
        return false; //callback error
493
    }
494
}
495
496
/**
497
 * Saves an uploaded media file
498
 *
499
 * @author Andreas Gohr <[email protected]>
500
 * @author Michael Klier <[email protected]>
501
 * @author Kate Arzamastseva <[email protected]>
502
 *
503
 * @param string $fn_tmp
504
 * @param string $fn
505
 * @param string $id        media id
506
 * @param string $imime     mime type
507
 * @param bool   $overwrite overwrite existing?
508
 * @param string $move      function name
509
 * @return array|string
510
 */
511
function media_upload_finish($fn_tmp, $fn, $id, $imime, $overwrite, $move = 'move_uploaded_file') {
512
    global $conf;
513
    global $lang;
514
    global $REV;
515
516
    $old = @filemtime($fn);
517
    if(!file_exists(mediaFN($id, $old)) && file_exists($fn)) {
518
        // add old revision to the attic if missing
519
        media_saveOldRevision($id);
520
    }
521
522
    // prepare directory
523
    io_createNamespace($id, 'media');
524
525
    $filesize_old = file_exists($fn) ? filesize($fn) : 0;
526
527
    if($move($fn_tmp, $fn)) {
528
        @clearstatcache(true,$fn);
529
        $new = @filemtime($fn);
530
        // Set the correct permission here.
531
        // Always chmod media because they may be saved with different permissions than expected from the php umask.
532
        // (Should normally chmod to $conf['fperm'] only if $conf['fperm'] is set.)
533
        chmod($fn, $conf['fmode']);
534
        msg($lang['uploadsucc'],1);
535
        media_notify($id,$fn,$imime,$old,$new);
536
        // add a log entry to the media changelog
537
        $filesize_new = filesize($fn);
538
        $sizechange = $filesize_new - $filesize_old;
539
        if($REV) {
540
            addMediaLogEntry(
541
                $new,
542
                $id,
543
                DOKU_CHANGE_TYPE_REVERT,
544
                sprintf($lang['restored'], dformat($REV)),
545
                $REV,
546
                null,
547
                $sizechange
548
            );
549
        } elseif($overwrite) {
550
            addMediaLogEntry($new, $id, DOKU_CHANGE_TYPE_EDIT, '', '', null, $sizechange);
551
        } else {
552
            addMediaLogEntry($new, $id, DOKU_CHANGE_TYPE_CREATE, $lang['created'], '', null, $sizechange);
553
        }
554
        return $id;
555
    }else{
556
        return array($lang['uploadfail'],-1);
557
    }
558
}
559
560
/**
561
 * Moves the current version of media file to the media_attic
562
 * directory
563
 *
564
 * @author Kate Arzamastseva <[email protected]>
565
 *
566
 * @param string $id
567
 * @return int - revision date
568
 */
569
function media_saveOldRevision($id){
570
    global $conf, $lang;
571
572
    $oldf = mediaFN($id);
573
    if(!file_exists($oldf)) return '';
574
    $date = filemtime($oldf);
575
    if (!$conf['mediarevisions']) return $date;
576
577
    $medialog = new MediaChangeLog($id);
578
    if (!$medialog->getRevisionInfo($date)) {
579
        // there was an external edit,
580
        // there is no log entry for current version of file
581
        $sizechange = filesize($oldf);
582
        if(!file_exists(mediaMetaFN($id, '.changes'))) {
583
            addMediaLogEntry($date, $id, DOKU_CHANGE_TYPE_CREATE, $lang['created'], '', null, $sizechange);
584
        } else {
585
            $oldRev = $medialog->getRevisions(-1, 1); // from changelog
586
            $oldRev = (int) (empty($oldRev) ? 0 : $oldRev[0]);
587
            $filesize_old = filesize(mediaFN($id, $oldRev));
588
            $sizechange = $sizechange - $filesize_old;
589
590
            addMediaLogEntry($date, $id, DOKU_CHANGE_TYPE_EDIT, '', '', null, $sizechange);
591
        }
592
    }
593
594
    $newf = mediaFN($id,$date);
595
    io_makeFileDir($newf);
596
    if(copy($oldf, $newf)) {
597
        // Set the correct permission here.
598
        // Always chmod media because they may be saved with different permissions than expected from the php umask.
599
        // (Should normally chmod to $conf['fperm'] only if $conf['fperm'] is set.)
600
        chmod($newf, $conf['fmode']);
601
    }
602
    return $date;
603
}
604
605
/**
606
 * This function checks if the uploaded content is really what the
607
 * mimetype says it is. We also do spam checking for text types here.
608
 *
609
 * We need to do this stuff because we can not rely on the browser
610
 * to do this check correctly. Yes, IE is broken as usual.
611
 *
612
 * @author Andreas Gohr <[email protected]>
613
 * @link   http://www.splitbrain.org/blog/2007-02/12-internet_explorer_facilitates_cross_site_scripting
614
 * @fixme  check all 26 magic IE filetypes here?
615
 *
616
 * @param string $file path to file
617
 * @param string $mime mimetype
618
 * @return int
619
 */
620
function media_contentcheck($file,$mime){
621
    global $conf;
622
    if($conf['iexssprotect']){
623
        $fh = @fopen($file, 'rb');
624
        if($fh){
625
            $bytes = fread($fh, 256);
626
            fclose($fh);
627
            if(preg_match('/<(script|a|img|html|body|iframe)[\s>]/i',$bytes)){
628
                return -3; //XSS: possibly malicious content
629
            }
630
        }
631
    }
632
    if(substr($mime,0,6) == 'image/'){
633
        $info = @getimagesize($file);
634
        if($mime == 'image/gif' && $info[2] != 1){
635
            return -1; // uploaded content did not match the file extension
636
        }elseif($mime == 'image/jpeg' && $info[2] != 2){
637
            return -1;
638
        }elseif($mime == 'image/png' && $info[2] != 3){
639
            return -1;
640
        }
641
        # fixme maybe check other images types as well
642
    }elseif(substr($mime,0,5) == 'text/'){
643
        global $TEXT;
644
        $TEXT = io_readFile($file);
645
        if(checkwordblock()){
646
            return -2; //blocked by the spam blacklist
647
        }
648
    }
649
    return 0;
650
}
651
652
/**
653
 * Send a notify mail on uploads
654
 *
655
 * @author Andreas Gohr <[email protected]>
656
 *
657
 * @param string   $id      media id
658
 * @param string   $file    path to file
659
 * @param string   $mime    mime type
660
 * @param bool|int $old_rev revision timestamp or false
661
 * @return bool
662
 */
663
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...
664
    global $conf;
665
    if(empty($conf['notify'])) return false; //notify enabled?
666
667
    $subscription = new MediaSubscriptionSender();
668
    return $subscription->sendMediaDiff($conf['notify'], 'uploadmail', $id, $old_rev, $current_rev);
669
}
670
671
/**
672
 * List all files in a given Media namespace
673
 *
674
 * @param string      $ns             namespace
675
 * @param null|int    $auth           permission level
676
 * @param string      $jump           id
677
 * @param bool        $fullscreenview
678
 * @param bool|string $sort           sorting order, false skips sorting
679
 */
680
function media_filelist($ns,$auth=null,$jump='',$fullscreenview=false,$sort=false){
681
    global $conf;
682
    global $lang;
683
    $ns = cleanID($ns);
684
685
    // check auth our self if not given (needed for ajax calls)
686
    if(is_null($auth)) $auth = auth_quickaclcheck("$ns:*");
687
688
    if (!$fullscreenview) echo '<h1 id="media__ns">:'.hsc($ns).'</h1>'.NL;
689
690
    if($auth < AUTH_READ){
691
        // FIXME: print permission warning here instead?
692
        echo '<div class="nothing">'.$lang['nothingfound'].'</div>'.NL;
693
    }else{
694
        if (!$fullscreenview) {
695
            media_uploadform($ns, $auth);
696
            media_searchform($ns);
697
        }
698
699
        $dir = utf8_encodeFN(str_replace(':','/',$ns));
700
        $data = array();
701
        search($data,$conf['mediadir'],'search_media',
702
                array('showmsg'=>true,'depth'=>1),$dir,1,$sort);
703
704
        if(!count($data)){
705
            echo '<div class="nothing">'.$lang['nothingfound'].'</div>'.NL;
706
        }else {
707
            if ($fullscreenview) {
708
                echo '<ul class="' . _media_get_list_type() . '">';
709
            }
710
            foreach($data as $item){
711
                if (!$fullscreenview) {
712
                    media_printfile($item,$auth,$jump);
713
                } else {
714
                    media_printfile_thumbs($item,$auth,$jump);
715
                }
716
            }
717
            if ($fullscreenview) echo '</ul>'.NL;
718
        }
719
    }
720
}
721
722
/**
723
 * Prints tabs for files list actions
724
 *
725
 * @author Kate Arzamastseva <[email protected]>
726
 * @author Adrian Lang <[email protected]>
727
 *
728
 * @param string $selected_tab - opened tab
729
 */
730
731
function media_tabs_files($selected_tab = ''){
732
    global $lang;
733
    $tabs = array();
734
    foreach(array('files'  => 'mediaselect',
735
                  'upload' => 'media_uploadtab',
736
                  'search' => 'media_searchtab') as $tab => $caption) {
737
        $tabs[$tab] = array('href'    => media_managerURL(['tab_files' => $tab], '&'),
738
                            'caption' => $lang[$caption]);
739
    }
740
741
    html_tabs($tabs, $selected_tab);
742
}
743
744
/**
745
 * Prints tabs for files details actions
746
 *
747
 * @author Kate Arzamastseva <[email protected]>
748
 * @param string $image filename of the current image
749
 * @param string $selected_tab opened tab
750
 */
751
function media_tabs_details($image, $selected_tab = '') {
752
    global $lang, $conf;
753
754
    $tabs = array();
755
    $tabs['view'] = array('href'    => media_managerURL(['tab_details' => 'view'], '&'),
756
                          'caption' => $lang['media_viewtab']);
757
758
    list(, $mime) = mimetype($image);
759
    if ($mime == 'image/jpeg' && file_exists(mediaFN($image))) {
760
        $tabs['edit'] = array('href'    => media_managerURL(['tab_details' => 'edit'], '&'),
761
                              'caption' => $lang['media_edittab']);
762
    }
763
    if ($conf['mediarevisions']) {
764
        $tabs['history'] = array('href'    => media_managerURL(['tab_details' => 'history'], '&'),
765
                                 'caption' => $lang['media_historytab']);
766
    }
767
768
    html_tabs($tabs, $selected_tab);
769
}
770
771
/**
772
 * Prints options for the tab that displays a list of all files
773
 *
774
 * @author Kate Arzamastseva <[email protected]>
775
 */
776
function media_tab_files_options() {
777
    global $lang;
778
    global $INPUT;
779
    global $ID;
780
781
    $form = new Form([
782
            'method' => 'get',
783
            'action' => wl($ID),
784
            'class' => 'options'
785
    ]);
786
    $form->addTagOpen('div')->addClass('no');
787
    $form->setHiddenField('sectok', null);
788
    $media_manager_params = media_managerURL([], '', false, true);
789
    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...
790
        $form->setHiddenField($pKey, $pVal);
791
    }
792
    if ($INPUT->has('q')) {
793
        $form->setHiddenField('q', $INPUT->str('q'));
794
    }
795
    $form->addHTML('<ul>'.NL);
796
    foreach (array('list' => array('listType', array('thumbs', 'rows')),
797
                  'sort' => array('sortBy', array('name', 'date')))
798
            as $group => $content) {
799
        $checked = "_media_get_${group}_type";
800
        $checked = $checked();
801
802
        $form->addHTML('<li class="'. $content[0] .'">');
803
        foreach ($content[1] as $option) {
804
            $attrs = array();
805
            if ($checked == $option) {
806
                $attrs['checked'] = 'checked';
807
            }
808
            $radio = $form->addRadioButton(
809
                $group.'_dwmedia',
810
                $lang['media_'.$group.'_'.$option]
811
            )->val($option)->id($content[0].'__'.$option)->addClass($option);
812
            $radio->attrs($attrs);
813
        }
814
        $form->addHTML('</li>'.NL);
815
    }
816
    $form->addHTML('<li>');
817
    $form->addButton('', $lang['btn_apply'])->attr('type', 'submit');
818
    $form->addHTML('</li>'.NL);
819
    $form->addHTML('</ul>'.NL);
820
    $form->addTagClose('div');
821
    print $form->toHTML();
822
}
823
824
/**
825
 * Returns type of sorting for the list of files in media manager
826
 *
827
 * @author Kate Arzamastseva <[email protected]>
828
 *
829
 * @return string - sort type
830
 */
831
function _media_get_sort_type() {
832
    return _media_get_display_param('sort', array('default' => 'name', 'date'));
833
}
834
835
/**
836
 * Returns type of listing for the list of files in media manager
837
 *
838
 * @author Kate Arzamastseva <[email protected]>
839
 *
840
 * @return string - list type
841
 */
842
function _media_get_list_type() {
843
    return _media_get_display_param('list', array('default' => 'thumbs', 'rows'));
844
}
845
846
/**
847
 * Get display parameters
848
 *
849
 * @param string $param   name of parameter
850
 * @param array  $values  allowed values, where default value has index key 'default'
851
 * @return string the parameter value
852
 */
853
function _media_get_display_param($param, $values) {
854
    global $INPUT;
855
    if (in_array($INPUT->str($param), $values)) {
856
        // FIXME: Set cookie
857
        return $INPUT->str($param);
858
    } else {
859
        $val = get_doku_pref($param, $values['default']);
860
        if (!in_array($val, $values)) {
861
            $val = $values['default'];
862
        }
863
        return $val;
864
    }
865
}
866
867
/**
868
 * Prints tab that displays a list of all files
869
 *
870
 * @author Kate Arzamastseva <[email protected]>
871
 *
872
 * @param string    $ns
873
 * @param null|int  $auth permission level
874
 * @param string    $jump item id
875
 */
876
function media_tab_files($ns,$auth=null,$jump='') {
877
    global $lang;
878
    if(is_null($auth)) $auth = auth_quickaclcheck("$ns:*");
879
880
    if($auth < AUTH_READ){
881
        echo '<div class="nothing">'.$lang['media_perm_read'].'</div>'.NL;
882
    }else{
883
        media_filelist($ns,$auth,$jump,true,_media_get_sort_type());
884
    }
885
}
886
887
/**
888
 * Prints tab that displays uploading form
889
 *
890
 * @author Kate Arzamastseva <[email protected]>
891
 *
892
 * @param string   $ns
893
 * @param null|int $auth permission level
894
 * @param string   $jump item id
895
 */
896
function media_tab_upload($ns,$auth=null,$jump='') {
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...
897
    global $lang;
898
    if(is_null($auth)) $auth = auth_quickaclcheck("$ns:*");
899
900
    echo '<div class="upload">'.NL;
901
    if ($auth >= AUTH_UPLOAD) {
902
        echo '<p>' . $lang['mediaupload'] . '</p>';
903
    }
904
    media_uploadform($ns, $auth, true);
905
    echo '</div>'.NL;
906
}
907
908
/**
909
 * Prints tab that displays search form
910
 *
911
 * @author Kate Arzamastseva <[email protected]>
912
 *
913
 * @param string $ns
914
 * @param null|int $auth permission level
915
 */
916
function media_tab_search($ns,$auth=null) {
917
    global $INPUT;
918
919
    $do = $INPUT->str('mediado');
920
    $query = $INPUT->str('q');
921
    echo '<div class="search">'.NL;
922
923
    media_searchform($ns, $query, true);
924
    if ($do == 'searchlist' || $query) {
925
        media_searchlist($query,$ns,$auth,true,_media_get_sort_type());
926
    }
927
    echo '</div>'.NL;
928
}
929
930
/**
931
 * Prints tab that displays mediafile details
932
 *
933
 * @author Kate Arzamastseva <[email protected]>
934
 *
935
 * @param string     $image media id
936
 * @param string     $ns
937
 * @param null|int   $auth  permission level
938
 * @param string|int $rev   revision timestamp or empty string
939
 */
940
function media_tab_view($image, $ns, $auth=null, $rev='') {
941
    global $lang;
942
    if(is_null($auth)) $auth = auth_quickaclcheck("$ns:*");
943
944
    if ($image && $auth >= AUTH_READ) {
945
        $meta = new JpegMeta(mediaFN($image, $rev));
946
        media_preview($image, $auth, $rev, $meta);
947
        media_preview_buttons($image, $auth, $rev);
948
        media_details($image, $auth, $rev, $meta);
949
950
    } else {
951
        echo '<div class="nothing">'.$lang['media_perm_read'].'</div>'.NL;
952
    }
953
}
954
955
/**
956
 * Prints tab that displays form for editing mediafile metadata
957
 *
958
 * @author Kate Arzamastseva <[email protected]>
959
 *
960
 * @param string     $image media id
961
 * @param string     $ns
962
 * @param null|int   $auth permission level
963
 */
964
function media_tab_edit($image, $ns, $auth=null) {
965
    if(is_null($auth)) $auth = auth_quickaclcheck("$ns:*");
966
967
    if ($image) {
968
        list(, $mime) = mimetype($image);
969
        if ($mime == 'image/jpeg') media_metaform($image,$auth);
970
    }
971
}
972
973
/**
974
 * Prints tab that displays mediafile revisions
975
 *
976
 * @author Kate Arzamastseva <[email protected]>
977
 *
978
 * @param string     $image media id
979
 * @param string     $ns
980
 * @param null|int   $auth permission level
981
 */
982
function media_tab_history($image, $ns, $auth=null) {
983
    global $lang;
984
    global $INPUT;
985
986
    if(is_null($auth)) $auth = auth_quickaclcheck("$ns:*");
987
    $do = $INPUT->str('mediado');
988
989
    if ($auth >= AUTH_READ && $image) {
990
        if ($do == 'diff'){
991
            media_diff($image, $ns, $auth);
992
        } else {
993
            $first = $INPUT->int('first');
994
            html_revisions($first, $image);
0 ignored issues
show
Deprecated Code introduced by
The function html_revisions() has been deprecated with message: 2020-07-18

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