Completed
Pull Request — master (#1066)
by
unknown
02:53
created

media.php ➔ media_image_preview_size()   B

Complexity

Conditions 7
Paths 6

Size

Total Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
nc 6
nop 4
dl 0
loc 21
rs 8.6506
c 0
b 0
f 0
1
<?php
2
/**
3
 * All output and handler function needed for the media management popup
4
 *
5
 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
6
 * @author     Andreas Gohr <[email protected]>
7
 */
8
9
use dokuwiki\ChangeLog\MediaChangeLog;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, MediaChangeLog.

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

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

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

// Bar.php
namespace OtherDir;

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

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

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

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

// Bar.php
namespace OtherDir;

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

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

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

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

// Bar.php
namespace OtherDir;

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

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

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

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

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
11
use dokuwiki\Subscriptions\MediaSubscriptionSender;
12
use dokuwiki\Extension\Event;
13
14
/**
15
 * Lists pages which currently use a media file selected for deletion
16
 *
17
 * References uses the same visual as search results and share
18
 * their CSS tags except pagenames won't be links.
19
 *
20
 * @author Matthias Grimm <[email protected]>
21
 *
22
 * @param array $data
23
 * @param string $id
24
 */
25
function media_filesinuse($data,$id){
26
    global $lang;
27
    echo '<h1>'.$lang['reference'].' <code>'.hsc(noNS($id)).'</code></h1>';
28
    echo '<p>'.hsc($lang['ref_inuse']).'</p>';
29
30
    $hidden=0; //count of hits without read permission
31
    foreach($data as $row){
32
        if(auth_quickaclcheck($row) >= AUTH_READ && isVisiblePage($row)){
33
            echo '<div class="search_result">';
34
            echo '<span class="mediaref_ref">'.hsc($row).'</span>';
35
            echo '</div>';
36
        }else
37
            $hidden++;
38
    }
39
    if ($hidden){
40
        print '<div class="mediaref_hidden">'.$lang['ref_hidden'].'</div>';
41
    }
42
}
43
44
/**
45
 * Handles the saving of image meta data
46
 *
47
 * @author Andreas Gohr <[email protected]>
48
 * @author Kate Arzamastseva <[email protected]>
49
 *
50
 * @param string $id media id
51
 * @param int $auth permission level
52
 * @param array $data
53
 * @return false|string
54
 */
55
function media_metasave($id,$auth,$data){
56
    if($auth < AUTH_UPLOAD) return false;
57
    if(!checkSecurityToken()) return false;
58
    global $lang;
59
    global $conf;
60
    $src = mediaFN($id);
61
62
    $meta = new JpegMeta($src);
63
    $meta->_parseAll();
64
65
    foreach($data as $key => $val){
66
        $val=trim($val);
67
        if(empty($val)){
68
            $meta->deleteField($key);
69
        }else{
70
            $meta->setField($key,$val);
71
        }
72
    }
73
74
    $old = @filemtime($src);
75
    if(!file_exists(mediaFN($id, $old)) && file_exists($src)) {
76
        // add old revision to the attic
77
        media_saveOldRevision($id);
78
    }
79
    $filesize_old = filesize($src);
80
    if($meta->save()){
81
        if($conf['fperm']) chmod($src, $conf['fperm']);
82
        @clearstatcache(true, $src);
83
        $new = @filemtime($src);
84
        $filesize_new = filesize($src);
85
        $sizechange = $filesize_new - $filesize_old;
86
87
        // add a log entry to the media changelog
88
        addMediaLogEntry($new, $id, DOKU_CHANGE_TYPE_EDIT, $lang['media_meta_edited'], '', null, $sizechange);
89
90
        msg($lang['metasaveok'],1);
91
        return $id;
92
    }else{
93
        msg($lang['metasaveerr'],-1);
94
        return false;
95
    }
96
}
97
98
/**
99
 * check if a media is external source
100
 *
101
 * @author Gerrit Uitslag <[email protected]>
102
 *
103
 * @param string $id the media ID or URL
104
 * @return bool
105
 */
106
function media_isexternal($id){
107
    if (preg_match('#^(?:https?|ftp)://#i', $id)) return true;
108
    return false;
109
}
110
111
/**
112
 * Check if a media item is public (eg, external URL or readable by @ALL)
113
 *
114
 * @author Andreas Gohr <[email protected]>
115
 *
116
 * @param string $id  the media ID or URL
117
 * @return bool
118
 */
119
function media_ispublic($id){
120
    if(media_isexternal($id)) return true;
121
    $id = cleanID($id);
122
    if(auth_aclcheck(getNS($id).':*', '', array()) >= AUTH_READ) return true;
123
    return false;
124
}
125
126
/**
127
 * Display the form to edit image meta data
128
 *
129
 * @author Andreas Gohr <[email protected]>
130
 * @author Kate Arzamastseva <[email protected]>
131
 *
132
 * @param string $id media id
133
 * @param int $auth permission level
134
 * @return bool
135
 */
136
function media_metaform($id,$auth){
137
    global $lang;
138
139
    if($auth < AUTH_UPLOAD) {
140
        echo '<div class="nothing">'.$lang['media_perm_upload'].'</div>'.NL;
141
        return false;
142
    }
143
144
    // load the field descriptions
145
    static $fields = null;
146
    if(is_null($fields)){
147
        $config_files = getConfigFiles('mediameta');
148
        foreach ($config_files as $config_file) {
149
            if(file_exists($config_file)) include($config_file);
150
        }
151
    }
152
153
    $src = mediaFN($id);
154
155
    // output
156
    $form = new Doku_Form(array('action' => media_managerURL(array('tab_details' => 'view'), '&'),
0 ignored issues
show
Deprecated Code introduced by
The class Doku_Form has been deprecated with message: 2019-07-14

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
157
                                'class' => 'meta'));
158
    $form->addHidden('img', $id);
159
    $form->addHidden('mediado', 'save');
160
    foreach($fields as $key => $field){
0 ignored issues
show
Bug introduced by
The expression $fields of type null is not traversable.
Loading history...
161
        // get current value
162
        if (empty($field[0])) continue;
163
        $tags = array($field[0]);
164
        if(is_array($field[3])) $tags = array_merge($tags,$field[3]);
165
        $value = tpl_img_getTag($tags,'',$src);
166
        $value = cleanText($value);
167
168
        // prepare attributes
169
        $p = array();
170
        $p['class'] = 'edit';
171
        $p['id']    = 'meta__'.$key;
172
        $p['name']  = 'meta['.$field[0].']';
173
        $p_attrs    = array('class' => 'edit');
174
175
        $form->addElement('<div class="row">');
176
        if($field[2] == 'text'){
177
            $form->addElement(
178
                form_makeField(
179
                    'text',
180
                    $p['name'],
181
                    $value,
182
                    ($lang[$field[1]]) ? $lang[$field[1]] : $field[1] . ':',
183
                    $p['id'],
184
                    $p['class'],
185
                    $p_attrs
186
                )
187
            );
188
        }else{
189
            $att = buildAttributes($p);
190
            $form->addElement('<label for="meta__'.$key.'">'.$lang[$field[1]].'</label>');
191
            $form->addElement("<textarea $att rows=\"6\" cols=\"50\">".formText($value).'</textarea>');
192
        }
193
        $form->addElement('</div>'.NL);
194
    }
195
    $form->addElement('<div class="buttons">');
196
    $form->addElement(
197
        form_makeButton(
198
            'submit',
199
            '',
200
            $lang['btn_save'],
201
            array('accesskey' => 's', 'name' => 'mediado[save]')
202
        )
203
    );
204
    $form->addElement('</div>'.NL);
205
    $form->printForm();
206
207
    return true;
208
}
209
210
/**
211
 * Convenience function to check if a media file is still in use
212
 *
213
 * @author Michael Klier <[email protected]>
214
 *
215
 * @param string $id media id
216
 * @return array|bool
217
 */
218
function media_inuse($id) {
219
    global $conf;
220
221
    if($conf['refcheck']){
222
        $mediareferences = ft_mediause($id,true);
223
        if(!count($mediareferences)) {
224
            return false;
225
        } else {
226
            return $mediareferences;
227
        }
228
    } else {
229
        return false;
230
    }
231
}
232
233
/**
234
 * Handles media file deletions
235
 *
236
 * If configured, checks for media references before deletion
237
 *
238
 * @author             Andreas Gohr <[email protected]>
239
 *
240
 * @param string $id media id
241
 * @param int $auth no longer used
242
 * @return int One of: 0,
243
 *                     DOKU_MEDIA_DELETED,
244
 *                     DOKU_MEDIA_DELETED | DOKU_MEDIA_EMPTY_NS,
245
 *                     DOKU_MEDIA_NOT_AUTH,
246
 *                     DOKU_MEDIA_INUSE
247
 */
248
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...
249
    global $lang;
250
    $auth = auth_quickaclcheck(ltrim(getNS($id).':*', ':'));
251
    if($auth < AUTH_DELETE) return DOKU_MEDIA_NOT_AUTH;
252
    if(media_inuse($id)) return DOKU_MEDIA_INUSE;
253
254
    $file = mediaFN($id);
255
256
    // trigger an event - MEDIA_DELETE_FILE
257
    $data = array();
258
    $data['id']   = $id;
259
    $data['name'] = \dokuwiki\Utf8\PhpString::basename($file);
260
    $data['path'] = $file;
261
    $data['size'] = (file_exists($file)) ? filesize($file) : 0;
262
263
    $data['unl'] = false;
264
    $data['del'] = false;
265
    $evt = new Event('MEDIA_DELETE_FILE',$data);
266
    if ($evt->advise_before()) {
267
        $old = @filemtime($file);
268
        if(!file_exists(mediaFN($id, $old)) && file_exists($file)) {
269
            // add old revision to the attic
270
            media_saveOldRevision($id);
271
        }
272
273
        $data['unl'] = @unlink($file);
274
        if($data['unl']) {
275
            $sizechange = 0 - $data['size'];
276
            addMediaLogEntry(time(), $id, DOKU_CHANGE_TYPE_DELETE, $lang['deleted'], '', null, $sizechange);
277
278
            $data['del'] = io_sweepNS($id, 'mediadir');
279
        }
280
    }
281
    $evt->advise_after();
282
    unset($evt);
283
284
    if($data['unl'] && $data['del']){
285
        return DOKU_MEDIA_DELETED | DOKU_MEDIA_EMPTY_NS;
286
    }
287
288
    return $data['unl'] ? DOKU_MEDIA_DELETED : 0;
289
}
290
291
/**
292
 * Handle file uploads via XMLHttpRequest
293
 *
294
 * @param string $ns   target namespace
295
 * @param int    $auth current auth check result
296
 * @return false|string false on error, id of the new file on success
297
 */
298
function media_upload_xhr($ns,$auth){
299
    if(!checkSecurityToken()) return false;
300
    global $INPUT;
301
302
    $id = $INPUT->get->str('qqfile');
303
    list($ext,$mime) = mimetype($id);
304
    $input = fopen("php://input", "r");
305
    if (!($tmp = io_mktmpdir())) return false;
306
    $path = $tmp.'/'.md5($id);
307
    $target = fopen($path, "w");
308
    $realSize = stream_copy_to_stream($input, $target);
309
    fclose($target);
310
    fclose($input);
311
    if (isset($_SERVER["CONTENT_LENGTH"]) && ($realSize != (int)$_SERVER["CONTENT_LENGTH"])){
312
        unlink($path);
313
        return false;
314
    }
315
316
    $res = media_save(
317
        array('name' => $path,
318
            'mime' => $mime,
319
            'ext'  => $ext),
320
        $ns.':'.$id,
321
        (($INPUT->get->str('ow') == 'true') ? true : false),
322
        $auth,
323
        'copy'
324
    );
325
    unlink($path);
326
    if ($tmp) io_rmdir($tmp, true);
327
    if (is_array($res)) {
328
        msg($res[0], $res[1]);
329
        return false;
330
    }
331
    return $res;
332
}
333
334
/**
335
 * Handles media file uploads
336
 *
337
 * @author Andreas Gohr <[email protected]>
338
 * @author Michael Klier <[email protected]>
339
 *
340
 * @param string     $ns    target namespace
341
 * @param int        $auth  current auth check result
342
 * @param bool|array $file  $_FILES member, $_FILES['upload'] if false
343
 * @return false|string false on error, id of the new file on success
344
 */
345
function media_upload($ns,$auth,$file=false){
346
    if(!checkSecurityToken()) return false;
347
    global $lang;
348
    global $INPUT;
349
350
    // get file and id
351
    $id   = $INPUT->post->str('mediaid');
352
    if (!$file) $file = $_FILES['upload'];
353
    if(empty($id)) $id = $file['name'];
354
355
    // check for errors (messages are done in lib/exe/mediamanager.php)
356
    if($file['error']) return false;
357
358
    // check extensions
359
    list($fext,$fmime) = mimetype($file['name']);
360
    list($iext,$imime) = mimetype($id);
361
    if($fext && !$iext){
362
        // no extension specified in id - read original one
363
        $id   .= '.'.$fext;
364
        $imime = $fmime;
365
    }elseif($fext && $fext != $iext){
366
        // extension was changed, print warning
367
        msg(sprintf($lang['mediaextchange'],$fext,$iext));
368
    }
369
370
    $res = media_save(array('name' => $file['tmp_name'],
371
                            'mime' => $imime,
372
                            'ext'  => $iext), $ns.':'.$id,
373
                      $INPUT->post->bool('ow'), $auth, 'copy_uploaded_file');
374
    if (is_array($res)) {
375
        msg($res[0], $res[1]);
376
        return false;
377
    }
378
    return $res;
379
}
380
381
/**
382
 * An alternative to move_uploaded_file that copies
383
 *
384
 * Using copy, makes sure any setgid bits on the media directory are honored
385
 *
386
 * @see   move_uploaded_file()
387
 *
388
 * @param string $from
389
 * @param string $to
390
 * @return bool
391
 */
392
function copy_uploaded_file($from, $to){
393
    if(!is_uploaded_file($from)) return false;
394
    $ok = copy($from, $to);
395
    @unlink($from);
396
    return $ok;
397
}
398
399
/**
400
 * This generates an action event and delegates to _media_upload_action().
401
 * Action plugins are allowed to pre/postprocess the uploaded file.
402
 * (The triggered event is preventable.)
403
 *
404
 * Event data:
405
 * $data[0]     fn_tmp:    the temporary file name (read from $_FILES)
406
 * $data[1]     fn:        the file name of the uploaded file
407
 * $data[2]     id:        the future directory id of the uploaded file
408
 * $data[3]     imime:     the mimetype of the uploaded file
409
 * $data[4]     overwrite: if an existing file is going to be overwritten
410
 * $data[5]     move:      name of function that performs move/copy/..
411
 *
412
 * @triggers MEDIA_UPLOAD_FINISH
413
 *
414
 * @param array  $file
415
 * @param string $id   media id
416
 * @param bool   $ow   overwrite?
417
 * @param int    $auth permission level
418
 * @param string $move name of functions that performs move/copy/..
419
 * @return false|array|string
420
 */
421
function media_save($file, $id, $ow, $auth, $move) {
422
    if($auth < AUTH_UPLOAD) {
423
        return array("You don't have permissions to upload files.", -1);
424
    }
425
426
    if (!isset($file['mime']) || !isset($file['ext'])) {
427
        list($ext, $mime) = mimetype($id);
428
        if (!isset($file['mime'])) {
429
            $file['mime'] = $mime;
430
        }
431
        if (!isset($file['ext'])) {
432
            $file['ext'] = $ext;
433
        }
434
    }
435
436
    global $lang, $conf;
437
438
    // get filename
439
    $id   = cleanID($id);
440
    $fn   = mediaFN($id);
441
442
    // get filetype regexp
443
    $types = array_keys(getMimeTypes());
444
    $types = array_map(
445
        function ($q) {
446
            return preg_quote($q, "/");
447
        },
448
        $types
449
    );
450
    $regex = join('|',$types);
451
452
    // because a temp file was created already
453
    if(!preg_match('/\.('.$regex.')$/i',$fn)) {
454
        return array($lang['uploadwrong'],-1);
455
    }
456
457
    //check for overwrite
458
    $overwrite = file_exists($fn);
459
    $auth_ow = (($conf['mediarevisions']) ? AUTH_UPLOAD : AUTH_DELETE);
460
    if($overwrite && (!$ow || $auth < $auth_ow)) {
461
        return array($lang['uploadexist'], 0);
462
    }
463
    // check for valid content
464
    $ok = media_contentcheck($file['name'], $file['mime']);
465
    if($ok == -1){
466
        return array(sprintf($lang['uploadbadcontent'],'.' . $file['ext']),-1);
467
    }elseif($ok == -2){
468
        return array($lang['uploadspam'],-1);
469
    }elseif($ok == -3){
470
        return array($lang['uploadxss'],-1);
471
    }
472
473
    // prepare event data
474
    $data = array();
475
    $data[0] = $file['name'];
476
    $data[1] = $fn;
477
    $data[2] = $id;
478
    $data[3] = $file['mime'];
479
    $data[4] = $overwrite;
480
    $data[5] = $move;
481
482
    // trigger event
483
    return Event::createAndTrigger('MEDIA_UPLOAD_FINISH', $data, '_media_upload_action', true);
484
}
485
486
/**
487
 * Callback adapter for media_upload_finish() triggered by MEDIA_UPLOAD_FINISH
488
 *
489
 * @author Michael Klier <[email protected]>
490
 *
491
 * @param array $data event data
492
 * @return false|array|string
493
 */
494
function _media_upload_action($data) {
495
    // fixme do further sanity tests of given data?
496
    if(is_array($data) && count($data)===6) {
497
        return media_upload_finish($data[0], $data[1], $data[2], $data[3], $data[4], $data[5]);
498
    } else {
499
        return false; //callback error
500
    }
501
}
502
503
/**
504
 * Saves an uploaded media file
505
 *
506
 * @author Andreas Gohr <[email protected]>
507
 * @author Michael Klier <[email protected]>
508
 * @author Kate Arzamastseva <[email protected]>
509
 *
510
 * @param string $fn_tmp
511
 * @param string $fn
512
 * @param string $id        media id
513
 * @param string $imime     mime type
514
 * @param bool   $overwrite overwrite existing?
515
 * @param string $move      function name
516
 * @return array|string
517
 */
518
function media_upload_finish($fn_tmp, $fn, $id, $imime, $overwrite, $move = 'move_uploaded_file') {
519
    global $conf;
520
    global $lang;
521
    global $REV;
522
523
    $old = @filemtime($fn);
524
    if(!file_exists(mediaFN($id, $old)) && file_exists($fn)) {
525
        // add old revision to the attic if missing
526
        media_saveOldRevision($id);
527
    }
528
529
    // prepare directory
530
    io_createNamespace($id, 'media');
531
532
    $filesize_old = file_exists($fn) ? filesize($fn) : 0;
533
534
    if($move($fn_tmp, $fn)) {
535
        @clearstatcache(true,$fn);
536
        $new = @filemtime($fn);
537
        // Set the correct permission here.
538
        // Always chmod media because they may be saved with different permissions than expected from the php umask.
539
        // (Should normally chmod to $conf['fperm'] only if $conf['fperm'] is set.)
540
        chmod($fn, $conf['fmode']);
541
        msg($lang['uploadsucc'],1);
542
        media_notify($id,$fn,$imime,$old,$new);
543
        // add a log entry to the media changelog
544
        $filesize_new = filesize($fn);
545
        $sizechange = $filesize_new - $filesize_old;
546
        if($REV) {
547
            addMediaLogEntry(
548
                $new,
549
                $id,
550
                DOKU_CHANGE_TYPE_REVERT,
551
                sprintf($lang['restored'], dformat($REV)),
552
                $REV,
553
                null,
554
                $sizechange
555
            );
556
        } elseif($overwrite) {
557
            addMediaLogEntry($new, $id, DOKU_CHANGE_TYPE_EDIT, '', '', null, $sizechange);
558
        } else {
559
            addMediaLogEntry($new, $id, DOKU_CHANGE_TYPE_CREATE, $lang['created'], '', null, $sizechange);
560
        }
561
        return $id;
562
    }else{
563
        return array($lang['uploadfail'],-1);
564
    }
565
}
566
567
/**
568
 * Moves the current version of media file to the media_attic
569
 * directory
570
 *
571
 * @author Kate Arzamastseva <[email protected]>
572
 *
573
 * @param string $id
574
 * @return int - revision date
575
 */
576
function media_saveOldRevision($id){
577
    global $conf, $lang;
578
579
    $oldf = mediaFN($id);
580
    if(!file_exists($oldf)) return '';
581
    $date = filemtime($oldf);
582
    if (!$conf['mediarevisions']) return $date;
583
584
    $medialog = new MediaChangeLog($id);
585
    if (!$medialog->getRevisionInfo($date)) {
586
        // there was an external edit,
587
        // there is no log entry for current version of file
588
        $sizechange = filesize($oldf);
589
        if(!file_exists(mediaMetaFN($id, '.changes'))) {
590
            addMediaLogEntry($date, $id, DOKU_CHANGE_TYPE_CREATE, $lang['created'], '', null, $sizechange);
591
        } else {
592
            $oldRev = $medialog->getRevisions(-1, 1); // from changelog
593
            $oldRev = (int) (empty($oldRev) ? 0 : $oldRev[0]);
594
            $filesize_old = filesize(mediaFN($id, $oldRev));
595
            $sizechange = $sizechange - $filesize_old;
596
597
            addMediaLogEntry($date, $id, DOKU_CHANGE_TYPE_EDIT, '', '', null, $sizechange);
598
        }
599
    }
600
601
    $newf = mediaFN($id,$date);
602
    io_makeFileDir($newf);
603
    if(copy($oldf, $newf)) {
604
        // Set the correct permission here.
605
        // Always chmod media because they may be saved with different permissions than expected from the php umask.
606
        // (Should normally chmod to $conf['fperm'] only if $conf['fperm'] is set.)
607
        chmod($newf, $conf['fmode']);
608
    }
609
    return $date;
610
}
611
612
/**
613
 * This function checks if the uploaded content is really what the
614
 * mimetype says it is. We also do spam checking for text types here.
615
 *
616
 * We need to do this stuff because we can not rely on the browser
617
 * to do this check correctly. Yes, IE is broken as usual.
618
 *
619
 * @author Andreas Gohr <[email protected]>
620
 * @link   http://www.splitbrain.org/blog/2007-02/12-internet_explorer_facilitates_cross_site_scripting
621
 * @fixme  check all 26 magic IE filetypes here?
622
 *
623
 * @param string $file path to file
624
 * @param string $mime mimetype
625
 * @return int
626
 */
627
function media_contentcheck($file,$mime){
628
    global $conf;
629
    if($conf['iexssprotect']){
630
        $fh = @fopen($file, 'rb');
631
        if($fh){
632
            $bytes = fread($fh, 256);
633
            fclose($fh);
634
            if(preg_match('/<(script|a|img|html|body|iframe)[\s>]/i',$bytes)){
635
                return -3; //XSS: possibly malicious content
636
            }
637
        }
638
    }
639
    if(substr($mime,0,6) == 'image/'){
640
        $info = @getimagesize($file);
641
        if($mime == 'image/gif' && $info[2] != 1){
642
            return -1; // uploaded content did not match the file extension
643
        }elseif($mime == 'image/jpeg' && $info[2] != 2){
644
            return -1;
645
        }elseif($mime == 'image/png' && $info[2] != 3){
646
            return -1;
647
        }
648
        # fixme maybe check other images types as well
649
    }elseif(substr($mime,0,5) == 'text/'){
650
        global $TEXT;
651
        $TEXT = io_readFile($file);
652
        if(checkwordblock()){
653
            return -2; //blocked by the spam blacklist
654
        }
655
    }
656
    return 0;
657
}
658
659
/**
660
 * Send a notify mail on uploads
661
 *
662
 * @author Andreas Gohr <[email protected]>
663
 *
664
 * @param string   $id      media id
665
 * @param string   $file    path to file
666
 * @param string   $mime    mime type
667
 * @param bool|int $old_rev revision timestamp or false
668
 * @return bool
669
 */
670
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...
671
    global $conf;
672
    if(empty($conf['notify'])) return false; //notify enabled?
673
674
    $subscription = new MediaSubscriptionSender();
675
    return $subscription->sendMediaDiff($conf['notify'], 'uploadmail', $id, $old_rev, $current_rev);
676
}
677
678
/**
679
 * List all files in a given Media namespace
680
 *
681
 * @param string      $ns             namespace
682
 * @param null|int    $auth           permission level
683
 * @param string      $jump           id
684
 * @param bool        $fullscreenview
685
 * @param bool|string $sort           sorting order, false skips sorting
686
 */
687
function media_filelist($ns,$auth=null,$jump='',$fullscreenview=false,$sort=false){
688
    global $conf;
689
    global $lang;
690
    $ns = cleanID($ns);
691
692
    // check auth our self if not given (needed for ajax calls)
693
    if(is_null($auth)) $auth = auth_quickaclcheck("$ns:*");
694
695
    if (!$fullscreenview) echo '<h1 id="media__ns">:'.hsc($ns).'</h1>'.NL;
696
697
    if($auth < AUTH_READ){
698
        // FIXME: print permission warning here instead?
699
        echo '<div class="nothing">'.$lang['nothingfound'].'</div>'.NL;
700
    }else{
701
        if (!$fullscreenview) {
702
            media_uploadform($ns, $auth);
703
            media_searchform($ns);
704
        }
705
706
        $dir = utf8_encodeFN(str_replace(':','/',$ns));
707
        $data = array();
708
        search($data,$conf['mediadir'],'search_media',
709
                array('showmsg'=>true,'depth'=>1),$dir,1,$sort);
710
711
        if(!count($data)){
712
            echo '<div class="nothing">'.$lang['nothingfound'].'</div>'.NL;
713
        }else {
714
            if ($fullscreenview) {
715
                echo '<ul class="' . _media_get_list_type() . '">';
716
            }
717
            foreach($data as $item){
718
                if (!$fullscreenview) {
719
                    media_printfile($item,$auth,$jump);
720
                } else {
721
                    media_printfile_thumbs($item,$auth,$jump);
722
                }
723
            }
724
            if ($fullscreenview) echo '</ul>'.NL;
725
        }
726
    }
727
}
728
729
/**
730
 * Prints tabs for files list actions
731
 *
732
 * @author Kate Arzamastseva <[email protected]>
733
 * @author Adrian Lang <[email protected]>
734
 *
735
 * @param string $selected_tab - opened tab
736
 */
737
738
function media_tabs_files($selected_tab = ''){
739
    global $lang;
740
    $tabs = array();
741
    foreach(array('files'  => 'mediaselect',
742
                  'upload' => 'media_uploadtab',
743
                  'search' => 'media_searchtab') as $tab => $caption) {
744
        $tabs[$tab] = array('href'    => media_managerURL(array('tab_files' => $tab), '&'),
745
                            'caption' => $lang[$caption]);
746
    }
747
748
    html_tabs($tabs, $selected_tab);
749
}
750
751
/**
752
 * Prints tabs for files details actions
753
 *
754
 * @author Kate Arzamastseva <[email protected]>
755
 * @param string $image filename of the current image
756
 * @param string $selected_tab opened tab
757
 */
758
function media_tabs_details($image, $selected_tab = ''){
759
    global $lang, $conf;
760
761
    $tabs = array();
762
    $tabs['view'] = array('href'    => media_managerURL(array('tab_details' => 'view'), '&'),
763
                          'caption' => $lang['media_viewtab']);
764
765
    list(, $mime) = mimetype($image);
766
    if ($mime == 'image/jpeg' && file_exists(mediaFN($image))) {
767
        $tabs['edit'] = array('href'    => media_managerURL(array('tab_details' => 'edit'), '&'),
768
                              'caption' => $lang['media_edittab']);
769
    }
770
    if ($conf['mediarevisions']) {
771
        $tabs['history'] = array('href'    => media_managerURL(array('tab_details' => 'history'), '&'),
772
                                 'caption' => $lang['media_historytab']);
773
    }
774
775
    html_tabs($tabs, $selected_tab);
776
}
777
778
/**
779
 * Prints options for the tab that displays a list of all files
780
 *
781
 * @author Kate Arzamastseva <[email protected]>
782
 */
783
function media_tab_files_options(){
784
    global $lang;
785
    global $INPUT;
786
    global $ID;
787
    $form = new Doku_Form(array('class' => 'options', 'method' => 'get',
0 ignored issues
show
Deprecated Code introduced by
The class Doku_Form has been deprecated with message: 2019-07-14

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
788
                                'action' => wl($ID)));
789
    $media_manager_params = media_managerURL(array(), '', false, true);
790
    foreach($media_manager_params as $pKey => $pVal){
0 ignored issues
show
Bug introduced by
The expression $media_manager_params of type string|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

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

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

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

Loading history...
791
        $form->addHidden($pKey, $pVal);
792
    }
793
    $form->addHidden('sectok', null);
794
    if ($INPUT->has('q')) {
795
        $form->addHidden('q', $INPUT->str('q'));
796
    }
797
    $form->addElement('<ul>'.NL);
798
    foreach(array('list' => array('listType', array('thumbs', 'rows')),
799
                  'sort' => array('sortBy', array('name', 'date')))
800
            as $group => $content) {
801
        $checked = "_media_get_${group}_type";
802
        $checked = $checked();
803
804
        $form->addElement('<li class="' . $content[0] . '">');
805
        foreach($content[1] as $option) {
806
            $attrs = array();
807
            if ($checked == $option) {
808
                $attrs['checked'] = 'checked';
809
            }
810
            $form->addElement(form_makeRadioField($group . '_dwmedia', $option,
811
                                       $lang['media_' . $group . '_' . $option],
812
                                                  $content[0] . '__' . $option,
813
                                                  $option, $attrs));
814
        }
815
        $form->addElement('</li>'.NL);
816
    }
817
    $form->addElement('<li>');
818
    $form->addElement(form_makeButton('submit', '', $lang['btn_apply']));
819
    $form->addElement('</li>'.NL);
820
    $form->addElement('</ul>'.NL);
821
    $form->printForm();
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);
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">'.NL;
1052
1053
    if($auth >= AUTH_DELETE && !$rev && file_exists(mediaFN($image))){
1054
1055
        // delete button
1056
        $form = new Doku_Form(array('id' => 'mediamanager__btn_delete',
0 ignored issues
show
Deprecated Code introduced by
The class Doku_Form has been deprecated with message: 2019-07-14

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
1057
            'action'=>media_managerURL(array('delete' => $image), '&')));
1058
        $form->addElement(form_makeButton('submit','',$lang['btn_delete']));
1059
        echo '<li>';
1060
        $form->printForm();
1061
        echo '</li>'.NL;
1062
    }
1063
1064
    $auth_ow = (($conf['mediarevisions']) ? AUTH_UPLOAD : AUTH_DELETE);
1065
    if($auth >= $auth_ow && !$rev){
1066
1067
        // upload new version button
1068
        $form = new Doku_Form(array('id' => 'mediamanager__btn_update',
0 ignored issues
show
Deprecated Code introduced by
The class Doku_Form has been deprecated with message: 2019-07-14

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
1069
            'action'=>media_managerURL(array('image' => $image, 'mediado' => 'update'), '&')));
1070
        $form->addElement(form_makeButton('submit','',$lang['media_update']));
1071
        echo '<li>';
1072
        $form->printForm();
1073
        echo '</li>'.NL;
1074
    }
1075
1076
    if($auth >= AUTH_UPLOAD && $rev && $conf['mediarevisions'] && file_exists(mediaFN($image, $rev))){
1077
1078
        // restore button
1079
        $form = new Doku_Form(array('id' => 'mediamanager__btn_restore',
0 ignored issues
show
Deprecated Code introduced by
The class Doku_Form has been deprecated with message: 2019-07-14

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
1080
            'action'=>media_managerURL(array('image' => $image), '&')));
1081
        $form->addHidden('mediado','restore');
1082
        $form->addHidden('rev',$rev);
1083
        $form->addElement(form_makeButton('submit','',$lang['media_restore']));
1084
        echo '<li>';
1085
        $form->printForm();
1086
        echo '</li>'.NL;
1087
    }
1088
1089
    echo '</ul>'.NL;
1090
}
1091
1092
/**
1093
 * Returns image width and height for mediamanager preview panel
1094
 *
1095
 * @author Kate Arzamastseva <[email protected]>
1096
 * @param string         $image
1097
 * @param int|string     $rev
1098
 * @param JpegMeta|bool  $meta
1099
 * @param int            $size
1100
 * @return array|false
1101
 */
1102
function media_image_preview_size($image, $rev, $meta, $size = 500) {
1103
    if (!file_exists(mediaFN($image, $rev))) return false;
1104
 
1105
    if (preg_match("/\.svg$/", $image) ) {
1106
        $xml = simplexml_load_file(mediaFN($image, $rev));
1107
        $attr = $xml->attributes();
1108
        $w = (int) $attr->width ;
1109
        $h = (int) $attr->height ;
1110
    } else if (preg_match("/\.(jpe?g|gif|png)$/", $image)  ) {
1111
        $info = getimagesize(mediaFN($image, $rev));
1112
        $w = (int) $info[0];
1113
        $h = (int) $info[1];
1114
    } else return false;
1115
1116
    if($meta && ($w > $size || $h > $size)){
1117
        $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...
1118
        $w = floor($w * $ratio);
1119
        $h = floor($h * $ratio);
1120
    }
1121
    return array($w, $h);
1122
}
1123
1124
/**
1125
 * Returns the requested EXIF/IPTC tag from the image meta
1126
 *
1127
 * @author Kate Arzamastseva <[email protected]>
1128
 *
1129
 * @param array    $tags array with tags, first existing is returned
1130
 * @param JpegMeta $meta
1131
 * @param string   $alt  alternative value
1132
 * @return string
1133
 */
1134
function media_getTag($tags,$meta,$alt=''){
1135
    if($meta === false) return $alt;
1136
    $info = $meta->getField($tags);
1137
    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...
1138
    return $info;
1139
}
1140
1141
/**
1142
 * Returns mediafile tags
1143
 *
1144
 * @author Kate Arzamastseva <[email protected]>
1145
 *
1146
 * @param JpegMeta $meta
1147
 * @return array list of tags of the mediafile
1148
 */
1149
function media_file_tags($meta) {
1150
    // load the field descriptions
1151
    static $fields = null;
1152
    if(is_null($fields)){
1153
        $config_files = getConfigFiles('mediameta');
1154
        foreach ($config_files as $config_file) {
1155
            if(file_exists($config_file)) include($config_file);
1156
        }
1157
    }
1158
1159
    $tags = array();
1160
1161
    foreach($fields as $key => $tag){
0 ignored issues
show
Bug introduced by
The expression $fields of type null is not traversable.
Loading history...
1162
        $t = array();
1163
        if (!empty($tag[0])) $t = array($tag[0]);
1164
        if(isset($tag[3]) && is_array($tag[3])) $t = array_merge($t,$tag[3]);
1165
        $value = media_getTag($t, $meta);
1166
        $tags[] = array('tag' => $tag, 'value' => $value);
1167
    }
1168
1169
    return $tags;
1170
}
1171
1172
/**
1173
 * Prints mediafile tags
1174
 *
1175
 * @author Kate Arzamastseva <[email protected]>
1176
 *
1177
 * @param string        $image image id
1178
 * @param int           $auth  permission level
1179
 * @param string|int    $rev   revision timestamp, or empty string
1180
 * @param bool|JpegMeta $meta  image object, or create one if false
1181
 */
1182
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...
1183
    global $lang;
1184
1185
    if (!$meta) $meta = new JpegMeta(mediaFN($image, $rev));
1186
    $tags = media_file_tags($meta);
0 ignored issues
show
Bug introduced by
It seems like $meta defined by parameter $meta on line 1182 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...
1187
1188
    echo '<dl>'.NL;
1189
    foreach($tags as $tag){
1190
        if ($tag['value']) {
1191
            $value = cleanText($tag['value']);
1192
            echo '<dt>'.$lang[$tag['tag'][1]].'</dt><dd>';
1193
            if ($tag['tag'][2] == 'date') echo dformat($value);
1194
            else echo hsc($value);
1195
            echo '</dd>'.NL;
1196
        }
1197
    }
1198
    echo '</dl>'.NL;
1199
    echo '<dl>'.NL;
1200
    echo '<dt>'.$lang['reference'].':</dt>';
1201
    $media_usage = ft_mediause($image,true);
1202
    if(count($media_usage) > 0){
1203
        foreach($media_usage as $path){
1204
            echo '<dd>'.html_wikilink($path).'</dd>';
1205
        }
1206
    }else{
1207
        echo '<dd>'.$lang['nothingfound'].'</dd>';
1208
    }
1209
    echo '</dl>'.NL;
1210
1211
}
1212
1213
/**
1214
 * Shows difference between two revisions of file
1215
 *
1216
 * @author Kate Arzamastseva <[email protected]>
1217
 *
1218
 * @param string $image  image id
1219
 * @param string $ns
1220
 * @param int $auth permission level
1221
 * @param bool $fromajax
1222
 * @return false|null|string
1223
 */
1224
function media_diff($image, $ns, $auth, $fromajax = false) {
1225
    global $conf;
1226
    global $INPUT;
1227
1228
    if ($auth < AUTH_READ || !$image || !$conf['mediarevisions']) return '';
1229
1230
    $rev1 = $INPUT->int('rev');
1231
1232
    $rev2 = $INPUT->ref('rev2');
1233
    if(is_array($rev2)){
1234
        $rev1 = (int) $rev2[0];
1235
        $rev2 = (int) $rev2[1];
1236
1237
        if(!$rev1){
1238
            $rev1 = $rev2;
1239
            unset($rev2);
1240
        }
1241
    }else{
1242
        $rev2 = $INPUT->int('rev2');
1243
    }
1244
1245
    if ($rev1 && !file_exists(mediaFN($image, $rev1))) $rev1 = false;
1246
    if ($rev2 && !file_exists(mediaFN($image, $rev2))) $rev2 = false;
1247
1248
    if($rev1 && $rev2){            // two specific revisions wanted
1249
        // make sure order is correct (older on the left)
1250
        if($rev1 < $rev2){
1251
            $l_rev = $rev1;
1252
            $r_rev = $rev2;
1253
        }else{
1254
            $l_rev = $rev2;
1255
            $r_rev = $rev1;
1256
        }
1257
    }elseif($rev1){                // single revision given, compare to current
1258
        $r_rev = '';
1259
        $l_rev = $rev1;
1260
    }else{                        // no revision was given, compare previous to current
1261
        $r_rev = '';
1262
        $medialog = new MediaChangeLog($image);
1263
        $revs = $medialog->getRevisions(0, 1);
1264
        if (file_exists(mediaFN($image, $revs[0]))) {
1265
            $l_rev = $revs[0];
1266
        } else {
1267
            $l_rev = '';
1268
        }
1269
    }
1270
1271
    // prepare event data
1272
    $data = array();
1273
    $data[0] = $image;
1274
    $data[1] = $l_rev;
1275
    $data[2] = $r_rev;
1276
    $data[3] = $ns;
1277
    $data[4] = $auth;
1278
    $data[5] = $fromajax;
1279
1280
    // trigger event
1281
    return Event::createAndTrigger('MEDIA_DIFF', $data, '_media_file_diff', true);
1282
}
1283
1284
/**
1285
 * Callback for media file diff
1286
 *
1287
 * @param array $data event data
1288
 * @return false|null
1289
 */
1290
function _media_file_diff($data) {
1291
    if(is_array($data) && count($data)===6) {
1292
        media_file_diff($data[0], $data[1], $data[2], $data[3], $data[4], $data[5]);
1293
    } else {
1294
        return false;
1295
    }
1296
}
1297
1298
/**
1299
 * Shows difference between two revisions of image
1300
 *
1301
 * @author Kate Arzamastseva <[email protected]>
1302
 *
1303
 * @param string $image
1304
 * @param string|int $l_rev revision timestamp, or empty string
1305
 * @param string|int $r_rev revision timestamp, or empty string
1306
 * @param string $ns
1307
 * @param int $auth permission level
1308
 * @param bool $fromajax
1309
 */
1310
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...
1311
    global $lang;
1312
    global $INPUT;
1313
1314
    $l_meta = new JpegMeta(mediaFN($image, $l_rev));
1315
    $r_meta = new JpegMeta(mediaFN($image, $r_rev));
1316
1317
    $is_img = preg_match('/\.(jpe?g|gif|png)$/', $image);
1318
    if ($is_img) {
1319
        $l_size = media_image_preview_size($image, $l_rev, $l_meta);
1320
        $r_size = media_image_preview_size($image, $r_rev, $r_meta);
1321
        $is_img = ($l_size && $r_size && ($l_size[0] >= 30 || $r_size[0] >= 30));
1322
1323
        $difftype = $INPUT->str('difftype');
1324
1325
        if (!$fromajax) {
1326
            $form = new Doku_Form(array(
0 ignored issues
show
Deprecated Code introduced by
The class Doku_Form has been deprecated with message: 2019-07-14

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
1327
                'action' => media_managerURL(array(), '&'),
1328
                'method' => 'get',
1329
                'id' => 'mediamanager__form_diffview',
1330
                'class' => 'diffView'
1331
            ));
1332
            $form->addHidden('sectok', null);
1333
            $form->addElement('<input type="hidden" name="rev2[]" value="'.$l_rev.'" ></input>');
1334
            $form->addElement('<input type="hidden" name="rev2[]" value="'.$r_rev.'" ></input>');
1335
            $form->addHidden('mediado', 'diff');
1336
            $form->printForm();
1337
1338
            echo NL.'<div id="mediamanager__diff" >'.NL;
1339
        }
1340
1341
        if ($difftype == 'opacity' || $difftype == 'portions') {
1342
            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 1319 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 1320 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...
1343
            if (!$fromajax) echo '</div>';
1344
            return;
1345
        }
1346
    }
1347
1348
    list($l_head, $r_head) = html_diff_head($l_rev, $r_rev, $image, true);
1349
1350
    ?>
1351
    <div class="table">
1352
    <table>
1353
      <tr>
1354
        <th><?php echo $l_head; ?></th>
1355
        <th><?php echo $r_head; ?></th>
1356
      </tr>
1357
    <?php
1358
1359
    echo '<tr class="image">';
1360
    echo '<td>';
1361
    media_preview($image, $auth, $l_rev, $l_meta);
1362
    echo '</td>';
1363
1364
    echo '<td>';
1365
    media_preview($image, $auth, $r_rev, $r_meta);
1366
    echo '</td>';
1367
    echo '</tr>'.NL;
1368
1369
    echo '<tr class="actions">';
1370
    echo '<td>';
1371
    media_preview_buttons($image, $auth, $l_rev);
1372
    echo '</td>';
1373
1374
    echo '<td>';
1375
    media_preview_buttons($image, $auth, $r_rev);
1376
    echo '</td>';
1377
    echo '</tr>'.NL;
1378
1379
    $l_tags = media_file_tags($l_meta);
1380
    $r_tags = media_file_tags($r_meta);
1381
    // FIXME r_tags-only stuff
1382
    foreach ($l_tags as $key => $l_tag) {
1383
        if ($l_tag['value'] != $r_tags[$key]['value']) {
1384
            $r_tags[$key]['highlighted'] = true;
1385
            $l_tags[$key]['highlighted'] = true;
1386
        } else if (!$l_tag['value'] || !$r_tags[$key]['value']) {
1387
            unset($r_tags[$key]);
1388
            unset($l_tags[$key]);
1389
        }
1390
    }
1391
1392
    echo '<tr>';
1393
    foreach(array($l_tags,$r_tags) as $tags){
1394
        echo '<td>'.NL;
1395
1396
        echo '<dl class="img_tags">';
1397
        foreach($tags as $tag){
1398
            $value = cleanText($tag['value']);
1399
            if (!$value) $value = '-';
1400
            echo '<dt>'.$lang[$tag['tag'][1]].'</dt>';
1401
            echo '<dd>';
1402
            if ($tag['highlighted']) {
1403
                echo '<strong>';
1404
            }
1405
            if ($tag['tag'][2] == 'date') echo dformat($value);
1406
            else echo hsc($value);
1407
            if ($tag['highlighted']) {
1408
                echo '</strong>';
1409
            }
1410
            echo '</dd>';
1411
        }
1412
        echo '</dl>'.NL;
1413
1414
        echo '</td>';
1415
    }
1416
    echo '</tr>'.NL;
1417
1418
    echo '</table>'.NL;
1419
    echo '</div>'.NL;
1420
1421
    if ($is_img && !$fromajax) echo '</div>';
1422
}
1423
1424
/**
1425
 * Prints two images side by side
1426
 * and slider
1427
 *
1428
 * @author Kate Arzamastseva <[email protected]>
1429
 *
1430
 * @param string $image   image id
1431
 * @param int    $l_rev   revision timestamp, or empty string
1432
 * @param int    $r_rev   revision timestamp, or empty string
1433
 * @param array  $l_size  array with width and height
1434
 * @param array  $r_size  array with width and height
1435
 * @param string $type
1436
 */
1437
function media_image_diff($image, $l_rev, $r_rev, $l_size, $r_size, $type) {
1438
    if ($l_size != $r_size) {
1439
        if ($r_size[0] > $l_size[0]) {
1440
            $l_size = $r_size;
1441
        }
1442
    }
1443
1444
    $l_more = array('rev' => $l_rev, 'h' => $l_size[1], 'w' => $l_size[0]);
1445
    $r_more = array('rev' => $r_rev, 'h' => $l_size[1], 'w' => $l_size[0]);
1446
1447
    $l_src = ml($image, $l_more);
1448
    $r_src = ml($image, $r_more);
1449
1450
    // slider
1451
    echo '<div class="slider" style="max-width: '.($l_size[0]-20).'px;" ></div>'.NL;
1452
1453
    // two images in divs
1454
    echo '<div class="imageDiff ' . $type . '">'.NL;
1455
    echo '<div class="image1" style="max-width: '.$l_size[0].'px;">';
1456
    echo '<img src="'.$l_src.'" alt="" />';
1457
    echo '</div>'.NL;
1458
    echo '<div class="image2" style="max-width: '.$l_size[0].'px;">';
1459
    echo '<img src="'.$r_src.'" alt="" />';
1460
    echo '</div>'.NL;
1461
    echo '</div>'.NL;
1462
}
1463
1464
/**
1465
 * Restores an old revision of a media file
1466
 *
1467
 * @param string $image media id
1468
 * @param int    $rev   revision timestamp or empty string
1469
 * @param int    $auth
1470
 * @return string - file's id
1471
 *
1472
 * @author Kate Arzamastseva <[email protected]>
1473
 */
1474
function media_restore($image, $rev, $auth){
1475
    global $conf;
1476
    if ($auth < AUTH_UPLOAD || !$conf['mediarevisions']) return false;
1477
    $removed = (!file_exists(mediaFN($image)) && file_exists(mediaMetaFN($image, '.changes')));
1478
    if (!$image || (!file_exists(mediaFN($image)) && !$removed)) return false;
1479
    if (!$rev || !file_exists(mediaFN($image, $rev))) return false;
1480
    list(,$imime,) = mimetype($image);
1481
    $res = media_upload_finish(mediaFN($image, $rev),
1482
        mediaFN($image),
1483
        $image,
1484
        $imime,
1485
        true,
1486
        'copy');
1487
    if (is_array($res)) {
1488
        msg($res[0], $res[1]);
1489
        return false;
1490
    }
1491
    return $res;
1492
}
1493
1494
/**
1495
 * List all files found by the search request
1496
 *
1497
 * @author Tobias Sarnowski <[email protected]>
1498
 * @author Andreas Gohr <[email protected]>
1499
 * @author Kate Arzamastseva <[email protected]>
1500
 * @triggers MEDIA_SEARCH
1501
 *
1502
 * @param string $query
1503
 * @param string $ns
1504
 * @param null|int $auth
1505
 * @param bool $fullscreen
1506
 * @param string $sort
1507
 */
1508
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...
1509
    global $conf;
1510
    global $lang;
1511
1512
    $ns = cleanID($ns);
1513
    $evdata = array(
1514
        'ns'    => $ns,
1515
        'data'  => array(),
1516
        'query' => $query
1517
    );
1518
    if (!blank($query)) {
1519
        $evt = new Event('MEDIA_SEARCH', $evdata);
1520
        if ($evt->advise_before()) {
1521
            $dir = utf8_encodeFN(str_replace(':','/',$evdata['ns']));
1522
            $quoted = preg_quote($evdata['query'],'/');
1523
            //apply globbing
1524
            $quoted = str_replace(array('\*', '\?'), array('.*', '.'), $quoted, $count);
1525
1526
            //if we use globbing file name must match entirely but may be preceded by arbitrary namespace
1527
            if ($count > 0) $quoted = '^([^:]*:)*'.$quoted.'$';
1528
1529
            $pattern = '/'.$quoted.'/i';
1530
            search($evdata['data'],
1531
                    $conf['mediadir'],
1532
                    'search_media',
1533
                    array('showmsg'=>false,'pattern'=>$pattern),
1534
                    $dir,
1535
                    1,
1536
                    $sort);
1537
        }
1538
        $evt->advise_after();
1539
        unset($evt);
1540
    }
1541
1542
    if (!$fullscreen) {
1543
        echo '<h1 id="media__ns">'.sprintf($lang['searchmedia_in'],hsc($ns).':*').'</h1>'.NL;
1544
        media_searchform($ns,$query);
1545
    }
1546
1547
    if(!count($evdata['data'])){
1548
        echo '<div class="nothing">'.$lang['nothingfound'].'</div>'.NL;
1549
    }else {
1550
        if ($fullscreen) {
1551
            echo '<ul class="' . _media_get_list_type() . '">';
1552
        }
1553
        foreach($evdata['data'] as $item){
1554
            if (!$fullscreen) media_printfile($item,$item['perm'],'',true);
1555
            else media_printfile_thumbs($item,$item['perm'],false,true);
1556
        }
1557
        if ($fullscreen) echo '</ul>'.NL;
1558
    }
1559
}
1560
1561
/**
1562
 * Formats and prints one file in the list
1563
 *
1564
 * @param array     $item
1565
 * @param int       $auth              permission level
1566
 * @param string    $jump              item id
1567
 * @param bool      $display_namespace
1568
 */
1569
function media_printfile($item,$auth,$jump,$display_namespace=false){
1570
    global $lang;
1571
1572
    // Prepare zebra coloring
1573
    // I always wanted to use this variable name :-D
1574
    static $twibble = 1;
1575
    $twibble *= -1;
1576
    $zebra = ($twibble == -1) ? 'odd' : 'even';
1577
1578
    // Automatically jump to recent action
1579
    if($jump == $item['id']) {
1580
        $jump = ' id="scroll__here" ';
1581
    }else{
1582
        $jump = '';
1583
    }
1584
1585
    // Prepare fileicons
1586
    list($ext) = mimetype($item['file'],false);
1587
    $class = preg_replace('/[^_\-a-z0-9]+/i','_',$ext);
1588
    $class = 'select mediafile mf_'.$class;
1589
1590
    // Prepare filename
1591
    $file = utf8_decodeFN($item['file']);
1592
1593
    // Prepare info
1594
    $info = '';
1595
    if($item['isimg']){
1596
        $info .= (int) $item['meta']->getField('File.Width');
1597
        $info .= '&#215;';
1598
        $info .= (int) $item['meta']->getField('File.Height');
1599
        $info .= ' ';
1600
    }
1601
    $info .= '<i>'.dformat($item['mtime']).'</i>';
1602
    $info .= ' ';
1603
    $info .= filesize_h($item['size']);
1604
1605
    // output
1606
    echo '<div class="'.$zebra.'"'.$jump.' title="'.hsc($item['id']).'">'.NL;
1607
    if (!$display_namespace) {
1608
        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 1591 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...
1609
    } else {
1610
        echo '<a id="h_:'.$item['id'].'" class="'.$class.'">'.hsc($item['id']).'</a><br/>';
1611
    }
1612
    echo '<span class="info">('.$info.')</span>'.NL;
1613
1614
    // view button
1615
    $link = ml($item['id'],'',true);
1616
    echo ' <a href="'.$link.'" target="_blank"><img src="'.DOKU_BASE.'lib/images/magnifier.png" '.
1617
        'alt="'.$lang['mediaview'].'" title="'.$lang['mediaview'].'" class="btn" /></a>';
1618
1619
    // mediamanager button
1620
    $link = wl('',array('do'=>'media','image'=>$item['id'],'ns'=>getNS($item['id'])));
1621
    echo ' <a href="'.$link.'" target="_blank"><img src="'.DOKU_BASE.'lib/images/mediamanager.png" '.
1622
        'alt="'.$lang['btn_media'].'" title="'.$lang['btn_media'].'" class="btn" /></a>';
1623
1624
    // delete button
1625
    if($item['writable'] && $auth >= AUTH_DELETE){
1626
        $link = DOKU_BASE.'lib/exe/mediamanager.php?delete='.rawurlencode($item['id']).
1627
            '&amp;sectok='.getSecurityToken();
1628
        echo ' <a href="'.$link.'" class="btn_media_delete" title="'.$item['id'].'">'.
1629
            '<img src="'.DOKU_BASE.'lib/images/trash.png" alt="'.$lang['btn_delete'].'" '.
1630
            'title="'.$lang['btn_delete'].'" class="btn" /></a>';
1631
    }
1632
1633
    echo '<div class="example" id="ex_'.str_replace(':','_',$item['id']).'">';
1634
    echo $lang['mediausage'].' <code>{{:'.$item['id'].'}}</code>';
1635
    echo '</div>';
1636
    if($item['isimg']) media_printimgdetail($item);
1637
    echo '<div class="clearer"></div>'.NL;
1638
    echo '</div>'.NL;
1639
}
1640
1641
/**
1642
 * Display a media icon
1643
 *
1644
 * @param string $filename media id
1645
 * @param string $size     the size subfolder, if not specified 16x16 is used
1646
 * @return string html
1647
 */
1648
function media_printicon($filename, $size=''){
1649
    list($ext) = mimetype(mediaFN($filename),false);
1650
1651
    if (file_exists(DOKU_INC.'lib/images/fileicons/'.$size.'/'.$ext.'.png')) {
1652
        $icon = DOKU_BASE.'lib/images/fileicons/'.$size.'/'.$ext.'.png';
1653
    } else {
1654
        $icon = DOKU_BASE.'lib/images/fileicons/'.$size.'/file.png';
1655
    }
1656
1657
    return '<img src="'.$icon.'" alt="'.$filename.'" class="icon" />';
1658
}
1659
1660
/**
1661
 * Formats and prints one file in the list in the thumbnails view
1662
 *
1663
 * @author Kate Arzamastseva <[email protected]>
1664
 *
1665
 * @param array       $item
1666
 * @param int         $auth              permission level
1667
 * @param bool|string $jump              item id
1668
 * @param bool        $display_namespace
1669
 */
1670
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...
1671
1672
    // Prepare filename
1673
    $file = utf8_decodeFN($item['file']);
1674
1675
    // output
1676
    echo '<li><dl title="'.hsc($item['id']).'">'.NL;
1677
1678
        echo '<dt>';
1679
    if($item['isimg']) {
1680
        media_printimgdetail($item, true);
1681
1682
    } else {
1683
        echo '<a id="d_:'.$item['id'].'" class="image" title="'.$item['id'].'" href="'.
1684
            media_managerURL(array('image' => hsc($item['id']), 'ns' => getNS($item['id']),
1685
            'tab_details' => 'view')).'">';
1686
        echo media_printicon($item['id'], '32x32');
1687
        echo '</a>';
1688
    }
1689
    echo '</dt>'.NL;
1690
    if (!$display_namespace) {
1691
        $name = hsc($file);
0 ignored issues
show
Security Bug introduced by
It seems like $file defined by utf8_decodeFN($item['file']) on line 1673 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...
1692
    } else {
1693
        $name = hsc($item['id']);
1694
    }
1695
    echo '<dd class="name"><a href="'.media_managerURL(array('image' => hsc($item['id']), 'ns' => getNS($item['id']),
1696
        'tab_details' => 'view')).'" id="h_:'.$item['id'].'">'.$name.'</a></dd>'.NL;
1697
1698
    if($item['isimg']){
1699
        $size = '';
1700
        $size .= (int) $item['meta']->getField('File.Width');
1701
        $size .= '&#215;';
1702
        $size .= (int) $item['meta']->getField('File.Height');
1703
        echo '<dd class="size">'.$size.'</dd>'.NL;
1704
    } else {
1705
        echo '<dd class="size">&#160;</dd>'.NL;
1706
    }
1707
    $date = dformat($item['mtime']);
1708
    echo '<dd class="date">'.$date.'</dd>'.NL;
1709
    $filesize = filesize_h($item['size']);
1710
    echo '<dd class="filesize">'.$filesize.'</dd>'.NL;
1711
    echo '</dl></li>'.NL;
1712
}
1713
1714
/**
1715
 * Prints a thumbnail and metainfo
1716
 *
1717
 * @param array $item
1718
 * @param bool  $fullscreen
1719
 */
1720
function media_printimgdetail($item, $fullscreen=false){
1721
    // prepare thumbnail
1722
    $size = $fullscreen ? 90 : 120;
1723
1724
    $w = (int) $item['meta']->getField('File.Width');
1725
    $h = (int) $item['meta']->getField('File.Height');
1726
    if($w>$size || $h>$size){
1727
        if (!$fullscreen) {
1728
            $ratio = $item['meta']->getResizeRatio($size);
1729
        } else {
1730
            $ratio = $item['meta']->getResizeRatio($size,$size);
1731
        }
1732
        $w = floor($w * $ratio);
1733
        $h = floor($h * $ratio);
1734
    }
1735
    $src = ml($item['id'],array('w'=>$w,'h'=>$h,'t'=>$item['mtime']));
1736
    $p = array();
1737
    if (!$fullscreen) {
1738
        // In fullscreen mediamanager view, image resizing is done via CSS.
1739
        $p['width']  = $w;
1740
        $p['height'] = $h;
1741
    }
1742
    $p['alt']    = $item['id'];
1743
    $att = buildAttributes($p);
1744
1745
    // output
1746
    if ($fullscreen) {
1747
        echo '<a id="l_:'.$item['id'].'" class="image thumb" href="'.
1748
            media_managerURL(['image' => hsc($item['id']), 'ns' => getNS($item['id']), 'tab_details' => 'view']).'">';
1749
        echo '<img src="'.$src.'" '.$att.' />';
1750
        echo '</a>';
1751
    }
1752
1753
    if ($fullscreen) return;
1754
1755
    echo '<div class="detail">';
1756
    echo '<div class="thumb">';
1757
    echo '<a id="d_:'.$item['id'].'" class="select">';
1758
    echo '<img src="'.$src.'" '.$att.' />';
1759
    echo '</a>';
1760
    echo '</div>';
1761
1762
    // read EXIF/IPTC data
1763
    $t = $item['meta']->getField(array('IPTC.Headline','xmp.dc:title'));
1764
    $d = $item['meta']->getField(array('IPTC.Caption','EXIF.UserComment',
1765
                'EXIF.TIFFImageDescription',
1766
                'EXIF.TIFFUserComment'));
1767
    if(\dokuwiki\Utf8\PhpString::strlen($d) > 250) $d = \dokuwiki\Utf8\PhpString::substr($d,0,250).'...';
1768
    $k = $item['meta']->getField(array('IPTC.Keywords','IPTC.Category','xmp.dc:subject'));
1769
1770
    // print EXIF/IPTC data
1771
    if($t || $d || $k ){
1772
        echo '<p>';
1773
        if($t) echo '<strong>'.hsc($t).'</strong><br />';
1774
        if($d) echo hsc($d).'<br />';
1775
        if($t) echo '<em>'.hsc($k).'</em>';
1776
        echo '</p>';
1777
    }
1778
    echo '</div>';
1779
}
1780
1781
/**
1782
 * Build link based on the current, adding/rewriting parameters
1783
 *
1784
 * @author Kate Arzamastseva <[email protected]>
1785
 *
1786
 * @param array|bool $params
1787
 * @param string     $amp           separator
1788
 * @param bool       $abs           absolute url?
1789
 * @param bool       $params_array  return the parmeters array?
1790
 * @return string|array - link or link parameters
1791
 */
1792
function media_managerURL($params=false, $amp='&amp;', $abs=false, $params_array=false) {
1793
    global $ID;
1794
    global $INPUT;
1795
1796
    $gets = array('do' => 'media');
1797
    $media_manager_params = array('tab_files', 'tab_details', 'image', 'ns', 'list', 'sort');
1798
    foreach ($media_manager_params as $x) {
1799
        if ($INPUT->has($x)) $gets[$x] = $INPUT->str($x);
1800
    }
1801
1802
    if ($params) {
1803
        $gets = $params + $gets;
1804
    }
1805
    unset($gets['id']);
1806
    if (isset($gets['delete'])) {
1807
        unset($gets['image']);
1808
        unset($gets['tab_details']);
1809
    }
1810
1811
    if ($params_array) return $gets;
1812
1813
    return wl($ID,$gets,$abs,$amp);
1814
}
1815
1816
/**
1817
 * Print the media upload form if permissions are correct
1818
 *
1819
 * @author Andreas Gohr <[email protected]>
1820
 * @author Kate Arzamastseva <[email protected]>
1821
 *
1822
 * @param string $ns
1823
 * @param int    $auth permission level
1824
 * @param bool  $fullscreen
1825
 */
1826
function media_uploadform($ns, $auth, $fullscreen = false){
1827
    global $lang;
1828
    global $conf;
1829
    global $INPUT;
1830
1831
    if($auth < AUTH_UPLOAD) {
1832
        echo '<div class="nothing">'.$lang['media_perm_upload'].'</div>'.NL;
1833
        return;
1834
    }
1835
    $auth_ow = (($conf['mediarevisions']) ? AUTH_UPLOAD : AUTH_DELETE);
1836
1837
    $update = false;
1838
    $id = '';
1839
    if ($auth >= $auth_ow && $fullscreen && $INPUT->str('mediado') == 'update') {
1840
        $update = true;
1841
        $id = cleanID($INPUT->str('image'));
1842
    }
1843
1844
    // The default HTML upload form
1845
    $params = array('id'      => 'dw__upload',
1846
                    'enctype' => 'multipart/form-data');
1847
    if (!$fullscreen) {
1848
        $params['action'] = DOKU_BASE.'lib/exe/mediamanager.php';
1849
    } else {
1850
        $params['action'] = media_managerURL(array('tab_files' => 'files',
1851
            'tab_details' => 'view'), '&');
1852
    }
1853
1854
    $form = new Doku_Form($params);
0 ignored issues
show
Deprecated Code introduced by
The class Doku_Form has been deprecated with message: 2019-07-14

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
1855
    if (!$fullscreen) echo '<div class="upload">' . $lang['mediaupload'] . '</div>';
1856
    $form->addElement(formSecurityToken());
1857
    $form->addHidden('ns', hsc($ns));
1858
    $form->addElement(form_makeOpenTag('p'));
1859
    $form->addElement(form_makeFileField('upload', $lang['txt_upload'], 'upload__file'));
1860
    $form->addElement(form_makeCloseTag('p'));
1861
    $form->addElement(form_makeOpenTag('p'));
1862
    $form->addElement(form_makeTextField('mediaid', noNS($id), $lang['txt_filename'], 'upload__name'));
1863
    $form->addElement(form_makeButton('submit', '', $lang['btn_upload']));
1864
    $form->addElement(form_makeCloseTag('p'));
1865
1866
    if($auth >= $auth_ow){
1867
        $form->addElement(form_makeOpenTag('p'));
1868
        $attrs = array();
1869
        if ($update) $attrs['checked'] = 'checked';
1870
        $form->addElement(form_makeCheckboxField('ow', 1, $lang['txt_overwrt'], 'dw__ow', 'check', $attrs));
1871
        $form->addElement(form_makeCloseTag('p'));
1872
    }
1873
1874
    echo NL.'<div id="mediamanager__uploader">'.NL;
1875
    html_form('upload', $form);
1876
1877
    echo '</div>'.NL;
1878
1879
    echo '<p class="maxsize">';
1880
    printf($lang['maxuploadsize'],filesize_h(media_getuploadsize()));
1881
    echo ' <a class="allowedmime" href="#">' . $lang['allowedmime'] . '</a>';
1882
    echo ' <span>' . implode(', ', array_keys(getMimeTypes())) .'</span>';
1883
    echo '</p>'.NL;
1884
}
1885
1886
/**
1887
 * Returns the size uploaded files may have
1888
 *
1889
 * This uses a conservative approach using the lowest number found
1890
 * in any of the limiting ini settings
1891
 *
1892
 * @returns int size in bytes
1893
 */
1894
function media_getuploadsize(){
1895
    $okay = 0;
1896
1897
    $post = (int) php_to_byte(@ini_get('post_max_size'));
1898
    $suho = (int) php_to_byte(@ini_get('suhosin.post.max_value_length'));
1899
    $upld = (int) php_to_byte(@ini_get('upload_max_filesize'));
1900
1901
    if($post && ($post < $okay || $okay == 0)) $okay = $post;
1902
    if($suho && ($suho < $okay || $okay == 0)) $okay = $suho;
1903
    if($upld && ($upld < $okay || $okay == 0)) $okay = $upld;
1904
1905
    return $okay;
1906
}
1907
1908
/**
1909
 * Print the search field form
1910
 *
1911
 * @author Tobias Sarnowski <[email protected]>
1912
 * @author Kate Arzamastseva <[email protected]>
1913
 *
1914
 * @param string $ns
1915
 * @param string $query
1916
 * @param bool $fullscreen
1917
 */
1918
function media_searchform($ns,$query='',$fullscreen=false){
1919
    global $lang;
1920
1921
    // The default HTML search form
1922
    $params = array('id' => 'dw__mediasearch');
1923
    if (!$fullscreen) {
1924
        $params['action'] = DOKU_BASE.'lib/exe/mediamanager.php';
1925
    } else {
1926
        $params['action'] = media_managerURL(array(), '&');
1927
    }
1928
    $form = new Doku_Form($params);
0 ignored issues
show
Deprecated Code introduced by
The class Doku_Form has been deprecated with message: 2019-07-14

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
1929
    $form->addHidden('ns', $ns);
1930
    $form->addHidden($fullscreen ? 'mediado' : 'do', 'searchlist');
1931
1932
    $form->addElement(form_makeOpenTag('p'));
1933
    $form->addElement(
1934
        form_makeTextField(
1935
            'q',
1936
            $query,
1937
            $lang['searchmedia'],
1938
            '',
1939
            '',
1940
            array('title' => sprintf($lang['searchmedia_in'], hsc($ns) . ':*'))
1941
        )
1942
    );
1943
    $form->addElement(form_makeButton('submit', '', $lang['btn_search']));
1944
    $form->addElement(form_makeCloseTag('p'));
1945
    html_form('searchmedia', $form);
1946
}
1947
1948
/**
1949
 * Build a tree outline of available media namespaces
1950
 *
1951
 * @author Andreas Gohr <[email protected]>
1952
 *
1953
 * @param string $ns
1954
 */
1955
function media_nstree($ns){
1956
    global $conf;
1957
    global $lang;
1958
1959
    // currently selected namespace
1960
    $ns  = cleanID($ns);
1961
    if(empty($ns)){
1962
        global $ID;
1963
        $ns = (string)getNS($ID);
1964
    }
1965
1966
    $ns_dir  = utf8_encodeFN(str_replace(':','/',$ns));
1967
1968
    $data = array();
1969
    search($data,$conf['mediadir'],'search_index',array('ns' => $ns_dir, 'nofiles' => true));
1970
1971
    // wrap a list with the root level around the other namespaces
1972
    array_unshift($data, array('level' => 0, 'id' => '', 'open' =>'true',
1973
                               'label' => '['.$lang['mediaroot'].']'));
1974
1975
    // insert the current ns into the hierarchy if it isn't already part of it
1976
    $ns_parts = explode(':', $ns);
1977
    $tmp_ns = '';
1978
    $pos = 0;
1979
    foreach ($ns_parts as $level => $part) {
1980
        if ($tmp_ns) $tmp_ns .= ':'.$part;
1981
        else $tmp_ns = $part;
1982
1983
        // find the namespace parts or insert them
1984
        while ($data[$pos]['id'] != $tmp_ns) {
1985
            if (
1986
                $pos >= count($data) ||
1987
                (
1988
                    $data[$pos]['level'] <= $level+1 &&
1989
                    strnatcmp(utf8_encodeFN($data[$pos]['id']), utf8_encodeFN($tmp_ns)) > 0
1990
                )
1991
            ) {
1992
                array_splice($data, $pos, 0, array(array('level' => $level+1, 'id' => $tmp_ns, 'open' => 'true')));
1993
                break;
1994
            }
1995
            ++$pos;
1996
        }
1997
    }
1998
1999
    echo html_buildlist($data,'idx','media_nstree_item','media_nstree_li');
2000
}
2001
2002
/**
2003
 * Userfunction for html_buildlist
2004
 *
2005
 * Prints a media namespace tree item
2006
 *
2007
 * @author Andreas Gohr <[email protected]>
2008
 *
2009
 * @param array $item
2010
 * @return string html
2011
 */
2012
function media_nstree_item($item){
2013
    global $INPUT;
2014
    $pos   = strrpos($item['id'], ':');
2015
    $label = substr($item['id'], $pos > 0 ? $pos + 1 : 0);
2016
    if(empty($item['label'])) $item['label'] = $label;
2017
2018
    $ret  = '';
2019
    if (!($INPUT->str('do') == 'media'))
2020
    $ret .= '<a href="'.DOKU_BASE.'lib/exe/mediamanager.php?ns='.idfilter($item['id']).'" class="idx_dir">';
2021
    else $ret .= '<a href="'.media_managerURL(array('ns' => idfilter($item['id'], false), 'tab_files' => 'files'))
2022
        .'" class="idx_dir">';
2023
    $ret .= $item['label'];
2024
    $ret .= '</a>';
2025
    return $ret;
2026
}
2027
2028
/**
2029
 * Userfunction for html_buildlist
2030
 *
2031
 * Prints a media namespace tree item opener
2032
 *
2033
 * @author Andreas Gohr <[email protected]>
2034
 *
2035
 * @param array $item
2036
 * @return string html
2037
 */
2038
function media_nstree_li($item){
2039
    $class='media level'.$item['level'];
2040
    if($item['open']){
2041
        $class .= ' open';
2042
        $img   = DOKU_BASE.'lib/images/minus.gif';
2043
        $alt   = '−';
2044
    }else{
2045
        $class .= ' closed';
2046
        $img   = DOKU_BASE.'lib/images/plus.gif';
2047
        $alt   = '+';
2048
    }
2049
    // TODO: only deliver an image if it actually has a subtree...
2050
    return '<li class="'.$class.'">'.
2051
        '<img src="'.$img.'" alt="'.$alt.'" />';
2052
}
2053
2054
/**
2055
 * Resizes the given image to the given size
2056
 *
2057
 * @author  Andreas Gohr <[email protected]>
2058
 *
2059
 * @param string $file filename, path to file
2060
 * @param string $ext  extension
2061
 * @param int    $w    desired width
2062
 * @param int    $h    desired height
2063
 * @return string path to resized or original size if failed
2064
 */
2065
function media_resize_image($file, $ext, $w, $h=0){
2066
    global $conf;
2067
2068
    $info = @getimagesize($file); //get original size
2069
    if($info == false) return $file; // that's no image - it's a spaceship!
2070
2071
    if(!$h) $h = round(($w * $info[1]) / $info[0]);
2072
    if(!$w) $w = round(($h * $info[0]) / $info[1]);
2073
2074
    // we wont scale up to infinity
2075
    if($w > 2000 || $h > 2000) return $file;
2076
2077
    // resize necessary? - (w,h) = native dimensions
2078
    if(($w == $info[0]) && ($h == $info[1])) return $file;
2079
2080
    //cache
2081
    $local = getCacheName($file,'.media.'.$w.'x'.$h.'.'.$ext);
2082
    $mtime = @filemtime($local); // 0 if not exists
2083
2084
    if($mtime > filemtime($file) ||
2085
        media_resize_imageIM($ext, $file, $info[0], $info[1], $local, $w, $h) ||
2086
        media_resize_imageGD($ext, $file, $info[0], $info[1], $local, $w, $h)
2087
    ) {
2088
        if($conf['fperm']) @chmod($local, $conf['fperm']);
2089
        return $local;
2090
    }
2091
    //still here? resizing failed
2092
    return $file;
2093
}
2094
2095
/**
2096
 * Crops the given image to the wanted ratio, then calls media_resize_image to scale it
2097
 * to the wanted size
2098
 *
2099
 * Crops are centered horizontally but prefer the upper third of an vertical
2100
 * image because most pics are more interesting in that area (rule of thirds)
2101
 *
2102
 * @author  Andreas Gohr <[email protected]>
2103
 *
2104
 * @param string $file filename, path to file
2105
 * @param string $ext  extension
2106
 * @param int    $w    desired width
2107
 * @param int    $h    desired height
2108
 * @return string path to resized or original size if failed
2109
 */
2110
function media_crop_image($file, $ext, $w, $h=0){
2111
    global $conf;
2112
2113
    if(!$h) $h = $w;
2114
    $info = @getimagesize($file); //get original size
2115
    if($info == false) return $file; // that's no image - it's a spaceship!
2116
2117
    // calculate crop size
2118
    $fr = $info[0]/$info[1];
2119
    $tr = $w/$h;
2120
2121
    // check if the crop can be handled completely by resize,
2122
    // i.e. the specified width & height match the aspect ratio of the source image
2123
    if ($w == round($h*$fr)) {
2124
        return media_resize_image($file, $ext, $w);
2125
    }
2126
2127
    if($tr >= 1){
2128
        if($tr > $fr){
2129
            $cw = $info[0];
2130
            $ch = (int) ($info[0]/$tr);
2131
        }else{
2132
            $cw = (int) ($info[1]*$tr);
2133
            $ch = $info[1];
2134
        }
2135
    }else{
2136
        if($tr < $fr){
2137
            $cw = (int) ($info[1]*$tr);
2138
            $ch = $info[1];
2139
        }else{
2140
            $cw = $info[0];
2141
            $ch = (int) ($info[0]/$tr);
2142
        }
2143
    }
2144
    // calculate crop offset
2145
    $cx = (int) (($info[0]-$cw)/2);
2146
    $cy = (int) (($info[1]-$ch)/3);
2147
2148
    //cache
2149
    $local = getCacheName($file,'.media.'.$cw.'x'.$ch.'.crop.'.$ext);
2150
    $mtime = @filemtime($local); // 0 if not exists
2151
2152
    if( $mtime > @filemtime($file) ||
2153
            media_crop_imageIM($ext,$file,$info[0],$info[1],$local,$cw,$ch,$cx,$cy) ||
2154
            media_resize_imageGD($ext,$file,$cw,$ch,$local,$cw,$ch,$cx,$cy) ){
2155
        if($conf['fperm']) @chmod($local, $conf['fperm']);
2156
        return media_resize_image($local,$ext, $w, $h);
2157
    }
2158
2159
    //still here? cropping failed
2160
    return media_resize_image($file,$ext, $w, $h);
2161
}
2162
2163
/**
2164
 * Calculate a token to be used to verify fetch requests for resized or
2165
 * cropped images have been internally generated - and prevent external
2166
 * DDOS attacks via fetch
2167
 *
2168
 * @author Christopher Smith <[email protected]>
2169
 *
2170
 * @param string  $id    id of the image
2171
 * @param int     $w     resize/crop width
2172
 * @param int     $h     resize/crop height
2173
 * @return string token or empty string if no token required
2174
 */
2175
function media_get_token($id,$w,$h){
2176
    // token is only required for modified images
2177
    if ($w || $h || media_isexternal($id)) {
2178
        $token = $id;
2179
        if ($w) $token .= '.'.$w;
2180
        if ($h) $token .= '.'.$h;
2181
2182
        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...
2183
    }
2184
2185
    return '';
2186
}
2187
2188
/**
2189
 * Download a remote file and return local filename
2190
 *
2191
 * returns false if download fails. Uses cached file if available and
2192
 * wanted
2193
 *
2194
 * @author  Andreas Gohr <[email protected]>
2195
 * @author  Pavel Vitis <[email protected]>
2196
 *
2197
 * @param string $url
2198
 * @param string $ext   extension
2199
 * @param int    $cache cachetime in seconds
2200
 * @return false|string path to cached file
2201
 */
2202
function media_get_from_URL($url,$ext,$cache){
2203
    global $conf;
2204
2205
    // if no cache or fetchsize just redirect
2206
    if ($cache==0)           return false;
2207
    if (!$conf['fetchsize']) return false;
2208
2209
    $local = getCacheName(strtolower($url),".media.$ext");
2210
    $mtime = @filemtime($local); // 0 if not exists
2211
2212
    //decide if download needed:
2213
    if(($mtime == 0) || // cache does not exist
2214
        ($cache != -1 && $mtime < time() - $cache) // 'recache' and cache has expired
2215
    ) {
2216
        if(media_image_download($url, $local)) {
2217
            return $local;
2218
        } else {
2219
            return false;
2220
        }
2221
    }
2222
2223
    //if cache exists use it else
2224
    if($mtime) return $local;
2225
2226
    //else return false
2227
    return false;
2228
}
2229
2230
/**
2231
 * Download image files
2232
 *
2233
 * @author Andreas Gohr <[email protected]>
2234
 *
2235
 * @param string $url
2236
 * @param string $file path to file in which to put the downloaded content
2237
 * @return bool
2238
 */
2239
function media_image_download($url,$file){
2240
    global $conf;
2241
    $http = new DokuHTTPClient();
2242
    $http->keep_alive = false; // we do single ops here, no need for keep-alive
2243
2244
    $http->max_bodysize = $conf['fetchsize'];
2245
    $http->timeout = 25; //max. 25 sec
2246
    $http->header_regexp = '!\r\nContent-Type: image/(jpe?g|gif|png)!i';
2247
2248
    $data = $http->get($url);
2249
    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...
2250
2251
    $fileexists = file_exists($file);
2252
    $fp = @fopen($file,"w");
2253
    if(!$fp) return false;
2254
    fwrite($fp,$data);
2255
    fclose($fp);
2256
    if(!$fileexists and $conf['fperm']) chmod($file, $conf['fperm']);
2257
2258
    // check if it is really an image
2259
    $info = @getimagesize($file);
2260
    if(!$info){
2261
        @unlink($file);
2262
        return false;
2263
    }
2264
2265
    return true;
2266
}
2267
2268
/**
2269
 * resize images using external ImageMagick convert program
2270
 *
2271
 * @author Pavel Vitis <[email protected]>
2272
 * @author Andreas Gohr <[email protected]>
2273
 *
2274
 * @param string $ext     extension
2275
 * @param string $from    filename path to file
2276
 * @param int    $from_w  original width
2277
 * @param int    $from_h  original height
2278
 * @param string $to      path to resized file
2279
 * @param int    $to_w    desired width
2280
 * @param int    $to_h    desired height
2281
 * @return bool
2282
 */
2283
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...
2284
    global $conf;
2285
2286
    // check if convert is configured
2287
    if(!$conf['im_convert']) return false;
2288
2289
    // prepare command
2290
    $cmd  = $conf['im_convert'];
2291
    $cmd .= ' -resize '.$to_w.'x'.$to_h.'!';
2292
    if ($ext == 'jpg' || $ext == 'jpeg') {
2293
        $cmd .= ' -quality '.$conf['jpg_quality'];
2294
    }
2295
    $cmd .= " $from $to";
2296
2297
    @exec($cmd,$out,$retval);
2298
    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...
2299
    return false;
2300
}
2301
2302
/**
2303
 * crop images using external ImageMagick convert program
2304
 *
2305
 * @author Andreas Gohr <[email protected]>
2306
 *
2307
 * @param string $ext     extension
2308
 * @param string $from    filename path to file
2309
 * @param int    $from_w  original width
2310
 * @param int    $from_h  original height
2311
 * @param string $to      path to resized file
2312
 * @param int    $to_w    desired width
2313
 * @param int    $to_h    desired height
2314
 * @param int    $ofs_x   offset of crop centre
2315
 * @param int    $ofs_y   offset of crop centre
2316
 * @return bool
2317
 */
2318
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...
2319
    global $conf;
2320
2321
    // check if convert is configured
2322
    if(!$conf['im_convert']) return false;
2323
2324
    // prepare command
2325
    $cmd  = $conf['im_convert'];
2326
    $cmd .= ' -crop '.$to_w.'x'.$to_h.'+'.$ofs_x.'+'.$ofs_y;
2327
    if ($ext == 'jpg' || $ext == 'jpeg') {
2328
        $cmd .= ' -quality '.$conf['jpg_quality'];
2329
    }
2330
    $cmd .= " $from $to";
2331
2332
    @exec($cmd,$out,$retval);
2333
    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...
2334
    return false;
2335
}
2336
2337
/**
2338
 * resize or crop images using PHP's libGD support
2339
 *
2340
 * @author Andreas Gohr <[email protected]>
2341
 * @author Sebastian Wienecke <[email protected]>
2342
 *
2343
 * @param string $ext     extension
2344
 * @param string $from    filename path to file
2345
 * @param int    $from_w  original width
2346
 * @param int    $from_h  original height
2347
 * @param string $to      path to resized file
2348
 * @param int    $to_w    desired width
2349
 * @param int    $to_h    desired height
2350
 * @param int    $ofs_x   offset of crop centre
2351
 * @param int    $ofs_y   offset of crop centre
2352
 * @return bool
2353
 */
2354
function media_resize_imageGD($ext,$from,$from_w,$from_h,$to,$to_w,$to_h,$ofs_x=0,$ofs_y=0){
2355
    global $conf;
2356
2357
    if($conf['gdlib'] < 1) return false; //no GDlib available or wanted
2358
2359
    // check available memory
2360
    if(!is_mem_available(($from_w * $from_h * 4) + ($to_w * $to_h * 4))){
2361
        return false;
2362
    }
2363
2364
    // create an image of the given filetype
2365
    $image = false;
2366
    if ($ext == 'jpg' || $ext == 'jpeg'){
2367
        if(!function_exists("imagecreatefromjpeg")) return false;
2368
        $image = @imagecreatefromjpeg($from);
2369
    }elseif($ext == 'png') {
2370
        if(!function_exists("imagecreatefrompng")) return false;
2371
        $image = @imagecreatefrompng($from);
2372
2373
    }elseif($ext == 'gif') {
2374
        if(!function_exists("imagecreatefromgif")) return false;
2375
        $image = @imagecreatefromgif($from);
2376
    }
2377
    if(!$image) return false;
2378
2379
    $newimg = false;
2380
    if(($conf['gdlib']>1) && function_exists("imagecreatetruecolor") && $ext != 'gif'){
2381
        $newimg = @imagecreatetruecolor ($to_w, $to_h);
2382
    }
2383
    if(!$newimg) $newimg = @imagecreate($to_w, $to_h);
2384
    if(!$newimg){
2385
        imagedestroy($image);
2386
        return false;
2387
    }
2388
2389
    //keep png alpha channel if possible
2390
    if($ext == 'png' && $conf['gdlib']>1 && function_exists('imagesavealpha')){
2391
        imagealphablending($newimg, false);
2392
        imagesavealpha($newimg,true);
2393
    }
2394
2395
    //keep gif transparent color if possible
2396
    if($ext == 'gif' && function_exists('imagefill') && function_exists('imagecolorallocate')) {
2397
        if(function_exists('imagecolorsforindex') && function_exists('imagecolortransparent')) {
2398
            $transcolorindex = @imagecolortransparent($image);
2399
            if($transcolorindex >= 0 ) { //transparent color exists
2400
                $transcolor = @imagecolorsforindex($image, $transcolorindex);
2401
                $transcolorindex = @imagecolorallocate(
2402
                    $newimg,
2403
                    $transcolor['red'],
2404
                    $transcolor['green'],
2405
                    $transcolor['blue']
2406
                );
2407
                @imagefill($newimg, 0, 0, $transcolorindex);
2408
                @imagecolortransparent($newimg, $transcolorindex);
2409
            }else{ //filling with white
2410
                $whitecolorindex = @imagecolorallocate($newimg, 255, 255, 255);
2411
                @imagefill($newimg, 0, 0, $whitecolorindex);
2412
            }
2413
        }else{ //filling with white
2414
            $whitecolorindex = @imagecolorallocate($newimg, 255, 255, 255);
2415
            @imagefill($newimg, 0, 0, $whitecolorindex);
2416
        }
2417
    }
2418
2419
    //try resampling first
2420
    if(function_exists("imagecopyresampled")){
2421
        if(!@imagecopyresampled($newimg, $image, 0, 0, $ofs_x, $ofs_y, $to_w, $to_h, $from_w, $from_h)) {
2422
            imagecopyresized($newimg, $image, 0, 0, $ofs_x, $ofs_y, $to_w, $to_h, $from_w, $from_h);
2423
        }
2424
    }else{
2425
        imagecopyresized($newimg, $image, 0, 0, $ofs_x, $ofs_y, $to_w, $to_h, $from_w, $from_h);
2426
    }
2427
2428
    $okay = false;
2429
    if ($ext == 'jpg' || $ext == 'jpeg'){
2430
        if(!function_exists('imagejpeg')){
2431
            $okay = false;
2432
        }else{
2433
            $okay = imagejpeg($newimg, $to, $conf['jpg_quality']);
2434
        }
2435
    }elseif($ext == 'png') {
2436
        if(!function_exists('imagepng')){
2437
            $okay = false;
2438
        }else{
2439
            $okay =  imagepng($newimg, $to);
2440
        }
2441
    }elseif($ext == 'gif') {
2442
        if(!function_exists('imagegif')){
2443
            $okay = false;
2444
        }else{
2445
            $okay = imagegif($newimg, $to);
2446
        }
2447
    }
2448
2449
    // destroy GD image ressources
2450
    if($image) imagedestroy($image);
2451
    if($newimg) imagedestroy($newimg);
2452
2453
    return $okay;
2454
}
2455
2456
/**
2457
 * Return other media files with the same base name
2458
 * but different extensions.
2459
 *
2460
 * @param string   $src     - ID of media file
2461
 * @param string[] $exts    - alternative extensions to find other files for
2462
 * @return array            - array(mime type => file ID)
2463
 *
2464
 * @author Anika Henke <[email protected]>
2465
 */
2466
function media_alternativefiles($src, $exts){
2467
2468
    $files = array();
2469
    list($srcExt, /* $srcMime */) = mimetype($src);
2470
    $filebase = substr($src, 0, -1 * (strlen($srcExt)+1));
2471
2472
    foreach($exts as $ext) {
2473
        $fileid = $filebase.'.'.$ext;
2474
        $file = mediaFN($fileid);
2475
        if(file_exists($file)) {
2476
            list(/* $fileExt */, $fileMime) = mimetype($file);
2477
            $files[$fileMime] = $fileid;
2478
        }
2479
    }
2480
    return $files;
2481
}
2482
2483
/**
2484
 * Check if video/audio is supported to be embedded.
2485
 *
2486
 * @param string $mime      - mimetype of media file
2487
 * @param string $type      - type of media files to check ('video', 'audio', or null for all)
2488
 * @return boolean
2489
 *
2490
 * @author Anika Henke <[email protected]>
2491
 */
2492
function media_supportedav($mime, $type=NULL){
2493
    $supportedAudio = array(
2494
        'ogg' => 'audio/ogg',
2495
        'mp3' => 'audio/mpeg',
2496
        'wav' => 'audio/wav',
2497
    );
2498
    $supportedVideo = array(
2499
        'webm' => 'video/webm',
2500
        'ogv' => 'video/ogg',
2501
        'mp4' => 'video/mp4',
2502
    );
2503
    if ($type == 'audio') {
2504
        $supportedAv = $supportedAudio;
2505
    } elseif ($type == 'video') {
2506
        $supportedAv = $supportedVideo;
2507
    } else {
2508
        $supportedAv = array_merge($supportedAudio, $supportedVideo);
2509
    }
2510
    return in_array($mime, $supportedAv);
2511
}
2512
2513
/**
2514
 * Return track media files with the same base name
2515
 * but extensions that indicate kind and lang.
2516
 * ie for foo.webm search foo.sub.lang.vtt, foo.cap.lang.vtt...
2517
 *
2518
 * @param string   $src     - ID of media file
2519
 * @return array            - array(mediaID => array( kind, srclang ))
2520
 *
2521
 * @author Schplurtz le Déboulonné <[email protected]>
2522
 */
2523
function media_trackfiles($src){
2524
    $kinds=array(
2525
        'sub' => 'subtitles',
2526
        'cap' => 'captions',
2527
        'des' => 'descriptions',
2528
        'cha' => 'chapters',
2529
        'met' => 'metadata'
2530
    );
2531
2532
    $files = array();
2533
    $re='/\\.(sub|cap|des|cha|met)\\.([^.]+)\\.vtt$/';
2534
    $baseid=pathinfo($src, PATHINFO_FILENAME);
2535
    $pattern=mediaFN($baseid).'.*.*.vtt';
2536
    $list=glob($pattern);
2537
    foreach($list as $track) {
2538
        if(preg_match($re, $track, $matches)){
2539
            $files[$baseid.'.'.$matches[1].'.'.$matches[2].'.vtt']=array(
2540
                $kinds[$matches[1]],
2541
                $matches[2],
2542
            );
2543
        }
2544
    }
2545
    return $files;
2546
}
2547
2548
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
2549