Completed
Push — master ( 680cee...345058 )
by Andreas
04:06
created

media.php ➔ media_trackfiles()   B

Complexity

Conditions 3
Paths 3

Size

Total Lines 24
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 18
nc 3
nop 1
dl 0
loc 24
rs 8.9713
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
if(!defined('DOKU_INC')) die('meh.');
10
if(!defined('NL')) define('NL',"\n");
11
12
/**
13
 * Lists pages which currently use a media file selected for deletion
14
 *
15
 * References uses the same visual as search results and share
16
 * their CSS tags except pagenames won't be links.
17
 *
18
 * @author Matthias Grimm <[email protected]>
19
 *
20
 * @param array $data
21
 * @param string $id
22
 */
23
function media_filesinuse($data,$id){
24
    global $lang;
25
    echo '<h1>'.$lang['reference'].' <code>'.hsc(noNS($id)).'</code></h1>';
26
    echo '<p>'.hsc($lang['ref_inuse']).'</p>';
27
28
    $hidden=0; //count of hits without read permission
29
    foreach($data as $row){
30
        if(auth_quickaclcheck($row) >= AUTH_READ && isVisiblePage($row)){
31
            echo '<div class="search_result">';
32
            echo '<span class="mediaref_ref">'.hsc($row).'</span>';
33
            echo '</div>';
34
        }else
35
            $hidden++;
36
    }
37
    if ($hidden){
38
        print '<div class="mediaref_hidden">'.$lang['ref_hidden'].'</div>';
39
    }
40
}
41
42
/**
43
 * Handles the saving of image meta data
44
 *
45
 * @author Andreas Gohr <[email protected]>
46
 * @author Kate Arzamastseva <[email protected]>
47
 *
48
 * @param string $id media id
49
 * @param int $auth permission level
50
 * @param array $data
51
 * @return false|string
52
 */
53
function media_metasave($id,$auth,$data){
54
    if($auth < AUTH_UPLOAD) return false;
55
    if(!checkSecurityToken()) return false;
56
    global $lang;
57
    global $conf;
58
    $src = mediaFN($id);
59
60
    $meta = new JpegMeta($src);
61
    $meta->_parseAll();
62
63
    foreach($data as $key => $val){
64
        $val=trim($val);
65
        if(empty($val)){
66
            $meta->deleteField($key);
67
        }else{
68
            $meta->setField($key,$val);
69
        }
70
    }
71
72
    $old = @filemtime($src);
73
    if(!file_exists(mediaFN($id, $old)) && file_exists($src)) {
74
        // add old revision to the attic
75
        media_saveOldRevision($id);
76
    }
77
    $filesize_old = filesize($src);
78
    if($meta->save()){
79
        if($conf['fperm']) chmod($src, $conf['fperm']);
80
        @clearstatcache(true, $src);
1 ignored issue
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
81
        $new = @filemtime($src);
82
        $filesize_new = filesize($src);
83
        $sizechange = $filesize_new - $filesize_old;
84
85
        // add a log entry to the media changelog
86
        addMediaLogEntry($new, $id, DOKU_CHANGE_TYPE_EDIT, $lang['media_meta_edited'], '', null, $sizechange);
87
88
        msg($lang['metasaveok'],1);
89
        return $id;
90
    }else{
91
        msg($lang['metasaveerr'],-1);
92
        return false;
93
    }
94
}
95
96
/**
97
 * check if a media is external source
98
 *
99
 * @author Gerrit Uitslag <[email protected]>
100
 *
101
 * @param string $id the media ID or URL
102
 * @return bool
103
 */
104
function media_isexternal($id){
105
    if (preg_match('#^(?:https?|ftp)://#i', $id)) return true;
106
    return false;
107
}
108
109
/**
110
 * Check if a media item is public (eg, external URL or readable by @ALL)
111
 *
112
 * @author Andreas Gohr <[email protected]>
113
 *
114
 * @param string $id  the media ID or URL
115
 * @return bool
116
 */
117
function media_ispublic($id){
118
    if(media_isexternal($id)) return true;
119
    $id = cleanID($id);
120
    if(auth_aclcheck(getNS($id).':*', '', array()) >= AUTH_READ) return true;
121
    return false;
122
}
123
124
/**
125
 * Display the form to edit image meta data
126
 *
127
 * @author Andreas Gohr <[email protected]>
128
 * @author Kate Arzamastseva <[email protected]>
129
 *
130
 * @param string $id media id
131
 * @param int $auth permission level
132
 * @return bool
133
 */
134
function media_metaform($id,$auth){
135
    global $lang;
136
137
    if($auth < AUTH_UPLOAD) {
138
        echo '<div class="nothing">'.$lang['media_perm_upload'].'</div>'.NL;
139
        return false;
140
    }
141
142
    // load the field descriptions
143
    static $fields = null;
144
    if(is_null($fields)){
145
        $config_files = getConfigFiles('mediameta');
146
        foreach ($config_files as $config_file) {
147
            if(file_exists($config_file)) include($config_file);
148
        }
149
    }
150
151
    $src = mediaFN($id);
152
153
    // output
154
    $form = new Doku_Form(array('action' => media_managerURL(array('tab_details' => 'view'), '&'),
155
                                'class' => 'meta'));
156
    $form->addHidden('img', $id);
157
    $form->addHidden('mediado', 'save');
158
    foreach($fields as $key => $field){
0 ignored issues
show
Bug introduced by
The expression $fields of type null is not traversable.
Loading history...
159
        // get current value
160
        if (empty($field[0])) continue;
161
        $tags = array($field[0]);
162
        if(is_array($field[3])) $tags = array_merge($tags,$field[3]);
163
        $value = tpl_img_getTag($tags,'',$src);
164
        $value = cleanText($value);
165
166
        // prepare attributes
167
        $p = array();
168
        $p['class'] = 'edit';
169
        $p['id']    = 'meta__'.$key;
170
        $p['name']  = 'meta['.$field[0].']';
171
        $p_attrs    = array('class' => 'edit');
172
173
        $form->addElement('<div class="row">');
174
        if($field[2] == 'text'){
175
            $form->addElement(form_makeField('text', $p['name'], $value, ($lang[$field[1]]) ? $lang[$field[1]] : $field[1] . ':', $p['id'], $p['class'], $p_attrs));
176
        }else{
177
            $att = buildAttributes($p);
178
            $form->addElement('<label for="meta__'.$key.'">'.$lang[$field[1]].'</label>');
179
            $form->addElement("<textarea $att rows=\"6\" cols=\"50\">".formText($value).'</textarea>');
180
        }
181
        $form->addElement('</div>'.NL);
182
    }
183
    $form->addElement('<div class="buttons">');
184
    $form->addElement(form_makeButton('submit', '', $lang['btn_save'], array('accesskey' => 's', 'name' => 'mediado[save]')));
185
    $form->addElement('</div>'.NL);
186
    $form->printForm();
187
188
    return true;
189
}
190
191
/**
192
 * Convenience function to check if a media file is still in use
193
 *
194
 * @author Michael Klier <[email protected]>
195
 *
196
 * @param string $id media id
197
 * @return array|bool
198
 */
199
function media_inuse($id) {
200
    global $conf;
201
202
    if($conf['refcheck']){
203
        $mediareferences = ft_mediause($id,true);
204
        if(!count($mediareferences)) {
205
            return false;
206
        } else {
207
            return $mediareferences;
208
        }
209
    } else {
210
        return false;
211
    }
212
}
213
214
define('DOKU_MEDIA_DELETED', 1);
215
define('DOKU_MEDIA_NOT_AUTH', 2);
216
define('DOKU_MEDIA_INUSE', 4);
217
define('DOKU_MEDIA_EMPTY_NS', 8);
218
219
/**
220
 * Handles media file deletions
221
 *
222
 * If configured, checks for media references before deletion
223
 *
224
 * @author             Andreas Gohr <[email protected]>
225
 *
226
 * @param string $id media id
227
 * @param int $auth no longer used
228
 * @return int One of: 0,
229
 *                     DOKU_MEDIA_DELETED,
230
 *                     DOKU_MEDIA_DELETED | DOKU_MEDIA_EMPTY_NS,
231
 *                     DOKU_MEDIA_NOT_AUTH,
232
 *                     DOKU_MEDIA_INUSE
233
 */
234
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...
235
    global $lang;
236
    $auth = auth_quickaclcheck(ltrim(getNS($id).':*', ':'));
237
    if($auth < AUTH_DELETE) return DOKU_MEDIA_NOT_AUTH;
238
    if(media_inuse($id)) return DOKU_MEDIA_INUSE;
239
240
    $file = mediaFN($id);
241
242
    // trigger an event - MEDIA_DELETE_FILE
243
    $data = array();
244
    $data['id']   = $id;
245
    $data['name'] = utf8_basename($file);
246
    $data['path'] = $file;
247
    $data['size'] = (file_exists($file)) ? filesize($file) : 0;
248
249
    $data['unl'] = false;
250
    $data['del'] = false;
251
    $evt = new Doku_Event('MEDIA_DELETE_FILE',$data);
252
    if ($evt->advise_before()) {
253
        $old = @filemtime($file);
254
        if(!file_exists(mediaFN($id, $old)) && file_exists($file)) {
255
            // add old revision to the attic
256
            media_saveOldRevision($id);
257
        }
258
259
        $data['unl'] = @unlink($file);
260
        if($data['unl']) {
261
            $sizechange = 0 - $data['size'];
262
            addMediaLogEntry(time(), $id, DOKU_CHANGE_TYPE_DELETE, $lang['deleted'], '', null, $sizechange);
263
264
            $data['del'] = io_sweepNS($id, 'mediadir');
265
        }
266
    }
267
    $evt->advise_after();
268
    unset($evt);
269
270
    if($data['unl'] && $data['del']){
271
        return DOKU_MEDIA_DELETED | DOKU_MEDIA_EMPTY_NS;
272
    }
273
274
    return $data['unl'] ? DOKU_MEDIA_DELETED : 0;
275
}
276
277
/**
278
 * Handle file uploads via XMLHttpRequest
279
 *
280
 * @param string $ns   target namespace
281
 * @param int    $auth current auth check result
282
 * @return false|string false on error, id of the new file on success
283
 */
284
function media_upload_xhr($ns,$auth){
285
    if(!checkSecurityToken()) return false;
286
    global $INPUT;
287
288
    $id = $INPUT->get->str('qqfile');
289
    list($ext,$mime) = mimetype($id);
290
    $input = fopen("php://input", "r");
291
    if (!($tmp = io_mktmpdir())) return false;
292
    $path = $tmp.'/'.md5($id);
293
    $target = fopen($path, "w");
294
    $realSize = stream_copy_to_stream($input, $target);
295
    fclose($target);
296
    fclose($input);
297
    if (isset($_SERVER["CONTENT_LENGTH"]) && ($realSize != (int)$_SERVER["CONTENT_LENGTH"])){
298
        unlink($path);
299
        return false;
300
    }
301
302
    $res = media_save(
303
        array('name' => $path,
304
            'mime' => $mime,
305
            'ext'  => $ext),
306
        $ns.':'.$id,
307
        (($INPUT->get->str('ow') == 'true') ? true : false),
308
        $auth,
309
        'copy'
310
    );
311
    unlink($path);
312
    if ($tmp) io_rmdir($tmp, true);
313
    if (is_array($res)) {
314
        msg($res[0], $res[1]);
315
        return false;
316
    }
317
    return $res;
318
}
319
320
/**
321
 * Handles media file uploads
322
 *
323
 * @author Andreas Gohr <[email protected]>
324
 * @author Michael Klier <[email protected]>
325
 *
326
 * @param string     $ns    target namespace
327
 * @param int        $auth  current auth check result
328
 * @param bool|array $file  $_FILES member, $_FILES['upload'] if false
329
 * @return false|string false on error, id of the new file on success
330
 */
331
function media_upload($ns,$auth,$file=false){
332
    if(!checkSecurityToken()) return false;
333
    global $lang;
334
    global $INPUT;
335
336
    // get file and id
337
    $id   = $INPUT->post->str('mediaid');
338
    if (!$file) $file = $_FILES['upload'];
339
    if(empty($id)) $id = $file['name'];
340
341
    // check for errors (messages are done in lib/exe/mediamanager.php)
342
    if($file['error']) return false;
343
344
    // check extensions
345
    list($fext,$fmime) = mimetype($file['name']);
346
    list($iext,$imime) = mimetype($id);
347
    if($fext && !$iext){
348
        // no extension specified in id - read original one
349
        $id   .= '.'.$fext;
350
        $imime = $fmime;
351
    }elseif($fext && $fext != $iext){
352
        // extension was changed, print warning
353
        msg(sprintf($lang['mediaextchange'],$fext,$iext));
354
    }
355
356
    $res = media_save(array('name' => $file['tmp_name'],
357
                            'mime' => $imime,
358
                            'ext'  => $iext), $ns.':'.$id,
359
                      $INPUT->post->bool('ow'), $auth, 'copy_uploaded_file');
360
    if (is_array($res)) {
361
        msg($res[0], $res[1]);
362
        return false;
363
    }
364
    return $res;
365
}
366
367
/**
368
 * An alternative to move_uploaded_file that copies
369
 *
370
 * Using copy, makes sure any setgid bits on the media directory are honored
371
 *
372
 * @see   move_uploaded_file()
373
 *
374
 * @param string $from
375
 * @param string $to
376
 * @return bool
377
 */
378
function copy_uploaded_file($from, $to){
379
    if(!is_uploaded_file($from)) return false;
380
    $ok = copy($from, $to);
381
    @unlink($from);
1 ignored issue
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
382
    return $ok;
383
}
384
385
/**
386
 * This generates an action event and delegates to _media_upload_action().
387
 * Action plugins are allowed to pre/postprocess the uploaded file.
388
 * (The triggered event is preventable.)
389
 *
390
 * Event data:
391
 * $data[0]     fn_tmp:    the temporary file name (read from $_FILES)
392
 * $data[1]     fn:        the file name of the uploaded file
393
 * $data[2]     id:        the future directory id of the uploaded file
394
 * $data[3]     imime:     the mimetype of the uploaded file
395
 * $data[4]     overwrite: if an existing file is going to be overwritten
396
 * $data[5]     move:      name of function that performs move/copy/..
397
 *
398
 * @triggers MEDIA_UPLOAD_FINISH
399
 *
400
 * @param array  $file
401
 * @param string $id   media id
402
 * @param bool   $ow   overwrite?
403
 * @param int    $auth permission level
404
 * @param string $move name of functions that performs move/copy/..
405
 * @return false|array|string
406
 */
407
function media_save($file, $id, $ow, $auth, $move) {
408
    if($auth < AUTH_UPLOAD) {
409
        return array("You don't have permissions to upload files.", -1);
410
    }
411
412
    if (!isset($file['mime']) || !isset($file['ext'])) {
413
        list($ext, $mime) = mimetype($id);
414
        if (!isset($file['mime'])) {
415
            $file['mime'] = $mime;
416
        }
417
        if (!isset($file['ext'])) {
418
            $file['ext'] = $ext;
419
        }
420
    }
421
422
    global $lang, $conf;
423
424
    // get filename
425
    $id   = cleanID($id);
426
    $fn   = mediaFN($id);
427
428
    // get filetype regexp
429
    $types = array_keys(getMimeTypes());
430
    $types = array_map(
431
        function ($q) {
432
            return preg_quote($q, "/");
433
        },
434
        $types
435
    );
436
    $regex = join('|',$types);
437
438
    // because a temp file was created already
439
    if(!preg_match('/\.('.$regex.')$/i',$fn)) {
440
        return array($lang['uploadwrong'],-1);
441
    }
442
443
    //check for overwrite
444
    $overwrite = file_exists($fn);
445
    $auth_ow = (($conf['mediarevisions']) ? AUTH_UPLOAD : AUTH_DELETE);
446
    if($overwrite && (!$ow || $auth < $auth_ow)) {
447
        return array($lang['uploadexist'], 0);
448
    }
449
    // check for valid content
450
    $ok = media_contentcheck($file['name'], $file['mime']);
451
    if($ok == -1){
452
        return array(sprintf($lang['uploadbadcontent'],'.' . $file['ext']),-1);
453
    }elseif($ok == -2){
454
        return array($lang['uploadspam'],-1);
455
    }elseif($ok == -3){
456
        return array($lang['uploadxss'],-1);
457
    }
458
459
    // prepare event data
460
    $data = array();
461
    $data[0] = $file['name'];
462
    $data[1] = $fn;
463
    $data[2] = $id;
464
    $data[3] = $file['mime'];
465
    $data[4] = $overwrite;
466
    $data[5] = $move;
467
468
    // trigger event
469
    return trigger_event('MEDIA_UPLOAD_FINISH', $data, '_media_upload_action', true);
470
}
471
472
/**
473
 * Callback adapter for media_upload_finish() triggered by MEDIA_UPLOAD_FINISH
474
 *
475
 * @author Michael Klier <[email protected]>
476
 *
477
 * @param array $data event data
478
 * @return false|array|string
479
 */
480
function _media_upload_action($data) {
481
    // fixme do further sanity tests of given data?
482
    if(is_array($data) && count($data)===6) {
483
        return media_upload_finish($data[0], $data[1], $data[2], $data[3], $data[4], $data[5]);
484
    } else {
485
        return false; //callback error
486
    }
487
}
488
489
/**
490
 * Saves an uploaded media file
491
 *
492
 * @author Andreas Gohr <[email protected]>
493
 * @author Michael Klier <[email protected]>
494
 * @author Kate Arzamastseva <[email protected]>
495
 *
496
 * @param string $fn_tmp
497
 * @param string $fn
498
 * @param string $id        media id
499
 * @param string $imime     mime type
500
 * @param bool   $overwrite overwrite existing?
501
 * @param string $move      function name
502
 * @return array|string
503
 */
504
function media_upload_finish($fn_tmp, $fn, $id, $imime, $overwrite, $move = 'move_uploaded_file') {
505
    global $conf;
506
    global $lang;
507
    global $REV;
508
509
    $old = @filemtime($fn);
510
    if(!file_exists(mediaFN($id, $old)) && file_exists($fn)) {
511
        // add old revision to the attic if missing
512
        media_saveOldRevision($id);
513
    }
514
515
    // prepare directory
516
    io_createNamespace($id, 'media');
517
518
    $filesize_old = file_exists($fn) ? filesize($fn) : 0;
519
520
    if($move($fn_tmp, $fn)) {
521
        @clearstatcache(true,$fn);
1 ignored issue
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
522
        $new = @filemtime($fn);
523
        // Set the correct permission here.
524
        // Always chmod media because they may be saved with different permissions than expected from the php umask.
525
        // (Should normally chmod to $conf['fperm'] only if $conf['fperm'] is set.)
526
        chmod($fn, $conf['fmode']);
527
        msg($lang['uploadsucc'],1);
528
        media_notify($id,$fn,$imime,$old);
529
        // add a log entry to the media changelog
530
        $filesize_new = filesize($fn);
531
        $sizechange = $filesize_new - $filesize_old;
532
        if($REV) {
533
            addMediaLogEntry($new, $id, DOKU_CHANGE_TYPE_REVERT, sprintf($lang['restored'], dformat($REV)), $REV, null, $sizechange);
534
        } elseif($overwrite) {
535
            addMediaLogEntry($new, $id, DOKU_CHANGE_TYPE_EDIT, '', '', null, $sizechange);
536
        } else {
537
            addMediaLogEntry($new, $id, DOKU_CHANGE_TYPE_CREATE, $lang['created'], '', null, $sizechange);
538
        }
539
        return $id;
540
    }else{
541
        return array($lang['uploadfail'],-1);
542
    }
543
}
544
545
/**
546
 * Moves the current version of media file to the media_attic
547
 * directory
548
 *
549
 * @author Kate Arzamastseva <[email protected]>
550
 *
551
 * @param string $id
552
 * @return int - revision date
553
 */
554
function media_saveOldRevision($id){
555
    global $conf, $lang;
556
557
    $oldf = mediaFN($id);
558
    if(!file_exists($oldf)) return '';
559
    $date = filemtime($oldf);
560
    if (!$conf['mediarevisions']) return $date;
561
562
    $medialog = new MediaChangeLog($id);
563
    if (!$medialog->getRevisionInfo($date)) {
564
        // there was an external edit,
565
        // there is no log entry for current version of file
566
        $sizechange = filesize($oldf);
567
        if(!file_exists(mediaMetaFN($id, '.changes'))) {
568
            addMediaLogEntry($date, $id, DOKU_CHANGE_TYPE_CREATE, $lang['created'], '', null, $sizechange);
569
        } else {
570
            $oldRev = $medialog->getRevisions(-1, 1); // from changelog
571
            $oldRev = (int) (empty($oldRev) ? 0 : $oldRev[0]);
572
            $filesize_old = filesize(mediaFN($id, $oldRev));
573
            $sizechange = $sizechange - $filesize_old;
574
575
            addMediaLogEntry($date, $id, DOKU_CHANGE_TYPE_EDIT, '', '', null, $sizechange);
576
        }
577
    }
578
579
    $newf = mediaFN($id,$date);
580
    io_makeFileDir($newf);
581
    if(copy($oldf, $newf)) {
582
        // Set the correct permission here.
583
        // Always chmod media because they may be saved with different permissions than expected from the php umask.
584
        // (Should normally chmod to $conf['fperm'] only if $conf['fperm'] is set.)
585
        chmod($newf, $conf['fmode']);
586
    }
587
    return $date;
588
}
589
590
/**
591
 * This function checks if the uploaded content is really what the
592
 * mimetype says it is. We also do spam checking for text types here.
593
 *
594
 * We need to do this stuff because we can not rely on the browser
595
 * to do this check correctly. Yes, IE is broken as usual.
596
 *
597
 * @author Andreas Gohr <[email protected]>
598
 * @link   http://www.splitbrain.org/blog/2007-02/12-internet_explorer_facilitates_cross_site_scripting
599
 * @fixme  check all 26 magic IE filetypes here?
600
 *
601
 * @param string $file path to file
602
 * @param string $mime mimetype
603
 * @return int
604
 */
605
function media_contentcheck($file,$mime){
606
    global $conf;
607
    if($conf['iexssprotect']){
608
        $fh = @fopen($file, 'rb');
609
        if($fh){
610
            $bytes = fread($fh, 256);
611
            fclose($fh);
612
            if(preg_match('/<(script|a|img|html|body|iframe)[\s>]/i',$bytes)){
613
                return -3; //XSS: possibly malicious content
614
            }
615
        }
616
    }
617
    if(substr($mime,0,6) == 'image/'){
618
        $info = @getimagesize($file);
619
        if($mime == 'image/gif' && $info[2] != 1){
620
            return -1; // uploaded content did not match the file extension
621
        }elseif($mime == 'image/jpeg' && $info[2] != 2){
622
            return -1;
623
        }elseif($mime == 'image/png' && $info[2] != 3){
624
            return -1;
625
        }
626
        # fixme maybe check other images types as well
627
    }elseif(substr($mime,0,5) == 'text/'){
628
        global $TEXT;
629
        $TEXT = io_readFile($file);
630
        if(checkwordblock()){
631
            return -2; //blocked by the spam blacklist
632
        }
633
    }
634
    return 0;
635
}
636
637
/**
638
 * Send a notify mail on uploads
639
 *
640
 * @author Andreas Gohr <[email protected]>
641
 *
642
 * @param string   $id      media id
643
 * @param string   $file    path to file
644
 * @param string   $mime    mime type
645
 * @param bool|int $old_rev revision timestamp or false
646
 * @return bool
647
 */
648
function media_notify($id,$file,$mime,$old_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...
649
    global $conf;
650
    if(empty($conf['notify'])) return false; //notify enabled?
651
652
    $subscription = new Subscription();
653
    return $subscription->send_media_diff($conf['notify'], 'uploadmail', $id, $old_rev);
654
}
655
656
/**
657
 * List all files in a given Media namespace
658
 *
659
 * @param string      $ns             namespace
660
 * @param null|int    $auth           permission level
661
 * @param string      $jump           id
662
 * @param bool        $fullscreenview
663
 * @param bool|string $sort           sorting order, false skips sorting
664
 */
665
function media_filelist($ns,$auth=null,$jump='',$fullscreenview=false,$sort=false){
666
    global $conf;
667
    global $lang;
668
    $ns = cleanID($ns);
669
670
    // check auth our self if not given (needed for ajax calls)
671
    if(is_null($auth)) $auth = auth_quickaclcheck("$ns:*");
672
673
    if (!$fullscreenview) echo '<h1 id="media__ns">:'.hsc($ns).'</h1>'.NL;
674
675
    if($auth < AUTH_READ){
676
        // FIXME: print permission warning here instead?
677
        echo '<div class="nothing">'.$lang['nothingfound'].'</div>'.NL;
678
    }else{
679
        if (!$fullscreenview) {
680
            media_uploadform($ns, $auth);
681
            media_searchform($ns);
682
        }
683
684
        $dir = utf8_encodeFN(str_replace(':','/',$ns));
685
        $data = array();
686
        search($data,$conf['mediadir'],'search_media',
687
                array('showmsg'=>true,'depth'=>1),$dir,1,$sort);
688
689
        if(!count($data)){
690
            echo '<div class="nothing">'.$lang['nothingfound'].'</div>'.NL;
691
        }else {
692
            if ($fullscreenview) {
693
                echo '<ul class="' . _media_get_list_type() . '">';
694
            }
695
            foreach($data as $item){
696
                if (!$fullscreenview) {
697
                    media_printfile($item,$auth,$jump);
698
                } else {
699
                    media_printfile_thumbs($item,$auth,$jump);
700
                }
701
            }
702
            if ($fullscreenview) echo '</ul>'.NL;
703
        }
704
    }
705
}
706
707
/**
708
 * Prints tabs for files list actions
709
 *
710
 * @author Kate Arzamastseva <[email protected]>
711
 * @author Adrian Lang <[email protected]>
712
 *
713
 * @param string $selected_tab - opened tab
714
 */
715
716
function media_tabs_files($selected_tab = ''){
717
    global $lang;
718
    $tabs = array();
719
    foreach(array('files'  => 'mediaselect',
720
                  'upload' => 'media_uploadtab',
721
                  'search' => 'media_searchtab') as $tab => $caption) {
722
        $tabs[$tab] = array('href'    => media_managerURL(array('tab_files' => $tab), '&'),
723
                            'caption' => $lang[$caption]);
724
    }
725
726
    html_tabs($tabs, $selected_tab);
727
}
728
729
/**
730
 * Prints tabs for files details actions
731
 *
732
 * @author Kate Arzamastseva <[email protected]>
733
 * @param string $image filename of the current image
734
 * @param string $selected_tab opened tab
735
 */
736
function media_tabs_details($image, $selected_tab = ''){
737
    global $lang, $conf;
738
739
    $tabs = array();
740
    $tabs['view'] = array('href'    => media_managerURL(array('tab_details' => 'view'), '&'),
741
                          'caption' => $lang['media_viewtab']);
742
743
    list(, $mime) = mimetype($image);
744
    if ($mime == 'image/jpeg' && file_exists(mediaFN($image))) {
745
        $tabs['edit'] = array('href'    => media_managerURL(array('tab_details' => 'edit'), '&'),
746
                              'caption' => $lang['media_edittab']);
747
    }
748
    if ($conf['mediarevisions']) {
749
        $tabs['history'] = array('href'    => media_managerURL(array('tab_details' => 'history'), '&'),
750
                                 'caption' => $lang['media_historytab']);
751
    }
752
753
    html_tabs($tabs, $selected_tab);
754
}
755
756
/**
757
 * Prints options for the tab that displays a list of all files
758
 *
759
 * @author Kate Arzamastseva <[email protected]>
760
 */
761
function media_tab_files_options(){
762
    global $lang;
763
    global $INPUT;
764
    global $ID;
765
    $form = new Doku_Form(array('class' => 'options', 'method' => 'get',
766
                                'action' => wl($ID)));
767
    $media_manager_params = media_managerURL(array(), '', false, true);
768
    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...
769
        $form->addHidden($pKey, $pVal);
770
    }
771
    $form->addHidden('sectok', null);
772
    if ($INPUT->has('q')) {
773
        $form->addHidden('q', $INPUT->str('q'));
774
    }
775
    $form->addElement('<ul>'.NL);
776
    foreach(array('list' => array('listType', array('thumbs', 'rows')),
777
                  'sort' => array('sortBy', array('name', 'date')))
778
            as $group => $content) {
779
        $checked = "_media_get_${group}_type";
780
        $checked = $checked();
781
782
        $form->addElement('<li class="' . $content[0] . '">');
783
        foreach($content[1] as $option) {
784
            $attrs = array();
785
            if ($checked == $option) {
786
                $attrs['checked'] = 'checked';
787
            }
788
            $form->addElement(form_makeRadioField($group . '_dwmedia', $option,
789
                                       $lang['media_' . $group . '_' . $option],
790
                                                  $content[0] . '__' . $option,
791
                                                  $option, $attrs));
792
        }
793
        $form->addElement('</li>'.NL);
794
    }
795
    $form->addElement('<li>');
796
    $form->addElement(form_makeButton('submit', '', $lang['btn_apply']));
797
    $form->addElement('</li>'.NL);
798
    $form->addElement('</ul>'.NL);
799
    $form->printForm();
800
}
801
802
/**
803
 * Returns type of sorting for the list of files in media manager
804
 *
805
 * @author Kate Arzamastseva <[email protected]>
806
 *
807
 * @return string - sort type
808
 */
809
function _media_get_sort_type() {
810
    return _media_get_display_param('sort', array('default' => 'name', 'date'));
811
}
812
813
/**
814
 * Returns type of listing for the list of files in media manager
815
 *
816
 * @author Kate Arzamastseva <[email protected]>
817
 *
818
 * @return string - list type
819
 */
820
function _media_get_list_type() {
821
    return _media_get_display_param('list', array('default' => 'thumbs', 'rows'));
822
}
823
824
/**
825
 * Get display parameters
826
 *
827
 * @param string $param   name of parameter
828
 * @param array  $values  allowed values, where default value has index key 'default'
829
 * @return string the parameter value
830
 */
831
function _media_get_display_param($param, $values) {
832
    global $INPUT;
833
    if (in_array($INPUT->str($param), $values)) {
834
        // FIXME: Set cookie
835
        return $INPUT->str($param);
836
    } else {
837
        $val = get_doku_pref($param, $values['default']);
838
        if (!in_array($val, $values)) {
839
            $val = $values['default'];
840
        }
841
        return $val;
842
    }
843
}
844
845
/**
846
 * Prints tab that displays a list of all files
847
 *
848
 * @author Kate Arzamastseva <[email protected]>
849
 *
850
 * @param string    $ns
851
 * @param null|int  $auth permission level
852
 * @param string    $jump item id
853
 */
854
function media_tab_files($ns,$auth=null,$jump='') {
855
    global $lang;
856
    if(is_null($auth)) $auth = auth_quickaclcheck("$ns:*");
857
858
    if($auth < AUTH_READ){
859
        echo '<div class="nothing">'.$lang['media_perm_read'].'</div>'.NL;
860
    }else{
861
        media_filelist($ns,$auth,$jump,true,_media_get_sort_type());
862
    }
863
}
864
865
/**
866
 * Prints tab that displays uploading form
867
 *
868
 * @author Kate Arzamastseva <[email protected]>
869
 *
870
 * @param string   $ns
871
 * @param null|int $auth permission level
872
 * @param string   $jump item id
873
 */
874
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...
875
    global $lang;
876
    if(is_null($auth)) $auth = auth_quickaclcheck("$ns:*");
877
878
    echo '<div class="upload">'.NL;
879
    if ($auth >= AUTH_UPLOAD) {
880
        echo '<p>' . $lang['mediaupload'] . '</p>';
881
    }
882
    media_uploadform($ns, $auth, true);
883
    echo '</div>'.NL;
884
}
885
886
/**
887
 * Prints tab that displays search form
888
 *
889
 * @author Kate Arzamastseva <[email protected]>
890
 *
891
 * @param string $ns
892
 * @param null|int $auth permission level
893
 */
894
function media_tab_search($ns,$auth=null) {
895
    global $INPUT;
896
897
    $do = $INPUT->str('mediado');
898
    $query = $INPUT->str('q');
899
    echo '<div class="search">'.NL;
900
901
    media_searchform($ns, $query, true);
902
    if ($do == 'searchlist' || $query) {
903
        media_searchlist($query,$ns,$auth,true,_media_get_sort_type());
904
    }
905
    echo '</div>'.NL;
906
}
907
908
/**
909
 * Prints tab that displays mediafile details
910
 *
911
 * @author Kate Arzamastseva <[email protected]>
912
 *
913
 * @param string     $image media id
914
 * @param string     $ns
915
 * @param null|int   $auth  permission level
916
 * @param string|int $rev   revision timestamp or empty string
917
 */
918
function media_tab_view($image, $ns, $auth=null, $rev='') {
919
    global $lang;
920
    if(is_null($auth)) $auth = auth_quickaclcheck("$ns:*");
921
922
    if ($image && $auth >= AUTH_READ) {
923
        $meta = new JpegMeta(mediaFN($image, $rev));
924
        media_preview($image, $auth, $rev, $meta);
925
        media_preview_buttons($image, $auth, $rev);
926
        media_details($image, $auth, $rev, $meta);
927
928
    } else {
929
        echo '<div class="nothing">'.$lang['media_perm_read'].'</div>'.NL;
930
    }
931
}
932
933
/**
934
 * Prints tab that displays form for editing mediafile metadata
935
 *
936
 * @author Kate Arzamastseva <[email protected]>
937
 *
938
 * @param string     $image media id
939
 * @param string     $ns
940
 * @param null|int   $auth permission level
941
 */
942
function media_tab_edit($image, $ns, $auth=null) {
943
    if(is_null($auth)) $auth = auth_quickaclcheck("$ns:*");
944
945
    if ($image) {
946
        list(, $mime) = mimetype($image);
947
        if ($mime == 'image/jpeg') media_metaform($image,$auth);
948
    }
949
}
950
951
/**
952
 * Prints tab that displays mediafile revisions
953
 *
954
 * @author Kate Arzamastseva <[email protected]>
955
 *
956
 * @param string     $image media id
957
 * @param string     $ns
958
 * @param null|int   $auth permission level
959
 */
960
function media_tab_history($image, $ns, $auth=null) {
961
    global $lang;
962
    global $INPUT;
963
964
    if(is_null($auth)) $auth = auth_quickaclcheck("$ns:*");
965
    $do = $INPUT->str('mediado');
966
967
    if ($auth >= AUTH_READ && $image) {
968
        if ($do == 'diff'){
969
            media_diff($image, $ns, $auth);
970
        } else {
971
            $first = $INPUT->int('first');
972
            html_revisions($first, $image);
973
        }
974
    } else {
975
        echo '<div class="nothing">'.$lang['media_perm_read'].'</div>'.NL;
976
    }
977
}
978
979
/**
980
 * Prints mediafile details
981
 *
982
 * @param string         $image media id
983
 * @param int            $auth permission level
984
 * @param int|string     $rev revision timestamp or empty string
985
 * @param JpegMeta|bool  $meta
986
 *
987
 * @author Kate Arzamastseva <[email protected]>
988
 */
989
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...
990
991
    $size = media_image_preview_size($image, $rev, $meta);
992
993
    if ($size) {
994
        global $lang;
995
        echo '<div class="image">';
996
997
        $more = array();
998
        if ($rev) {
999
            $more['rev'] = $rev;
1000
        } else {
1001
            $t = @filemtime(mediaFN($image));
1002
            $more['t'] = $t;
1003
        }
1004
1005
        $more['w'] = $size[0];
1006
        $more['h'] = $size[1];
1007
        $src = ml($image, $more);
1008
1009
        echo '<a href="'.$src.'" target="_blank" title="'.$lang['mediaview'].'">';
1010
        echo '<img src="'.$src.'" alt="" style="max-width: '.$size[0].'px;" />';
1011
        echo '</a>';
1012
1013
        echo '</div>'.NL;
1014
    }
1015
}
1016
1017
/**
1018
 * Prints mediafile action buttons
1019
 *
1020
 * @author Kate Arzamastseva <[email protected]>
1021
 *
1022
 * @param string     $image media id
1023
 * @param int        $auth  permission level
1024
 * @param string|int $rev   revision timestamp, or empty string
1025
 */
1026
function media_preview_buttons($image, $auth, $rev='') {
1027
    global $lang, $conf;
1028
1029
    echo '<ul class="actions">'.NL;
1030
1031
    if($auth >= AUTH_DELETE && !$rev && file_exists(mediaFN($image))){
1032
1033
        // delete button
1034
        $form = new Doku_Form(array('id' => 'mediamanager__btn_delete',
1035
            'action'=>media_managerURL(array('delete' => $image), '&')));
1036
        $form->addElement(form_makeButton('submit','',$lang['btn_delete']));
1037
        echo '<li>';
1038
        $form->printForm();
1039
        echo '</li>'.NL;
1040
    }
1041
1042
    $auth_ow = (($conf['mediarevisions']) ? AUTH_UPLOAD : AUTH_DELETE);
1043
    if($auth >= $auth_ow && !$rev){
1044
1045
        // upload new version button
1046
        $form = new Doku_Form(array('id' => 'mediamanager__btn_update',
1047
            'action'=>media_managerURL(array('image' => $image, 'mediado' => 'update'), '&')));
1048
        $form->addElement(form_makeButton('submit','',$lang['media_update']));
1049
        echo '<li>';
1050
        $form->printForm();
1051
        echo '</li>'.NL;
1052
    }
1053
1054
    if($auth >= AUTH_UPLOAD && $rev && $conf['mediarevisions'] && file_exists(mediaFN($image, $rev))){
1055
1056
        // restore button
1057
        $form = new Doku_Form(array('id' => 'mediamanager__btn_restore',
1058
            'action'=>media_managerURL(array('image' => $image), '&')));
1059
        $form->addHidden('mediado','restore');
1060
        $form->addHidden('rev',$rev);
1061
        $form->addElement(form_makeButton('submit','',$lang['media_restore']));
1062
        echo '<li>';
1063
        $form->printForm();
1064
        echo '</li>'.NL;
1065
    }
1066
1067
    echo '</ul>'.NL;
1068
}
1069
1070
/**
1071
 * Returns image width and height for mediamanager preview panel
1072
 *
1073
 * @author Kate Arzamastseva <[email protected]>
1074
 * @param string         $image
1075
 * @param int|string     $rev
1076
 * @param JpegMeta|bool  $meta
1077
 * @param int            $size
1078
 * @return array|false
1079
 */
1080
function media_image_preview_size($image, $rev, $meta, $size = 500) {
1081
    if (!preg_match("/\.(jpe?g|gif|png)$/", $image) || !file_exists(mediaFN($image, $rev))) return false;
1082
1083
    $info = getimagesize(mediaFN($image, $rev));
1084
    $w = (int) $info[0];
1085
    $h = (int) $info[1];
1086
1087
    if($meta && ($w > $size || $h > $size)){
1088
        $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...
1089
        $w = floor($w * $ratio);
1090
        $h = floor($h * $ratio);
1091
    }
1092
    return array($w, $h);
1093
}
1094
1095
/**
1096
 * Returns the requested EXIF/IPTC tag from the image meta
1097
 *
1098
 * @author Kate Arzamastseva <[email protected]>
1099
 *
1100
 * @param array    $tags array with tags, first existing is returned
1101
 * @param JpegMeta $meta
1102
 * @param string   $alt  alternative value
1103
 * @return string
1104
 */
1105
function media_getTag($tags,$meta,$alt=''){
1106
    if($meta === false) return $alt;
1107
    $info = $meta->getField($tags);
1108
    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...
1109
    return $info;
1110
}
1111
1112
/**
1113
 * Returns mediafile tags
1114
 *
1115
 * @author Kate Arzamastseva <[email protected]>
1116
 *
1117
 * @param JpegMeta $meta
1118
 * @return array list of tags of the mediafile
1119
 */
1120
function media_file_tags($meta) {
1121
    // load the field descriptions
1122
    static $fields = null;
1123
    if(is_null($fields)){
1124
        $config_files = getConfigFiles('mediameta');
1125
        foreach ($config_files as $config_file) {
1126
            if(file_exists($config_file)) include($config_file);
1127
        }
1128
    }
1129
1130
    $tags = array();
1131
1132
    foreach($fields as $key => $tag){
0 ignored issues
show
Bug introduced by
The expression $fields of type null is not traversable.
Loading history...
1133
        $t = array();
1134
        if (!empty($tag[0])) $t = array($tag[0]);
1135
        if(isset($tag[3]) && is_array($tag[3])) $t = array_merge($t,$tag[3]);
1136
        $value = media_getTag($t, $meta);
1137
        $tags[] = array('tag' => $tag, 'value' => $value);
1138
    }
1139
1140
    return $tags;
1141
}
1142
1143
/**
1144
 * Prints mediafile tags
1145
 *
1146
 * @author Kate Arzamastseva <[email protected]>
1147
 *
1148
 * @param string        $image image id
1149
 * @param int           $auth  permission level
1150
 * @param string|int    $rev   revision timestamp, or empty string
1151
 * @param bool|JpegMeta $meta  image object, or create one if false
1152
 */
1153
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...
1154
    global $lang;
1155
1156
    if (!$meta) $meta = new JpegMeta(mediaFN($image, $rev));
1157
    $tags = media_file_tags($meta);
0 ignored issues
show
Bug introduced by
It seems like $meta defined by parameter $meta on line 1153 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...
1158
1159
    echo '<dl>'.NL;
1160
    foreach($tags as $tag){
1161
        if ($tag['value']) {
1162
            $value = cleanText($tag['value']);
1163
            echo '<dt>'.$lang[$tag['tag'][1]].'</dt><dd>';
1164
            if ($tag['tag'][2] == 'date') echo dformat($value);
1165
            else echo hsc($value);
1166
            echo '</dd>'.NL;
1167
        }
1168
    }
1169
    echo '</dl>'.NL;
1170
    echo '<dl>'.NL;
1171
    echo '<dt>'.$lang['reference'].':</dt>';
1172
    $media_usage = ft_mediause($image,true);
1173
    if(count($media_usage) > 0){
1174
        foreach($media_usage as $path){
1175
            echo '<dd>'.html_wikilink($path).'</dd>';
1176
        }
1177
    }else{
1178
        echo '<dd>'.$lang['nothingfound'].'</dd>';
1179
    }
1180
    echo '</dl>'.NL;
1181
1182
}
1183
1184
/**
1185
 * Shows difference between two revisions of file
1186
 *
1187
 * @author Kate Arzamastseva <[email protected]>
1188
 *
1189
 * @param string $image  image id
1190
 * @param string $ns
1191
 * @param int $auth permission level
1192
 * @param bool $fromajax
1193
 * @return false|null|string
1194
 */
1195
function media_diff($image, $ns, $auth, $fromajax = false) {
1196
    global $conf;
1197
    global $INPUT;
1198
1199
    if ($auth < AUTH_READ || !$image || !$conf['mediarevisions']) return '';
1200
1201
    $rev1 = $INPUT->int('rev');
1202
1203
    $rev2 = $INPUT->ref('rev2');
1204
    if(is_array($rev2)){
1205
        $rev1 = (int) $rev2[0];
1206
        $rev2 = (int) $rev2[1];
1207
1208
        if(!$rev1){
1209
            $rev1 = $rev2;
1210
            unset($rev2);
1211
        }
1212
    }else{
1213
        $rev2 = $INPUT->int('rev2');
1214
    }
1215
1216
    if ($rev1 && !file_exists(mediaFN($image, $rev1))) $rev1 = false;
1217
    if ($rev2 && !file_exists(mediaFN($image, $rev2))) $rev2 = false;
1218
1219
    if($rev1 && $rev2){            // two specific revisions wanted
1220
        // make sure order is correct (older on the left)
1221
        if($rev1 < $rev2){
1222
            $l_rev = $rev1;
1223
            $r_rev = $rev2;
1224
        }else{
1225
            $l_rev = $rev2;
1226
            $r_rev = $rev1;
1227
        }
1228
    }elseif($rev1){                // single revision given, compare to current
1229
        $r_rev = '';
1230
        $l_rev = $rev1;
1231
    }else{                        // no revision was given, compare previous to current
1232
        $r_rev = '';
1233
        $medialog = new MediaChangeLog($image);
1234
        $revs = $medialog->getRevisions(0, 1);
1235
        if (file_exists(mediaFN($image, $revs[0]))) {
1236
            $l_rev = $revs[0];
1237
        } else {
1238
            $l_rev = '';
1239
        }
1240
    }
1241
1242
    // prepare event data
1243
    $data = array();
1244
    $data[0] = $image;
1245
    $data[1] = $l_rev;
1246
    $data[2] = $r_rev;
1247
    $data[3] = $ns;
1248
    $data[4] = $auth;
1249
    $data[5] = $fromajax;
1250
1251
    // trigger event
1252
    return trigger_event('MEDIA_DIFF', $data, '_media_file_diff', true);
1253
}
1254
1255
/**
1256
 * Callback for media file diff
1257
 *
1258
 * @param array $data event data
1259
 * @return false|null
1260
 */
1261
function _media_file_diff($data) {
1262
    if(is_array($data) && count($data)===6) {
1263
        media_file_diff($data[0], $data[1], $data[2], $data[3], $data[4], $data[5]);
1264
    } else {
1265
        return false;
1266
    }
1267
}
1268
1269
/**
1270
 * Shows difference between two revisions of image
1271
 *
1272
 * @author Kate Arzamastseva <[email protected]>
1273
 *
1274
 * @param string $image
1275
 * @param string|int $l_rev revision timestamp, or empty string
1276
 * @param string|int $r_rev revision timestamp, or empty string
1277
 * @param string $ns
1278
 * @param int $auth permission level
1279
 * @param bool $fromajax
1280
 */
1281
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...
1282
    global $lang;
1283
    global $INPUT;
1284
1285
    $l_meta = new JpegMeta(mediaFN($image, $l_rev));
1286
    $r_meta = new JpegMeta(mediaFN($image, $r_rev));
1287
1288
    $is_img = preg_match('/\.(jpe?g|gif|png)$/', $image);
1289
    if ($is_img) {
1290
        $l_size = media_image_preview_size($image, $l_rev, $l_meta);
1291
        $r_size = media_image_preview_size($image, $r_rev, $r_meta);
1292
        $is_img = ($l_size && $r_size && ($l_size[0] >= 30 || $r_size[0] >= 30));
1293
1294
        $difftype = $INPUT->str('difftype');
1295
1296
        if (!$fromajax) {
1297
            $form = new Doku_Form(array(
1298
                'action' => media_managerURL(array(), '&'),
1299
                'method' => 'get',
1300
                'id' => 'mediamanager__form_diffview',
1301
                'class' => 'diffView'
1302
            ));
1303
            $form->addHidden('sectok', null);
1304
            $form->addElement('<input type="hidden" name="rev2[]" value="'.$l_rev.'" ></input>');
1305
            $form->addElement('<input type="hidden" name="rev2[]" value="'.$r_rev.'" ></input>');
1306
            $form->addHidden('mediado', 'diff');
1307
            $form->printForm();
1308
1309
            echo NL.'<div id="mediamanager__diff" >'.NL;
1310
        }
1311
1312
        if ($difftype == 'opacity' || $difftype == 'portions') {
1313
            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 1290 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 1291 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...
1314
            if (!$fromajax) echo '</div>';
1315
            return;
1316
        }
1317
    }
1318
1319
    list($l_head, $r_head) = html_diff_head($l_rev, $r_rev, $image, true);
1320
1321
    ?>
1322
    <div class="table">
1323
    <table>
1324
      <tr>
1325
        <th><?php echo $l_head; ?></th>
1326
        <th><?php echo $r_head; ?></th>
1327
      </tr>
1328
    <?php
1329
1330
    echo '<tr class="image">';
1331
    echo '<td>';
1332
    media_preview($image, $auth, $l_rev, $l_meta);
1333
    echo '</td>';
1334
1335
    echo '<td>';
1336
    media_preview($image, $auth, $r_rev, $r_meta);
1337
    echo '</td>';
1338
    echo '</tr>'.NL;
1339
1340
    echo '<tr class="actions">';
1341
    echo '<td>';
1342
    media_preview_buttons($image, $auth, $l_rev);
1343
    echo '</td>';
1344
1345
    echo '<td>';
1346
    media_preview_buttons($image, $auth, $r_rev);
1347
    echo '</td>';
1348
    echo '</tr>'.NL;
1349
1350
    $l_tags = media_file_tags($l_meta);
1351
    $r_tags = media_file_tags($r_meta);
1352
    // FIXME r_tags-only stuff
1353
    foreach ($l_tags as $key => $l_tag) {
1354
        if ($l_tag['value'] != $r_tags[$key]['value']) {
1355
            $r_tags[$key]['highlighted'] = true;
1356
            $l_tags[$key]['highlighted'] = true;
1357
        } else if (!$l_tag['value'] || !$r_tags[$key]['value']) {
1358
            unset($r_tags[$key]);
1359
            unset($l_tags[$key]);
1360
        }
1361
    }
1362
1363
    echo '<tr>';
1364
    foreach(array($l_tags,$r_tags) as $tags){
1365
        echo '<td>'.NL;
1366
1367
        echo '<dl class="img_tags">';
1368
        foreach($tags as $tag){
1369
            $value = cleanText($tag['value']);
1370
            if (!$value) $value = '-';
1371
            echo '<dt>'.$lang[$tag['tag'][1]].'</dt>';
1372
            echo '<dd>';
1373
            if ($tag['highlighted']) {
1374
                echo '<strong>';
1375
            }
1376
            if ($tag['tag'][2] == 'date') echo dformat($value);
1377
            else echo hsc($value);
1378
            if ($tag['highlighted']) {
1379
                echo '</strong>';
1380
            }
1381
            echo '</dd>';
1382
        }
1383
        echo '</dl>'.NL;
1384
1385
        echo '</td>';
1386
    }
1387
    echo '</tr>'.NL;
1388
1389
    echo '</table>'.NL;
1390
    echo '</div>'.NL;
1391
1392
    if ($is_img && !$fromajax) echo '</div>';
1393
}
1394
1395
/**
1396
 * Prints two images side by side
1397
 * and slider
1398
 *
1399
 * @author Kate Arzamastseva <[email protected]>
1400
 *
1401
 * @param string $image   image id
1402
 * @param int    $l_rev   revision timestamp, or empty string
1403
 * @param int    $r_rev   revision timestamp, or empty string
1404
 * @param array  $l_size  array with width and height
1405
 * @param array  $r_size  array with width and height
1406
 * @param string $type
1407
 */
1408
function media_image_diff($image, $l_rev, $r_rev, $l_size, $r_size, $type) {
1409
    if ($l_size != $r_size) {
1410
        if ($r_size[0] > $l_size[0]) {
1411
            $l_size = $r_size;
1412
        }
1413
    }
1414
1415
    $l_more = array('rev' => $l_rev, 'h' => $l_size[1], 'w' => $l_size[0]);
1416
    $r_more = array('rev' => $r_rev, 'h' => $l_size[1], 'w' => $l_size[0]);
1417
1418
    $l_src = ml($image, $l_more);
1419
    $r_src = ml($image, $r_more);
1420
1421
    // slider
1422
    echo '<div class="slider" style="max-width: '.($l_size[0]-20).'px;" ></div>'.NL;
1423
1424
    // two images in divs
1425
    echo '<div class="imageDiff ' . $type . '">'.NL;
1426
    echo '<div class="image1" style="max-width: '.$l_size[0].'px;">';
1427
    echo '<img src="'.$l_src.'" alt="" />';
1428
    echo '</div>'.NL;
1429
    echo '<div class="image2" style="max-width: '.$l_size[0].'px;">';
1430
    echo '<img src="'.$r_src.'" alt="" />';
1431
    echo '</div>'.NL;
1432
    echo '</div>'.NL;
1433
}
1434
1435
/**
1436
 * Restores an old revision of a media file
1437
 *
1438
 * @param string $image media id
1439
 * @param int    $rev   revision timestamp or empty string
1440
 * @param int    $auth
1441
 * @return string - file's id
1442
 *
1443
 * @author Kate Arzamastseva <[email protected]>
1444
 */
1445
function media_restore($image, $rev, $auth){
1446
    global $conf;
1447
    if ($auth < AUTH_UPLOAD || !$conf['mediarevisions']) return false;
1448
    $removed = (!file_exists(mediaFN($image)) && file_exists(mediaMetaFN($image, '.changes')));
1449
    if (!$image || (!file_exists(mediaFN($image)) && !$removed)) return false;
1450
    if (!$rev || !file_exists(mediaFN($image, $rev))) return false;
1451
    list(,$imime,) = mimetype($image);
1452
    $res = media_upload_finish(mediaFN($image, $rev),
1453
        mediaFN($image),
1454
        $image,
1455
        $imime,
1456
        true,
1457
        'copy');
1458
    if (is_array($res)) {
1459
        msg($res[0], $res[1]);
1460
        return false;
1461
    }
1462
    return $res;
1463
}
1464
1465
/**
1466
 * List all files found by the search request
1467
 *
1468
 * @author Tobias Sarnowski <[email protected]>
1469
 * @author Andreas Gohr <[email protected]>
1470
 * @author Kate Arzamastseva <[email protected]>
1471
 * @triggers MEDIA_SEARCH
1472
 *
1473
 * @param string $query
1474
 * @param string $ns
1475
 * @param null|int $auth
1476
 * @param bool $fullscreen
1477
 * @param string $sort
1478
 */
1479
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...
1480
    global $conf;
1481
    global $lang;
1482
1483
    $ns = cleanID($ns);
1484
    $evdata = array(
1485
        'ns'    => $ns,
1486
        'data'  => array(),
1487
        'query' => $query
1488
    );
1489
    if (!blank($query)) {
1490
        $evt = new Doku_Event('MEDIA_SEARCH', $evdata);
1491
        if ($evt->advise_before()) {
1492
            $dir = utf8_encodeFN(str_replace(':','/',$evdata['ns']));
1493
            $quoted = preg_quote($evdata['query'],'/');
1494
            //apply globbing
1495
            $quoted = str_replace(array('\*', '\?'), array('.*', '.'), $quoted, $count);
1496
1497
            //if we use globbing file name must match entirely but may be preceded by arbitrary namespace
1498
            if ($count > 0) $quoted = '^([^:]*:)*'.$quoted.'$';
1499
1500
            $pattern = '/'.$quoted.'/i';
1501
            search($evdata['data'],
1502
                    $conf['mediadir'],
1503
                    'search_media',
1504
                    array('showmsg'=>false,'pattern'=>$pattern),
1505
                    $dir,
1506
                    1,
1507
                    $sort);
1508
        }
1509
        $evt->advise_after();
1510
        unset($evt);
1511
    }
1512
1513
    if (!$fullscreen) {
1514
        echo '<h1 id="media__ns">'.sprintf($lang['searchmedia_in'],hsc($ns).':*').'</h1>'.NL;
1515
        media_searchform($ns,$query);
1516
    }
1517
1518
    if(!count($evdata['data'])){
1519
        echo '<div class="nothing">'.$lang['nothingfound'].'</div>'.NL;
1520
    }else {
1521
        if ($fullscreen) {
1522
            echo '<ul class="' . _media_get_list_type() . '">';
1523
        }
1524
        foreach($evdata['data'] as $item){
1525
            if (!$fullscreen) media_printfile($item,$item['perm'],'',true);
1526
            else media_printfile_thumbs($item,$item['perm'],false,true);
1527
        }
1528
        if ($fullscreen) echo '</ul>'.NL;
1529
    }
1530
}
1531
1532
/**
1533
 * Formats and prints one file in the list
1534
 *
1535
 * @param array     $item
1536
 * @param int       $auth              permission level
1537
 * @param string    $jump              item id
1538
 * @param bool      $display_namespace
1539
 */
1540
function media_printfile($item,$auth,$jump,$display_namespace=false){
1541
    global $lang;
1542
1543
    // Prepare zebra coloring
1544
    // I always wanted to use this variable name :-D
1545
    static $twibble = 1;
1546
    $twibble *= -1;
1547
    $zebra = ($twibble == -1) ? 'odd' : 'even';
1548
1549
    // Automatically jump to recent action
1550
    if($jump == $item['id']) {
1551
        $jump = ' id="scroll__here" ';
1552
    }else{
1553
        $jump = '';
1554
    }
1555
1556
    // Prepare fileicons
1557
    list($ext) = mimetype($item['file'],false);
1558
    $class = preg_replace('/[^_\-a-z0-9]+/i','_',$ext);
1559
    $class = 'select mediafile mf_'.$class;
1560
1561
    // Prepare filename
1562
    $file = utf8_decodeFN($item['file']);
1563
1564
    // Prepare info
1565
    $info = '';
1566
    if($item['isimg']){
1567
        $info .= (int) $item['meta']->getField('File.Width');
1568
        $info .= '&#215;';
1569
        $info .= (int) $item['meta']->getField('File.Height');
1570
        $info .= ' ';
1571
    }
1572
    $info .= '<i>'.dformat($item['mtime']).'</i>';
1573
    $info .= ' ';
1574
    $info .= filesize_h($item['size']);
1575
1576
    // output
1577
    echo '<div class="'.$zebra.'"'.$jump.' title="'.hsc($item['id']).'">'.NL;
1578
    if (!$display_namespace) {
1579
        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 1562 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...
1580
    } else {
1581
        echo '<a id="h_:'.$item['id'].'" class="'.$class.'">'.hsc($item['id']).'</a><br/>';
1582
    }
1583
    echo '<span class="info">('.$info.')</span>'.NL;
1584
1585
    // view button
1586
    $link = ml($item['id'],'',true);
1587
    echo ' <a href="'.$link.'" target="_blank"><img src="'.DOKU_BASE.'lib/images/magnifier.png" '.
1588
        'alt="'.$lang['mediaview'].'" title="'.$lang['mediaview'].'" class="btn" /></a>';
1589
1590
    // mediamanager button
1591
    $link = wl('',array('do'=>'media','image'=>$item['id'],'ns'=>getNS($item['id'])));
1592
    echo ' <a href="'.$link.'" target="_blank"><img src="'.DOKU_BASE.'lib/images/mediamanager.png" '.
1593
        'alt="'.$lang['btn_media'].'" title="'.$lang['btn_media'].'" class="btn" /></a>';
1594
1595
    // delete button
1596
    if($item['writable'] && $auth >= AUTH_DELETE){
1597
        $link = DOKU_BASE.'lib/exe/mediamanager.php?delete='.rawurlencode($item['id']).
1598
            '&amp;sectok='.getSecurityToken();
1599
        echo ' <a href="'.$link.'" class="btn_media_delete" title="'.$item['id'].'">'.
1600
            '<img src="'.DOKU_BASE.'lib/images/trash.png" alt="'.$lang['btn_delete'].'" '.
1601
            'title="'.$lang['btn_delete'].'" class="btn" /></a>';
1602
    }
1603
1604
    echo '<div class="example" id="ex_'.str_replace(':','_',$item['id']).'">';
1605
    echo $lang['mediausage'].' <code>{{:'.$item['id'].'}}</code>';
1606
    echo '</div>';
1607
    if($item['isimg']) media_printimgdetail($item);
1608
    echo '<div class="clearer"></div>'.NL;
1609
    echo '</div>'.NL;
1610
}
1611
1612
/**
1613
 * Display a media icon
1614
 *
1615
 * @param string $filename media id
1616
 * @param string $size     the size subfolder, if not specified 16x16 is used
1617
 * @return string html
1618
 */
1619
function media_printicon($filename, $size=''){
1620
    list($ext) = mimetype(mediaFN($filename),false);
1621
1622
    if (file_exists(DOKU_INC.'lib/images/fileicons/'.$size.'/'.$ext.'.png')) {
1623
        $icon = DOKU_BASE.'lib/images/fileicons/'.$size.'/'.$ext.'.png';
1624
    } else {
1625
        $icon = DOKU_BASE.'lib/images/fileicons/'.$size.'/file.png';
1626
    }
1627
1628
    return '<img src="'.$icon.'" alt="'.$filename.'" class="icon" />';
1629
}
1630
1631
/**
1632
 * Formats and prints one file in the list in the thumbnails view
1633
 *
1634
 * @author Kate Arzamastseva <[email protected]>
1635
 *
1636
 * @param array       $item
1637
 * @param int         $auth              permission level
1638
 * @param bool|string $jump              item id
1639
 * @param bool        $display_namespace
1640
 */
1641
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...
1642
1643
    // Prepare filename
1644
    $file = utf8_decodeFN($item['file']);
1645
1646
    // output
1647
    echo '<li><dl title="'.hsc($item['id']).'">'.NL;
1648
1649
        echo '<dt>';
1650
    if($item['isimg']) {
1651
        media_printimgdetail($item, true);
1652
1653
    } else {
1654
        echo '<a id="d_:'.$item['id'].'" class="image" title="'.$item['id'].'" href="'.
1655
            media_managerURL(array('image' => hsc($item['id']), 'ns' => getNS($item['id']),
1656
            'tab_details' => 'view')).'">';
1657
        echo media_printicon($item['id'], '32x32');
1658
        echo '</a>';
1659
    }
1660
    echo '</dt>'.NL;
1661
    if (!$display_namespace) {
1662
        $name = hsc($file);
0 ignored issues
show
Security Bug introduced by
It seems like $file defined by utf8_decodeFN($item['file']) on line 1644 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...
1663
    } else {
1664
        $name = hsc($item['id']);
1665
    }
1666
    echo '<dd class="name"><a href="'.media_managerURL(array('image' => hsc($item['id']), 'ns' => getNS($item['id']),
1667
        'tab_details' => 'view')).'" id="h_:'.$item['id'].'">'.$name.'</a></dd>'.NL;
1668
1669
    if($item['isimg']){
1670
        $size = '';
1671
        $size .= (int) $item['meta']->getField('File.Width');
1672
        $size .= '&#215;';
1673
        $size .= (int) $item['meta']->getField('File.Height');
1674
        echo '<dd class="size">'.$size.'</dd>'.NL;
1675
    } else {
1676
        echo '<dd class="size">&#160;</dd>'.NL;
1677
    }
1678
    $date = dformat($item['mtime']);
1679
    echo '<dd class="date">'.$date.'</dd>'.NL;
1680
    $filesize = filesize_h($item['size']);
1681
    echo '<dd class="filesize">'.$filesize.'</dd>'.NL;
1682
    echo '</dl></li>'.NL;
1683
}
1684
1685
/**
1686
 * Prints a thumbnail and metainfo
1687
 *
1688
 * @param array $item
1689
 * @param bool  $fullscreen
1690
 */
1691
function media_printimgdetail($item, $fullscreen=false){
1692
    // prepare thumbnail
1693
    $size = $fullscreen ? 90 : 120;
1694
1695
    $w = (int) $item['meta']->getField('File.Width');
1696
    $h = (int) $item['meta']->getField('File.Height');
1697
    if($w>$size || $h>$size){
1698
        if (!$fullscreen) {
1699
            $ratio = $item['meta']->getResizeRatio($size);
1700
        } else {
1701
            $ratio = $item['meta']->getResizeRatio($size,$size);
1702
        }
1703
        $w = floor($w * $ratio);
1704
        $h = floor($h * $ratio);
1705
    }
1706
    $src = ml($item['id'],array('w'=>$w,'h'=>$h,'t'=>$item['mtime']));
1707
    $p = array();
1708
    if (!$fullscreen) {
1709
        // In fullscreen mediamanager view, image resizing is done via CSS.
1710
        $p['width']  = $w;
1711
        $p['height'] = $h;
1712
    }
1713
    $p['alt']    = $item['id'];
1714
    $att = buildAttributes($p);
1715
1716
    // output
1717
    if ($fullscreen) {
1718
        echo '<a id="l_:'.$item['id'].'" class="image thumb" href="'.
1719
            media_managerURL(array('image' => hsc($item['id']), 'ns' => getNS($item['id']), 'tab_details' => 'view')).'">';
1720
        echo '<img src="'.$src.'" '.$att.' />';
1721
        echo '</a>';
1722
    }
1723
1724
    if ($fullscreen) return;
1725
1726
    echo '<div class="detail">';
1727
    echo '<div class="thumb">';
1728
    echo '<a id="d_:'.$item['id'].'" class="select">';
1729
    echo '<img src="'.$src.'" '.$att.' />';
1730
    echo '</a>';
1731
    echo '</div>';
1732
1733
    // read EXIF/IPTC data
1734
    $t = $item['meta']->getField(array('IPTC.Headline','xmp.dc:title'));
1735
    $d = $item['meta']->getField(array('IPTC.Caption','EXIF.UserComment',
1736
                'EXIF.TIFFImageDescription',
1737
                'EXIF.TIFFUserComment'));
1738
    if(utf8_strlen($d) > 250) $d = utf8_substr($d,0,250).'...';
1739
    $k = $item['meta']->getField(array('IPTC.Keywords','IPTC.Category','xmp.dc:subject'));
1740
1741
    // print EXIF/IPTC data
1742
    if($t || $d || $k ){
1743
        echo '<p>';
1744
        if($t) echo '<strong>'.hsc($t).'</strong><br />';
1745
        if($d) echo hsc($d).'<br />';
1746
        if($t) echo '<em>'.hsc($k).'</em>';
1747
        echo '</p>';
1748
    }
1749
    echo '</div>';
1750
}
1751
1752
/**
1753
 * Build link based on the current, adding/rewriting parameters
1754
 *
1755
 * @author Kate Arzamastseva <[email protected]>
1756
 *
1757
 * @param array|bool $params
1758
 * @param string     $amp           separator
1759
 * @param bool       $abs           absolute url?
1760
 * @param bool       $params_array  return the parmeters array?
1761
 * @return string|array - link or link parameters
1762
 */
1763
function media_managerURL($params=false, $amp='&amp;', $abs=false, $params_array=false) {
1764
    global $ID;
1765
    global $INPUT;
1766
1767
    $gets = array('do' => 'media');
1768
    $media_manager_params = array('tab_files', 'tab_details', 'image', 'ns', 'list', 'sort');
1769
    foreach ($media_manager_params as $x) {
1770
        if ($INPUT->has($x)) $gets[$x] = $INPUT->str($x);
1771
    }
1772
1773
    if ($params) {
1774
        $gets = $params + $gets;
1775
    }
1776
    unset($gets['id']);
1777
    if (isset($gets['delete'])) {
1778
        unset($gets['image']);
1779
        unset($gets['tab_details']);
1780
    }
1781
1782
    if ($params_array) return $gets;
1783
1784
    return wl($ID,$gets,$abs,$amp);
1785
}
1786
1787
/**
1788
 * Print the media upload form if permissions are correct
1789
 *
1790
 * @author Andreas Gohr <[email protected]>
1791
 * @author Kate Arzamastseva <[email protected]>
1792
 *
1793
 * @param string $ns
1794
 * @param int    $auth permission level
1795
 * @param bool  $fullscreen
1796
 */
1797
function media_uploadform($ns, $auth, $fullscreen = false){
1798
    global $lang;
1799
    global $conf;
1800
    global $INPUT;
1801
1802
    if($auth < AUTH_UPLOAD) {
1803
        echo '<div class="nothing">'.$lang['media_perm_upload'].'</div>'.NL;
1804
        return;
1805
    }
1806
    $auth_ow = (($conf['mediarevisions']) ? AUTH_UPLOAD : AUTH_DELETE);
1807
1808
    $update = false;
1809
    $id = '';
1810
    if ($auth >= $auth_ow && $fullscreen && $INPUT->str('mediado') == 'update') {
1811
        $update = true;
1812
        $id = cleanID($INPUT->str('image'));
1813
    }
1814
1815
    // The default HTML upload form
1816
    $params = array('id'      => 'dw__upload',
1817
                    'enctype' => 'multipart/form-data');
1818
    if (!$fullscreen) {
1819
        $params['action'] = DOKU_BASE.'lib/exe/mediamanager.php';
1820
    } else {
1821
        $params['action'] = media_managerURL(array('tab_files' => 'files',
1822
            'tab_details' => 'view'), '&');
1823
    }
1824
1825
    $form = new Doku_Form($params);
1826
    if (!$fullscreen) echo '<div class="upload">' . $lang['mediaupload'] . '</div>';
1827
    $form->addElement(formSecurityToken());
1828
    $form->addHidden('ns', hsc($ns));
1829
    $form->addElement(form_makeOpenTag('p'));
1830
    $form->addElement(form_makeFileField('upload', $lang['txt_upload'], 'upload__file'));
1831
    $form->addElement(form_makeCloseTag('p'));
1832
    $form->addElement(form_makeOpenTag('p'));
1833
    $form->addElement(form_makeTextField('mediaid', noNS($id), $lang['txt_filename'], 'upload__name'));
1834
    $form->addElement(form_makeButton('submit', '', $lang['btn_upload']));
1835
    $form->addElement(form_makeCloseTag('p'));
1836
1837
    if($auth >= $auth_ow){
1838
        $form->addElement(form_makeOpenTag('p'));
1839
        $attrs = array();
1840
        if ($update) $attrs['checked'] = 'checked';
1841
        $form->addElement(form_makeCheckboxField('ow', 1, $lang['txt_overwrt'], 'dw__ow', 'check', $attrs));
1842
        $form->addElement(form_makeCloseTag('p'));
1843
    }
1844
1845
    echo NL.'<div id="mediamanager__uploader">'.NL;
1846
    html_form('upload', $form);
1847
1848
    echo '</div>'.NL;
1849
1850
    echo '<p class="maxsize">';
1851
    printf($lang['maxuploadsize'],filesize_h(media_getuploadsize()));
1852
    echo '</p>'.NL;
1853
1854
}
1855
1856
/**
1857
 * Returns the size uploaded files may have
1858
 *
1859
 * This uses a conservative approach using the lowest number found
1860
 * in any of the limiting ini settings
1861
 *
1862
 * @returns int size in bytes
1863
 */
1864
function media_getuploadsize(){
1865
    $okay = 0;
1866
1867
    $post = (int) php_to_byte(@ini_get('post_max_size'));
1868
    $suho = (int) php_to_byte(@ini_get('suhosin.post.max_value_length'));
1869
    $upld = (int) php_to_byte(@ini_get('upload_max_filesize'));
1870
1871
    if($post && ($post < $okay || $okay == 0)) $okay = $post;
1872
    if($suho && ($suho < $okay || $okay == 0)) $okay = $suho;
1873
    if($upld && ($upld < $okay || $okay == 0)) $okay = $upld;
1874
1875
    return $okay;
1876
}
1877
1878
/**
1879
 * Print the search field form
1880
 *
1881
 * @author Tobias Sarnowski <[email protected]>
1882
 * @author Kate Arzamastseva <[email protected]>
1883
 *
1884
 * @param string $ns
1885
 * @param string $query
1886
 * @param bool $fullscreen
1887
 */
1888
function media_searchform($ns,$query='',$fullscreen=false){
1889
    global $lang;
1890
1891
    // The default HTML search form
1892
    $params = array('id' => 'dw__mediasearch');
1893
    if (!$fullscreen) {
1894
        $params['action'] = DOKU_BASE.'lib/exe/mediamanager.php';
1895
    } else {
1896
        $params['action'] = media_managerURL(array(), '&');
1897
    }
1898
    $form = new Doku_Form($params);
1899
    $form->addHidden('ns', $ns);
1900
    $form->addHidden($fullscreen ? 'mediado' : 'do', 'searchlist');
1901
1902
    $form->addElement(form_makeOpenTag('p'));
1903
    $form->addElement(form_makeTextField('q', $query,$lang['searchmedia'],'','',array('title'=>sprintf($lang['searchmedia_in'],hsc($ns).':*'))));
1904
    $form->addElement(form_makeButton('submit', '', $lang['btn_search']));
1905
    $form->addElement(form_makeCloseTag('p'));
1906
    html_form('searchmedia', $form);
1907
}
1908
1909
/**
1910
 * Build a tree outline of available media namespaces
1911
 *
1912
 * @author Andreas Gohr <[email protected]>
1913
 *
1914
 * @param string $ns
1915
 */
1916
function media_nstree($ns){
1917
    global $conf;
1918
    global $lang;
1919
1920
    // currently selected namespace
1921
    $ns  = cleanID($ns);
1922
    if(empty($ns)){
1923
        global $ID;
1924
        $ns = (string)getNS($ID);
1925
    }
1926
1927
    $ns_dir  = utf8_encodeFN(str_replace(':','/',$ns));
1928
1929
    $data = array();
1930
    search($data,$conf['mediadir'],'search_index',array('ns' => $ns_dir, 'nofiles' => true));
1931
1932
    // wrap a list with the root level around the other namespaces
1933
    array_unshift($data, array('level' => 0, 'id' => '', 'open' =>'true',
1934
                               'label' => '['.$lang['mediaroot'].']'));
1935
1936
    // insert the current ns into the hierarchy if it isn't already part of it
1937
    $ns_parts = explode(':', $ns);
1938
    $tmp_ns = '';
1939
    $pos = 0;
1940
    foreach ($ns_parts as $level => $part) {
1941
        if ($tmp_ns) $tmp_ns .= ':'.$part;
1942
        else $tmp_ns = $part;
1943
1944
        // find the namespace parts or insert them
1945
        while ($data[$pos]['id'] != $tmp_ns) {
1946
            if ($pos >= count($data) || ($data[$pos]['level'] <= $level+1 && strnatcmp(utf8_encodeFN($data[$pos]['id']), utf8_encodeFN($tmp_ns)) > 0)) {
1947
                array_splice($data, $pos, 0, array(array('level' => $level+1, 'id' => $tmp_ns, 'open' => 'true')));
1948
                break;
1949
            }
1950
            ++$pos;
1951
        }
1952
    }
1953
1954
    echo html_buildlist($data,'idx','media_nstree_item','media_nstree_li');
1955
}
1956
1957
/**
1958
 * Userfunction for html_buildlist
1959
 *
1960
 * Prints a media namespace tree item
1961
 *
1962
 * @author Andreas Gohr <[email protected]>
1963
 *
1964
 * @param array $item
1965
 * @return string html
1966
 */
1967
function media_nstree_item($item){
1968
    global $INPUT;
1969
    $pos   = strrpos($item['id'], ':');
1970
    $label = substr($item['id'], $pos > 0 ? $pos + 1 : 0);
1971
    if(empty($item['label'])) $item['label'] = $label;
1972
1973
    $ret  = '';
1974
    if (!($INPUT->str('do') == 'media'))
1975
    $ret .= '<a href="'.DOKU_BASE.'lib/exe/mediamanager.php?ns='.idfilter($item['id']).'" class="idx_dir">';
1976
    else $ret .= '<a href="'.media_managerURL(array('ns' => idfilter($item['id'], false), 'tab_files' => 'files'))
1977
        .'" class="idx_dir">';
1978
    $ret .= $item['label'];
1979
    $ret .= '</a>';
1980
    return $ret;
1981
}
1982
1983
/**
1984
 * Userfunction for html_buildlist
1985
 *
1986
 * Prints a media namespace tree item opener
1987
 *
1988
 * @author Andreas Gohr <[email protected]>
1989
 *
1990
 * @param array $item
1991
 * @return string html
1992
 */
1993
function media_nstree_li($item){
1994
    $class='media level'.$item['level'];
1995
    if($item['open']){
1996
        $class .= ' open';
1997
        $img   = DOKU_BASE.'lib/images/minus.gif';
1998
        $alt   = '−';
1999
    }else{
2000
        $class .= ' closed';
2001
        $img   = DOKU_BASE.'lib/images/plus.gif';
2002
        $alt   = '+';
2003
    }
2004
    // TODO: only deliver an image if it actually has a subtree...
2005
    return '<li class="'.$class.'">'.
2006
        '<img src="'.$img.'" alt="'.$alt.'" />';
2007
}
2008
2009
/**
2010
 * Resizes the given image to the given size
2011
 *
2012
 * @author  Andreas Gohr <[email protected]>
2013
 *
2014
 * @param string $file filename, path to file
2015
 * @param string $ext  extension
2016
 * @param int    $w    desired width
2017
 * @param int    $h    desired height
2018
 * @return string path to resized or original size if failed
2019
 */
2020
function media_resize_image($file, $ext, $w, $h=0){
2021
    global $conf;
2022
2023
    $info = @getimagesize($file); //get original size
2024
    if($info == false) return $file; // that's no image - it's a spaceship!
2025
2026
    if(!$h) $h = round(($w * $info[1]) / $info[0]);
2027
    if(!$w) $w = round(($h * $info[0]) / $info[1]);
2028
2029
    // we wont scale up to infinity
2030
    if($w > 2000 || $h > 2000) return $file;
2031
2032
    // resize necessary? - (w,h) = native dimensions
2033
    if(($w == $info[0]) && ($h == $info[1])) return $file;
2034
2035
    //cache
2036
    $local = getCacheName($file,'.media.'.$w.'x'.$h.'.'.$ext);
2037
    $mtime = @filemtime($local); // 0 if not exists
2038
2039
    if($mtime > filemtime($file) ||
2040
        media_resize_imageIM($ext, $file, $info[0], $info[1], $local, $w, $h) ||
2041
        media_resize_imageGD($ext, $file, $info[0], $info[1], $local, $w, $h)
2042
    ) {
2043
        if(!empty($conf['fperm'])) @chmod($local, $conf['fperm']);
1 ignored issue
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
2044
        return $local;
2045
    }
2046
    //still here? resizing failed
2047
    return $file;
2048
}
2049
2050
/**
2051
 * Crops the given image to the wanted ratio, then calls media_resize_image to scale it
2052
 * to the wanted size
2053
 *
2054
 * Crops are centered horizontally but prefer the upper third of an vertical
2055
 * image because most pics are more interesting in that area (rule of thirds)
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_crop_image($file, $ext, $w, $h=0){
2066
    global $conf;
2067
2068
    if(!$h) $h = $w;
2069
    $info = @getimagesize($file); //get original size
2070
    if($info == false) return $file; // that's no image - it's a spaceship!
2071
2072
    // calculate crop size
2073
    $fr = $info[0]/$info[1];
2074
    $tr = $w/$h;
2075
2076
    // check if the crop can be handled completely by resize,
2077
    // i.e. the specified width & height match the aspect ratio of the source image
2078
    if ($w == round($h*$fr)) {
2079
        return media_resize_image($file, $ext, $w);
2080
    }
2081
2082
    if($tr >= 1){
2083
        if($tr > $fr){
2084
            $cw = $info[0];
2085
            $ch = (int) ($info[0]/$tr);
2086
        }else{
2087
            $cw = (int) ($info[1]*$tr);
2088
            $ch = $info[1];
2089
        }
2090
    }else{
2091
        if($tr < $fr){
2092
            $cw = (int) ($info[1]*$tr);
2093
            $ch = $info[1];
2094
        }else{
2095
            $cw = $info[0];
2096
            $ch = (int) ($info[0]/$tr);
2097
        }
2098
    }
2099
    // calculate crop offset
2100
    $cx = (int) (($info[0]-$cw)/2);
2101
    $cy = (int) (($info[1]-$ch)/3);
2102
2103
    //cache
2104
    $local = getCacheName($file,'.media.'.$cw.'x'.$ch.'.crop.'.$ext);
2105
    $mtime = @filemtime($local); // 0 if not exists
2106
2107
    if( $mtime > @filemtime($file) ||
2108
            media_crop_imageIM($ext,$file,$info[0],$info[1],$local,$cw,$ch,$cx,$cy) ||
2109
            media_resize_imageGD($ext,$file,$cw,$ch,$local,$cw,$ch,$cx,$cy) ){
2110
        if(!empty($conf['fperm'])) @chmod($local, $conf['fperm']);
1 ignored issue
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
2111
        return media_resize_image($local,$ext, $w, $h);
2112
    }
2113
2114
    //still here? cropping failed
2115
    return media_resize_image($file,$ext, $w, $h);
2116
}
2117
2118
/**
2119
 * Calculate a token to be used to verify fetch requests for resized or
2120
 * cropped images have been internally generated - and prevent external
2121
 * DDOS attacks via fetch
2122
 *
2123
 * @author Christopher Smith <[email protected]>
2124
 *
2125
 * @param string  $id    id of the image
2126
 * @param int     $w     resize/crop width
2127
 * @param int     $h     resize/crop height
2128
 * @return string token or empty string if no token required
2129
 */
2130
function media_get_token($id,$w,$h){
2131
    // token is only required for modified images
2132
    if ($w || $h || media_isexternal($id)) {
2133
        $token = $id;
2134
        if ($w) $token .= '.'.$w;
2135
        if ($h) $token .= '.'.$h;
2136
2137
        return substr(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, 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...
2138
    }
2139
2140
    return '';
2141
}
2142
2143
/**
2144
 * Download a remote file and return local filename
2145
 *
2146
 * returns false if download fails. Uses cached file if available and
2147
 * wanted
2148
 *
2149
 * @author  Andreas Gohr <[email protected]>
2150
 * @author  Pavel Vitis <[email protected]>
2151
 *
2152
 * @param string $url
2153
 * @param string $ext   extension
2154
 * @param int    $cache cachetime in seconds
2155
 * @return false|string path to cached file
2156
 */
2157
function media_get_from_URL($url,$ext,$cache){
2158
    global $conf;
2159
2160
    // if no cache or fetchsize just redirect
2161
    if ($cache==0)           return false;
2162
    if (!$conf['fetchsize']) return false;
2163
2164
    $local = getCacheName(strtolower($url),".media.$ext");
2165
    $mtime = @filemtime($local); // 0 if not exists
2166
2167
    //decide if download needed:
2168
    if(($mtime == 0) || // cache does not exist
2169
        ($cache != -1 && $mtime < time() - $cache) // 'recache' and cache has expired
2170
    ) {
2171
        if(media_image_download($url, $local)) {
2172
            return $local;
2173
        } else {
2174
            return false;
2175
        }
2176
    }
2177
2178
    //if cache exists use it else
2179
    if($mtime) return $local;
2180
2181
    //else return false
2182
    return false;
2183
}
2184
2185
/**
2186
 * Download image files
2187
 *
2188
 * @author Andreas Gohr <[email protected]>
2189
 *
2190
 * @param string $url
2191
 * @param string $file path to file in which to put the downloaded content
2192
 * @return bool
2193
 */
2194
function media_image_download($url,$file){
2195
    global $conf;
2196
    $http = new DokuHTTPClient();
2197
    $http->keep_alive = false; // we do single ops here, no need for keep-alive
2198
2199
    $http->max_bodysize = $conf['fetchsize'];
2200
    $http->timeout = 25; //max. 25 sec
2201
    $http->header_regexp = '!\r\nContent-Type: image/(jpe?g|gif|png)!i';
2202
2203
    $data = $http->get($url);
2204
    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...
2205
2206
    $fileexists = file_exists($file);
2207
    $fp = @fopen($file,"w");
2208
    if(!$fp) return false;
2209
    fwrite($fp,$data);
2210
    fclose($fp);
2211
    if(!$fileexists and $conf['fperm']) chmod($file, $conf['fperm']);
2212
2213
    // check if it is really an image
2214
    $info = @getimagesize($file);
2215
    if(!$info){
2216
        @unlink($file);
1 ignored issue
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
2217
        return false;
2218
    }
2219
2220
    return true;
2221
}
2222
2223
/**
2224
 * resize images using external ImageMagick convert program
2225
 *
2226
 * @author Pavel Vitis <[email protected]>
2227
 * @author Andreas Gohr <[email protected]>
2228
 *
2229
 * @param string $ext     extension
2230
 * @param string $from    filename path to file
2231
 * @param int    $from_w  original width
2232
 * @param int    $from_h  original height
2233
 * @param string $to      path to resized file
2234
 * @param int    $to_w    desired width
2235
 * @param int    $to_h    desired height
2236
 * @return bool
2237
 */
2238
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...
2239
    global $conf;
2240
2241
    // check if convert is configured
2242
    if(!$conf['im_convert']) return false;
2243
2244
    // prepare command
2245
    $cmd  = $conf['im_convert'];
2246
    $cmd .= ' -resize '.$to_w.'x'.$to_h.'!';
2247
    if ($ext == 'jpg' || $ext == 'jpeg') {
2248
        $cmd .= ' -quality '.$conf['jpg_quality'];
2249
    }
2250
    $cmd .= " $from $to";
2251
2252
    @exec($cmd,$out,$retval);
1 ignored issue
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
2253
    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...
2254
    return false;
2255
}
2256
2257
/**
2258
 * crop images using external ImageMagick convert program
2259
 *
2260
 * @author Andreas Gohr <[email protected]>
2261
 *
2262
 * @param string $ext     extension
2263
 * @param string $from    filename path to file
2264
 * @param int    $from_w  original width
2265
 * @param int    $from_h  original height
2266
 * @param string $to      path to resized file
2267
 * @param int    $to_w    desired width
2268
 * @param int    $to_h    desired height
2269
 * @param int    $ofs_x   offset of crop centre
2270
 * @param int    $ofs_y   offset of crop centre
2271
 * @return bool
2272
 */
2273
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...
2274
    global $conf;
2275
2276
    // check if convert is configured
2277
    if(!$conf['im_convert']) return false;
2278
2279
    // prepare command
2280
    $cmd  = $conf['im_convert'];
2281
    $cmd .= ' -crop '.$to_w.'x'.$to_h.'+'.$ofs_x.'+'.$ofs_y;
2282
    if ($ext == 'jpg' || $ext == 'jpeg') {
2283
        $cmd .= ' -quality '.$conf['jpg_quality'];
2284
    }
2285
    $cmd .= " $from $to";
2286
2287
    @exec($cmd,$out,$retval);
1 ignored issue
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
2288
    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...
2289
    return false;
2290
}
2291
2292
/**
2293
 * resize or crop images using PHP's libGD support
2294
 *
2295
 * @author Andreas Gohr <[email protected]>
2296
 * @author Sebastian Wienecke <[email protected]>
2297
 *
2298
 * @param string $ext     extension
2299
 * @param string $from    filename path to file
2300
 * @param int    $from_w  original width
2301
 * @param int    $from_h  original height
2302
 * @param string $to      path to resized file
2303
 * @param int    $to_w    desired width
2304
 * @param int    $to_h    desired height
2305
 * @param int    $ofs_x   offset of crop centre
2306
 * @param int    $ofs_y   offset of crop centre
2307
 * @return bool
2308
 */
2309
function media_resize_imageGD($ext,$from,$from_w,$from_h,$to,$to_w,$to_h,$ofs_x=0,$ofs_y=0){
2310
    global $conf;
2311
2312
    if($conf['gdlib'] < 1) return false; //no GDlib available or wanted
2313
2314
    // check available memory
2315
    if(!is_mem_available(($from_w * $from_h * 4) + ($to_w * $to_h * 4))){
2316
        return false;
2317
    }
2318
2319
    // create an image of the given filetype
2320
    $image = false;
2321
    if ($ext == 'jpg' || $ext == 'jpeg'){
2322
        if(!function_exists("imagecreatefromjpeg")) return false;
2323
        $image = @imagecreatefromjpeg($from);
2324
    }elseif($ext == 'png') {
2325
        if(!function_exists("imagecreatefrompng")) return false;
2326
        $image = @imagecreatefrompng($from);
2327
2328
    }elseif($ext == 'gif') {
2329
        if(!function_exists("imagecreatefromgif")) return false;
2330
        $image = @imagecreatefromgif($from);
2331
    }
2332
    if(!$image) return false;
2333
2334
    $newimg = false;
2335
    if(($conf['gdlib']>1) && function_exists("imagecreatetruecolor") && $ext != 'gif'){
2336
        $newimg = @imagecreatetruecolor ($to_w, $to_h);
2337
    }
2338
    if(!$newimg) $newimg = @imagecreate($to_w, $to_h);
2339
    if(!$newimg){
2340
        imagedestroy($image);
2341
        return false;
2342
    }
2343
2344
    //keep png alpha channel if possible
2345
    if($ext == 'png' && $conf['gdlib']>1 && function_exists('imagesavealpha')){
2346
        imagealphablending($newimg, false);
2347
        imagesavealpha($newimg,true);
2348
    }
2349
2350
    //keep gif transparent color if possible
2351
    if($ext == 'gif' && function_exists('imagefill') && function_exists('imagecolorallocate')) {
2352
        if(function_exists('imagecolorsforindex') && function_exists('imagecolortransparent')) {
2353
            $transcolorindex = @imagecolortransparent($image);
2354
            if($transcolorindex >= 0 ) { //transparent color exists
2355
                $transcolor = @imagecolorsforindex($image, $transcolorindex);
2356
                $transcolorindex = @imagecolorallocate($newimg, $transcolor['red'], $transcolor['green'], $transcolor['blue']);
2357
                @imagefill($newimg, 0, 0, $transcolorindex);
1 ignored issue
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
2358
                @imagecolortransparent($newimg, $transcolorindex);
1 ignored issue
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
2359
            }else{ //filling with white
2360
                $whitecolorindex = @imagecolorallocate($newimg, 255, 255, 255);
2361
                @imagefill($newimg, 0, 0, $whitecolorindex);
1 ignored issue
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
2362
            }
2363
        }else{ //filling with white
2364
            $whitecolorindex = @imagecolorallocate($newimg, 255, 255, 255);
2365
            @imagefill($newimg, 0, 0, $whitecolorindex);
1 ignored issue
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
2366
        }
2367
    }
2368
2369
    //try resampling first
2370
    if(function_exists("imagecopyresampled")){
2371
        if(!@imagecopyresampled($newimg, $image, 0, 0, $ofs_x, $ofs_y, $to_w, $to_h, $from_w, $from_h)) {
2372
            imagecopyresized($newimg, $image, 0, 0, $ofs_x, $ofs_y, $to_w, $to_h, $from_w, $from_h);
2373
        }
2374
    }else{
2375
        imagecopyresized($newimg, $image, 0, 0, $ofs_x, $ofs_y, $to_w, $to_h, $from_w, $from_h);
2376
    }
2377
2378
    $okay = false;
2379
    if ($ext == 'jpg' || $ext == 'jpeg'){
2380
        if(!function_exists('imagejpeg')){
2381
            $okay = false;
2382
        }else{
2383
            $okay = imagejpeg($newimg, $to, $conf['jpg_quality']);
2384
        }
2385
    }elseif($ext == 'png') {
2386
        if(!function_exists('imagepng')){
2387
            $okay = false;
2388
        }else{
2389
            $okay =  imagepng($newimg, $to);
2390
        }
2391
    }elseif($ext == 'gif') {
2392
        if(!function_exists('imagegif')){
2393
            $okay = false;
2394
        }else{
2395
            $okay = imagegif($newimg, $to);
2396
        }
2397
    }
2398
2399
    // destroy GD image ressources
2400
    if($image) imagedestroy($image);
2401
    if($newimg) imagedestroy($newimg);
2402
2403
    return $okay;
2404
}
2405
2406
/**
2407
 * Return other media files with the same base name
2408
 * but different extensions.
2409
 *
2410
 * @param string   $src     - ID of media file
2411
 * @param string[] $exts    - alternative extensions to find other files for
2412
 * @return array            - array(mime type => file ID)
2413
 *
2414
 * @author Anika Henke <[email protected]>
2415
 */
2416
function media_alternativefiles($src, $exts){
2417
2418
    $files = array();
2419
    list($srcExt, /* $srcMime */) = mimetype($src);
2420
    $filebase = substr($src, 0, -1 * (strlen($srcExt)+1));
2421
2422
    foreach($exts as $ext) {
2423
        $fileid = $filebase.'.'.$ext;
2424
        $file = mediaFN($fileid);
2425
        if(file_exists($file)) {
2426
            list(/* $fileExt */, $fileMime) = mimetype($file);
2427
            $files[$fileMime] = $fileid;
2428
        }
2429
    }
2430
    return $files;
2431
}
2432
2433
/**
2434
 * Check if video/audio is supported to be embedded.
2435
 *
2436
 * @param string $mime      - mimetype of media file
2437
 * @param string $type      - type of media files to check ('video', 'audio', or null for all)
2438
 * @return boolean
2439
 *
2440
 * @author Anika Henke <[email protected]>
2441
 */
2442
function media_supportedav($mime, $type=NULL){
2443
    $supportedAudio = array(
2444
        'ogg' => 'audio/ogg',
2445
        'mp3' => 'audio/mpeg',
2446
        'wav' => 'audio/wav',
2447
    );
2448
    $supportedVideo = array(
2449
        'webm' => 'video/webm',
2450
        'ogv' => 'video/ogg',
2451
        'mp4' => 'video/mp4',
2452
    );
2453
    if ($type == 'audio') {
2454
        $supportedAv = $supportedAudio;
2455
    } elseif ($type == 'video') {
2456
        $supportedAv = $supportedVideo;
2457
    } else {
2458
        $supportedAv = array_merge($supportedAudio, $supportedVideo);
2459
    }
2460
    return in_array($mime, $supportedAv);
2461
}
2462
2463
/**
2464
 * Return track media files with the same base name
2465
 * but extensions that indicate kind and lang.
2466
 * ie for foo.webm search foo.sub.lang.vtt, foo.cap.lang.vtt...
2467
 *
2468
 * @param string   $src     - ID of media file
2469
 * @return array            - array(mediaID => array( kind, srclang ))
2470
 *
2471
 * @author Schplurtz le Déboulonné <[email protected]>
2472
 */
2473
function media_trackfiles($src){
2474
    $kinds=array(
2475
        'sub' => 'subtitles',
2476
        'cap' => 'captions',
2477
        'des' => 'descriptions',
2478
        'cha' => 'chapters',
2479
        'met' => 'metadata'
2480
    );
2481
2482
    $files = array();
2483
    $re='/\\.(sub|cap|des|cha|met)\\.([^.]+)\\.vtt$/';
2484
    $baseid=pathinfo($src, PATHINFO_FILENAME);
2485
    $pattern=mediaFN($baseid).'.*.*.vtt';
2486
    $list=glob($pattern);
2487
    foreach($list as $track) {
2488
        if(preg_match($re, $track, $matches)){
2489
            $files[$baseid.'.'.$matches[1].'.'.$matches[2].'.vtt']=array(
2490
                $kinds[$matches[1]],
2491
                $matches[2],
2492
            );
2493
        }
2494
    }
2495
    return $files;
2496
}
2497
2498
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
2499