Failed Conditions
Push — psr2 ( 2b9c4a...b47790 )
by Andreas
08:24 queued 05:51
created

inc/media.php (8 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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

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

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

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

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

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

Loading history...
793
                                'action' => wl($ID)));
794
    $media_manager_params = media_managerURL(array(), '', false, true);
795
    foreach($media_manager_params as $pKey => $pVal){
796
        $form->addHidden($pKey, $pVal);
797
    }
798
    $form->addHidden('sectok', null);
799
    if ($INPUT->has('q')) {
800
        $form->addHidden('q', $INPUT->str('q'));
801
    }
802
    $form->addElement('<ul>'.NL);
803
    foreach(array('list' => array('listType', array('thumbs', 'rows')),
804
                  'sort' => array('sortBy', array('name', 'date')))
805
            as $group => $content) {
806
        $checked = "_media_get_${group}_type";
807
        $checked = $checked();
808
809
        $form->addElement('<li class="' . $content[0] . '">');
810
        foreach($content[1] as $option) {
811
            $attrs = array();
812
            if ($checked == $option) {
813
                $attrs['checked'] = 'checked';
814
            }
815
            $form->addElement(form_makeRadioField($group . '_dwmedia', $option,
816
                                       $lang['media_' . $group . '_' . $option],
817
                                                  $content[0] . '__' . $option,
818
                                                  $option, $attrs));
819
        }
820
        $form->addElement('</li>'.NL);
821
    }
822
    $form->addElement('<li>');
823
    $form->addElement(form_makeButton('submit', '', $lang['btn_apply']));
824
    $form->addElement('</li>'.NL);
825
    $form->addElement('</ul>'.NL);
826
    $form->printForm();
827
}
828
829
/**
830
 * Returns type of sorting for the list of files in media manager
831
 *
832
 * @author Kate Arzamastseva <[email protected]>
833
 *
834
 * @return string - sort type
835
 */
836
function _media_get_sort_type() {
837
    return _media_get_display_param('sort', array('default' => 'name', 'date'));
838
}
839
840
/**
841
 * Returns type of listing for the list of files in media manager
842
 *
843
 * @author Kate Arzamastseva <[email protected]>
844
 *
845
 * @return string - list type
846
 */
847
function _media_get_list_type() {
848
    return _media_get_display_param('list', array('default' => 'thumbs', 'rows'));
849
}
850
851
/**
852
 * Get display parameters
853
 *
854
 * @param string $param   name of parameter
855
 * @param array  $values  allowed values, where default value has index key 'default'
856
 * @return string the parameter value
857
 */
858
function _media_get_display_param($param, $values) {
859
    global $INPUT;
860
    if (in_array($INPUT->str($param), $values)) {
861
        // FIXME: Set cookie
862
        return $INPUT->str($param);
863
    } else {
864
        $val = get_doku_pref($param, $values['default']);
865
        if (!in_array($val, $values)) {
866
            $val = $values['default'];
867
        }
868
        return $val;
869
    }
870
}
871
872
/**
873
 * Prints tab that displays a list of all files
874
 *
875
 * @author Kate Arzamastseva <[email protected]>
876
 *
877
 * @param string    $ns
878
 * @param null|int  $auth permission level
879
 * @param string    $jump item id
880
 */
881
function media_tab_files($ns,$auth=null,$jump='') {
882
    global $lang;
883
    if(is_null($auth)) $auth = auth_quickaclcheck("$ns:*");
884
885
    if($auth < AUTH_READ){
886
        echo '<div class="nothing">'.$lang['media_perm_read'].'</div>'.NL;
887
    }else{
888
        media_filelist($ns,$auth,$jump,true,_media_get_sort_type());
889
    }
890
}
891
892
/**
893
 * Prints tab that displays uploading form
894
 *
895
 * @author Kate Arzamastseva <[email protected]>
896
 *
897
 * @param string   $ns
898
 * @param null|int $auth permission level
899
 * @param string   $jump item id
900
 */
901
function media_tab_upload($ns,$auth=null,$jump='') {
902
    global $lang;
903
    if(is_null($auth)) $auth = auth_quickaclcheck("$ns:*");
904
905
    echo '<div class="upload">'.NL;
906
    if ($auth >= AUTH_UPLOAD) {
907
        echo '<p>' . $lang['mediaupload'] . '</p>';
908
    }
909
    media_uploadform($ns, $auth, true);
910
    echo '</div>'.NL;
911
}
912
913
/**
914
 * Prints tab that displays search form
915
 *
916
 * @author Kate Arzamastseva <[email protected]>
917
 *
918
 * @param string $ns
919
 * @param null|int $auth permission level
920
 */
921
function media_tab_search($ns,$auth=null) {
922
    global $INPUT;
923
924
    $do = $INPUT->str('mediado');
925
    $query = $INPUT->str('q');
926
    echo '<div class="search">'.NL;
927
928
    media_searchform($ns, $query, true);
929
    if ($do == 'searchlist' || $query) {
930
        media_searchlist($query,$ns,$auth,true,_media_get_sort_type());
931
    }
932
    echo '</div>'.NL;
933
}
934
935
/**
936
 * Prints tab that displays mediafile details
937
 *
938
 * @author Kate Arzamastseva <[email protected]>
939
 *
940
 * @param string     $image media id
941
 * @param string     $ns
942
 * @param null|int   $auth  permission level
943
 * @param string|int $rev   revision timestamp or empty string
944
 */
945
function media_tab_view($image, $ns, $auth=null, $rev='') {
946
    global $lang;
947
    if(is_null($auth)) $auth = auth_quickaclcheck("$ns:*");
948
949
    if ($image && $auth >= AUTH_READ) {
950
        $meta = new JpegMeta(mediaFN($image, $rev));
951
        media_preview($image, $auth, $rev, $meta);
952
        media_preview_buttons($image, $auth, $rev);
953
        media_details($image, $auth, $rev, $meta);
954
955
    } else {
956
        echo '<div class="nothing">'.$lang['media_perm_read'].'</div>'.NL;
957
    }
958
}
959
960
/**
961
 * Prints tab that displays form for editing mediafile metadata
962
 *
963
 * @author Kate Arzamastseva <[email protected]>
964
 *
965
 * @param string     $image media id
966
 * @param string     $ns
967
 * @param null|int   $auth permission level
968
 */
969
function media_tab_edit($image, $ns, $auth=null) {
970
    if(is_null($auth)) $auth = auth_quickaclcheck("$ns:*");
971
972
    if ($image) {
973
        list(, $mime) = mimetype($image);
974
        if ($mime == 'image/jpeg') media_metaform($image,$auth);
975
    }
976
}
977
978
/**
979
 * Prints tab that displays mediafile revisions
980
 *
981
 * @author Kate Arzamastseva <[email protected]>
982
 *
983
 * @param string     $image media id
984
 * @param string     $ns
985
 * @param null|int   $auth permission level
986
 */
987
function media_tab_history($image, $ns, $auth=null) {
988
    global $lang;
989
    global $INPUT;
990
991
    if(is_null($auth)) $auth = auth_quickaclcheck("$ns:*");
992
    $do = $INPUT->str('mediado');
993
994
    if ($auth >= AUTH_READ && $image) {
995
        if ($do == 'diff'){
996
            media_diff($image, $ns, $auth);
997
        } else {
998
            $first = $INPUT->int('first');
999
            html_revisions($first, $image);
1000
        }
1001
    } else {
1002
        echo '<div class="nothing">'.$lang['media_perm_read'].'</div>'.NL;
1003
    }
1004
}
1005
1006
/**
1007
 * Prints mediafile details
1008
 *
1009
 * @param string         $image media id
1010
 * @param int            $auth permission level
1011
 * @param int|string     $rev revision timestamp or empty string
1012
 * @param JpegMeta|bool  $meta
1013
 *
1014
 * @author Kate Arzamastseva <[email protected]>
1015
 */
1016
function media_preview($image, $auth, $rev='', $meta=false) {
1017
1018
    $size = media_image_preview_size($image, $rev, $meta);
1019
1020
    if ($size) {
1021
        global $lang;
1022
        echo '<div class="image">';
1023
1024
        $more = array();
1025
        if ($rev) {
1026
            $more['rev'] = $rev;
1027
        } else {
1028
            $t = @filemtime(mediaFN($image));
1029
            $more['t'] = $t;
1030
        }
1031
1032
        $more['w'] = $size[0];
1033
        $more['h'] = $size[1];
1034
        $src = ml($image, $more);
1035
1036
        echo '<a href="'.$src.'" target="_blank" title="'.$lang['mediaview'].'">';
1037
        echo '<img src="'.$src.'" alt="" style="max-width: '.$size[0].'px;" />';
1038
        echo '</a>';
1039
1040
        echo '</div>'.NL;
1041
    }
1042
}
1043
1044
/**
1045
 * Prints mediafile action buttons
1046
 *
1047
 * @author Kate Arzamastseva <[email protected]>
1048
 *
1049
 * @param string     $image media id
1050
 * @param int        $auth  permission level
1051
 * @param string|int $rev   revision timestamp, or empty string
1052
 */
1053
function media_preview_buttons($image, $auth, $rev='') {
1054
    global $lang, $conf;
1055
1056
    echo '<ul class="actions">'.NL;
1057
1058
    if($auth >= AUTH_DELETE && !$rev && file_exists(mediaFN($image))){
1059
1060
        // delete button
1061
        $form = new Doku_Form(array('id' => 'mediamanager__btn_delete',
0 ignored issues
show
Deprecated Code introduced by
The class Doku_Form has been deprecated with message: 2019-07-14

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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