Completed
Pull Request — remoteapiGetversions (#1573)
by Gerrit
06:07 queued 01:35
created

media.php ➔ media_file_diff()   F

Complexity

Conditions 21
Paths 1596

Size

Total Lines 113
Code Lines 80

Duplication

Lines 0
Ratio 0 %
Metric Value
cc 21
eloc 80
nc 1596
nop 6
dl 0
loc 113
rs 2

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

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

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
2174
2175
    $http->max_bodysize = $conf['fetchsize'];
0 ignored issues
show
Bug introduced by
The property max_bodysize cannot be accessed from this context as it is declared private in class HTTPClient.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
2176
    $http->timeout = 25; //max. 25 sec
0 ignored issues
show
Bug introduced by
The property timeout cannot be accessed from this context as it is declared private in class HTTPClient.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
2177
    $http->header_regexp = '!\r\nContent-Type: image/(jpe?g|gif|png)!i';
0 ignored issues
show
Bug introduced by
The property header_regexp cannot be accessed from this context as it is declared private in class HTTPClient.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
2178
2179
    $data = $http->get($url);
2180
    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...
2181
2182
    $fileexists = file_exists($file);
2183
    $fp = @fopen($file,"w");
2184
    if(!$fp) return false;
2185
    fwrite($fp,$data);
2186
    fclose($fp);
2187
    if(!$fileexists and $conf['fperm']) chmod($file, $conf['fperm']);
2188
2189
    // check if it is really an image
2190
    $info = @getimagesize($file);
2191
    if(!$info){
2192
        @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...
2193
        return false;
2194
    }
2195
2196
    return true;
2197
}
2198
2199
/**
2200
 * resize images using external ImageMagick convert program
2201
 *
2202
 * @author Pavel Vitis <[email protected]>
2203
 * @author Andreas Gohr <[email protected]>
2204
 *
2205
 * @param string $ext     extension
2206
 * @param string $from    filename path to file
2207
 * @param int    $from_w  original width
2208
 * @param int    $from_h  original height
2209
 * @param string $to      path to resized file
2210
 * @param int    $to_w    desired width
2211
 * @param int    $to_h    desired height
2212
 * @return bool
2213
 */
2214
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...
2215
    global $conf;
2216
2217
    // check if convert is configured
2218
    if(!$conf['im_convert']) return false;
2219
2220
    // prepare command
2221
    $cmd  = $conf['im_convert'];
2222
    $cmd .= ' -resize '.$to_w.'x'.$to_h.'!';
2223
    if ($ext == 'jpg' || $ext == 'jpeg') {
2224
        $cmd .= ' -quality '.$conf['jpg_quality'];
2225
    }
2226
    $cmd .= " $from $to";
2227
2228
    @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...
2229
    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...
2230
    return false;
2231
}
2232
2233
/**
2234
 * crop images using external ImageMagick convert program
2235
 *
2236
 * @author Andreas Gohr <[email protected]>
2237
 *
2238
 * @param string $ext     extension
2239
 * @param string $from    filename path to file
2240
 * @param int    $from_w  original width
2241
 * @param int    $from_h  original height
2242
 * @param string $to      path to resized file
2243
 * @param int    $to_w    desired width
2244
 * @param int    $to_h    desired height
2245
 * @param int    $ofs_x   offset of crop centre
2246
 * @param int    $ofs_y   offset of crop centre
2247
 * @return bool
2248
 */
2249
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...
2250
    global $conf;
2251
2252
    // check if convert is configured
2253
    if(!$conf['im_convert']) return false;
2254
2255
    // prepare command
2256
    $cmd  = $conf['im_convert'];
2257
    $cmd .= ' -crop '.$to_w.'x'.$to_h.'+'.$ofs_x.'+'.$ofs_y;
2258
    if ($ext == 'jpg' || $ext == 'jpeg') {
2259
        $cmd .= ' -quality '.$conf['jpg_quality'];
2260
    }
2261
    $cmd .= " $from $to";
2262
2263
    @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...
2264
    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...
2265
    return false;
2266
}
2267
2268
/**
2269
 * resize or crop images using PHP's libGD support
2270
 *
2271
 * @author Andreas Gohr <[email protected]>
2272
 * @author Sebastian Wienecke <[email protected]>
2273
 *
2274
 * @param string $ext     extension
2275
 * @param string $from    filename path to file
2276
 * @param int    $from_w  original width
2277
 * @param int    $from_h  original height
2278
 * @param string $to      path to resized file
2279
 * @param int    $to_w    desired width
2280
 * @param int    $to_h    desired height
2281
 * @param int    $ofs_x   offset of crop centre
2282
 * @param int    $ofs_y   offset of crop centre
2283
 * @return bool
2284
 */
2285
function media_resize_imageGD($ext,$from,$from_w,$from_h,$to,$to_w,$to_h,$ofs_x=0,$ofs_y=0){
2286
    global $conf;
2287
2288
    if($conf['gdlib'] < 1) return false; //no GDlib available or wanted
2289
2290
    // check available memory
2291
    if(!is_mem_available(($from_w * $from_h * 4) + ($to_w * $to_h * 4))){
2292
        return false;
2293
    }
2294
2295
    // create an image of the given filetype
2296
    $image = false;
2297
    if ($ext == 'jpg' || $ext == 'jpeg'){
2298
        if(!function_exists("imagecreatefromjpeg")) return false;
2299
        $image = @imagecreatefromjpeg($from);
2300
    }elseif($ext == 'png') {
2301
        if(!function_exists("imagecreatefrompng")) return false;
2302
        $image = @imagecreatefrompng($from);
2303
2304
    }elseif($ext == 'gif') {
2305
        if(!function_exists("imagecreatefromgif")) return false;
2306
        $image = @imagecreatefromgif($from);
2307
    }
2308
    if(!$image) return false;
2309
2310
    $newimg = false;
2311
    if(($conf['gdlib']>1) && function_exists("imagecreatetruecolor") && $ext != 'gif'){
2312
        $newimg = @imagecreatetruecolor ($to_w, $to_h);
2313
    }
2314
    if(!$newimg) $newimg = @imagecreate($to_w, $to_h);
2315
    if(!$newimg){
2316
        imagedestroy($image);
2317
        return false;
2318
    }
2319
2320
    //keep png alpha channel if possible
2321
    if($ext == 'png' && $conf['gdlib']>1 && function_exists('imagesavealpha')){
2322
        imagealphablending($newimg, false);
2323
        imagesavealpha($newimg,true);
2324
    }
2325
2326
    //keep gif transparent color if possible
2327
    if($ext == 'gif' && function_exists('imagefill') && function_exists('imagecolorallocate')) {
2328
        if(function_exists('imagecolorsforindex') && function_exists('imagecolortransparent')) {
2329
            $transcolorindex = @imagecolortransparent($image);
2330
            if($transcolorindex >= 0 ) { //transparent color exists
2331
                $transcolor = @imagecolorsforindex($image, $transcolorindex);
2332
                $transcolorindex = @imagecolorallocate($newimg, $transcolor['red'], $transcolor['green'], $transcolor['blue']);
2333
                @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...
2334
                @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...
2335
            }else{ //filling with white
2336
                $whitecolorindex = @imagecolorallocate($newimg, 255, 255, 255);
2337
                @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...
2338
            }
2339
        }else{ //filling with white
2340
            $whitecolorindex = @imagecolorallocate($newimg, 255, 255, 255);
2341
            @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...
2342
        }
2343
    }
2344
2345
    //try resampling first
2346
    if(function_exists("imagecopyresampled")){
2347
        if(!@imagecopyresampled($newimg, $image, 0, 0, $ofs_x, $ofs_y, $to_w, $to_h, $from_w, $from_h)) {
2348
            imagecopyresized($newimg, $image, 0, 0, $ofs_x, $ofs_y, $to_w, $to_h, $from_w, $from_h);
2349
        }
2350
    }else{
2351
        imagecopyresized($newimg, $image, 0, 0, $ofs_x, $ofs_y, $to_w, $to_h, $from_w, $from_h);
2352
    }
2353
2354
    $okay = false;
2355
    if ($ext == 'jpg' || $ext == 'jpeg'){
2356
        if(!function_exists('imagejpeg')){
2357
            $okay = false;
2358
        }else{
2359
            $okay = imagejpeg($newimg, $to, $conf['jpg_quality']);
2360
        }
2361
    }elseif($ext == 'png') {
2362
        if(!function_exists('imagepng')){
2363
            $okay = false;
2364
        }else{
2365
            $okay =  imagepng($newimg, $to);
2366
        }
2367
    }elseif($ext == 'gif') {
2368
        if(!function_exists('imagegif')){
2369
            $okay = false;
2370
        }else{
2371
            $okay = imagegif($newimg, $to);
2372
        }
2373
    }
2374
2375
    // destroy GD image ressources
2376
    if($image) imagedestroy($image);
2377
    if($newimg) imagedestroy($newimg);
2378
2379
    return $okay;
2380
}
2381
2382
/**
2383
 * Return other media files with the same base name
2384
 * but different extensions.
2385
 *
2386
 * @param string   $src     - ID of media file
2387
 * @param string[] $exts    - alternative extensions to find other files for
2388
 * @return array            - array(mime type => file ID)
2389
 *
2390
 * @author Anika Henke <[email protected]>
2391
 */
2392
function media_alternativefiles($src, $exts){
2393
2394
    $files = array();
2395
    list($srcExt, /* $srcMime */) = mimetype($src);
2396
    $filebase = substr($src, 0, -1 * (strlen($srcExt)+1));
2397
2398
    foreach($exts as $ext) {
2399
        $fileid = $filebase.'.'.$ext;
2400
        $file = mediaFN($fileid);
2401
        if(file_exists($file)) {
2402
            list(/* $fileExt */, $fileMime) = mimetype($file);
2403
            $files[$fileMime] = $fileid;
2404
        }
2405
    }
2406
    return $files;
2407
}
2408
2409
/**
2410
 * Check if video/audio is supported to be embedded.
2411
 *
2412
 * @param string $mime      - mimetype of media file
2413
 * @param string $type      - type of media files to check ('video', 'audio', or null for all)
2414
 * @return boolean
2415
 *
2416
 * @author Anika Henke <[email protected]>
2417
 */
2418
function media_supportedav($mime, $type=NULL){
2419
    $supportedAudio = array(
2420
        'ogg' => 'audio/ogg',
2421
        'mp3' => 'audio/mpeg',
2422
        'wav' => 'audio/wav',
2423
    );
2424
    $supportedVideo = array(
2425
        'webm' => 'video/webm',
2426
        'ogv' => 'video/ogg',
2427
        'mp4' => 'video/mp4',
2428
    );
2429
    if ($type == 'audio') {
2430
        $supportedAv = $supportedAudio;
2431
    } elseif ($type == 'video') {
2432
        $supportedAv = $supportedVideo;
2433
    } else {
2434
        $supportedAv = array_merge($supportedAudio, $supportedVideo);
2435
    }
2436
    return in_array($mime, $supportedAv);
2437
}
2438
2439
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
2440