Completed
Pull Request — master (#3272)
by Andreas
03:04
created

pageutils.php ➔ media_exists()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
nc 3
nop 4
dl 0
loc 11
rs 9.9
c 0
b 0
f 0
1
<?php
2
/**
3
 * Utilities for handling pagenames
4
 *
5
 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
6
 * @author     Andreas Gohr <[email protected]>
7
 * @todo       Combine similar functions like {wiki,media,meta}FN()
8
 */
9
10
use dokuwiki\ChangeLog\MediaChangeLog;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, MediaChangeLog.

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

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

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

// Bar.php
namespace OtherDir;

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

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

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

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

// Bar.php
namespace OtherDir;

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

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

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

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

// Bar.php
namespace OtherDir;

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

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

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

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

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
12
use dokuwiki\Utils\MediaResolver;
13
use dokuwiki\Utils\PageResolver;
14
15
/**
16
 * Fetch the an ID from request
17
 *
18
 * Uses either standard $_REQUEST variable or extracts it from
19
 * the full request URI when userewrite is set to 2
20
 *
21
 * For $param='id' $conf['start'] is returned if no id was found.
22
 * If the second parameter is true (default) the ID is cleaned.
23
 *
24
 * @author Andreas Gohr <[email protected]>
25
 *
26
 * @param string $param  the $_REQUEST variable name, default 'id'
27
 * @param bool   $clean  if true, ID is cleaned
28
 * @return string
29
 */
30
function getID($param='id',$clean=true){
31
    /** @var Input $INPUT */
32
    global $INPUT;
33
    global $conf;
34
    global $ACT;
35
36
    $id = $INPUT->str($param);
37
38
    //construct page id from request URI
39
    if(empty($id) && $conf['userewrite'] == 2){
40
        $request = $INPUT->server->str('REQUEST_URI');
41
        $script = '';
42
43
        //get the script URL
44
        if($conf['basedir']){
45
            $relpath = '';
46
            if($param != 'id') {
47
                $relpath = 'lib/exe/';
48
            }
49
            $script = $conf['basedir'] . $relpath .
50
                \dokuwiki\Utf8\PhpString::basename($INPUT->server->str('SCRIPT_FILENAME'));
51
52
        }elseif($INPUT->server->str('PATH_INFO')){
53
            $request = $INPUT->server->str('PATH_INFO');
54
        }elseif($INPUT->server->str('SCRIPT_NAME')){
55
            $script = $INPUT->server->str('SCRIPT_NAME');
56
        }elseif($INPUT->server->str('DOCUMENT_ROOT') && $INPUT->server->str('SCRIPT_FILENAME')){
57
            $script = preg_replace ('/^'.preg_quote($INPUT->server->str('DOCUMENT_ROOT'),'/').'/','',
58
                    $INPUT->server->str('SCRIPT_FILENAME'));
59
            $script = '/'.$script;
60
        }
61
62
        //clean script and request (fixes a windows problem)
63
        $script  = preg_replace('/\/\/+/','/',$script);
64
        $request = preg_replace('/\/\/+/','/',$request);
65
66
        //remove script URL and Querystring to gain the id
67
        if(preg_match('/^'.preg_quote($script,'/').'(.*)/',$request, $match)){
68
            $id = preg_replace ('/\?.*/','',$match[1]);
69
        }
70
        $id = urldecode($id);
71
        //strip leading slashes
72
        $id = preg_replace('!^/+!','',$id);
73
    }
74
75
    // Namespace autolinking from URL
76
    if(substr($id,-1) == ':' || ($conf['useslash'] && substr($id,-1) == '/')){
77
        if(page_exists($id.$conf['start'])){
78
            // start page inside namespace
79
            $id = $id.$conf['start'];
80
        }elseif(page_exists($id.noNS(cleanID($id)))){
81
            // page named like the NS inside the NS
82
            $id = $id.noNS(cleanID($id));
83
        }elseif(page_exists($id)){
84
            // page like namespace exists
85
            $id = substr($id,0,-1);
86
        }else{
87
            // fall back to default
88
            $id = $id.$conf['start'];
89
        }
90
        if (isset($ACT) && $ACT === 'show') {
91
            $urlParameters = $_GET;
92
            if (isset($urlParameters['id'])) {
93
                unset($urlParameters['id']);
94
            }
95
            send_redirect(wl($id, $urlParameters, true, '&'));
96
        }
97
    }
98
    if($clean) $id = cleanID($id);
99
    if($id === '' && $param=='id') $id = $conf['start'];
100
101
    return $id;
102
}
103
104
/**
105
 * Remove unwanted chars from ID
106
 *
107
 * Cleans a given ID to only use allowed characters. Accented characters are
108
 * converted to unaccented ones
109
 *
110
 * @author Andreas Gohr <[email protected]>
111
 *
112
 * @param  string  $raw_id    The pageid to clean
113
 * @param  boolean $ascii     Force ASCII
114
 * @return string cleaned id
115
 */
116
function cleanID($raw_id,$ascii=false){
117
    global $conf;
118
    static $sepcharpat = null;
119
120
    global $cache_cleanid;
121
    $cache = & $cache_cleanid;
122
123
    // check if it's already in the memory cache
124
    if (!$ascii && isset($cache[(string)$raw_id])) {
125
        return $cache[(string)$raw_id];
126
    }
127
128
    $sepchar = $conf['sepchar'];
129
    if($sepcharpat == null) // build string only once to save clock cycles
130
        $sepcharpat = '#\\'.$sepchar.'+#';
131
132
    $id = trim((string)$raw_id);
133
    $id = \dokuwiki\Utf8\PhpString::strtolower($id);
134
135
    //alternative namespace seperator
136
    if($conf['useslash']){
137
        $id = strtr($id,';/','::');
138
    }else{
139
        $id = strtr($id,';/',':'.$sepchar);
140
    }
141
142
    if($conf['deaccent'] == 2 || $ascii) $id = \dokuwiki\Utf8\Clean::romanize($id);
143
    if($conf['deaccent'] || $ascii) $id = \dokuwiki\Utf8\Clean::deaccent($id,-1);
144
145
    //remove specials
146
    $id = \dokuwiki\Utf8\Clean::stripspecials($id,$sepchar,'\*');
147
148
    if($ascii) $id = \dokuwiki\Utf8\Clean::strip($id);
149
150
    //clean up
151
    $id = preg_replace($sepcharpat,$sepchar,$id);
152
    $id = preg_replace('#:+#',':',$id);
153
    $id = trim($id,':._-');
154
    $id = preg_replace('#:[:\._\-]+#',':',$id);
155
    $id = preg_replace('#[:\._\-]+:#',':',$id);
156
157
    if (!$ascii) $cache[(string)$raw_id] = $id;
158
    return($id);
159
}
160
161
/**
162
 * Return namespacepart of a wiki ID
163
 *
164
 * @author Andreas Gohr <[email protected]>
165
 *
166
 * @param string $id
167
 * @return string|false the namespace part or false if the given ID has no namespace (root)
168
 */
169
function getNS($id){
170
    $pos = strrpos((string)$id,':');
171
    if($pos!==false){
172
        return substr((string)$id,0,$pos);
173
    }
174
    return false;
175
}
176
177
/**
178
 * Returns the ID without the namespace
179
 *
180
 * @author Andreas Gohr <[email protected]>
181
 *
182
 * @param string $id
183
 * @return string
184
 */
185
function noNS($id) {
186
    $pos = strrpos($id, ':');
187
    if ($pos!==false) {
188
        return substr($id, $pos+1);
189
    } else {
190
        return $id;
191
    }
192
}
193
194
/**
195
 * Returns the current namespace
196
 *
197
 * @author Nathan Fritz <[email protected]>
198
 *
199
 * @param string $id
200
 * @return string
201
 */
202
function curNS($id) {
203
    return noNS(getNS($id));
0 ignored issues
show
Security Bug introduced by
It seems like getNS($id) targeting getNS() can also be of type false; however, noNS() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
204
}
205
206
/**
207
 * Returns the ID without the namespace or current namespace for 'start' pages
208
 *
209
 * @author Nathan Fritz <[email protected]>
210
 *
211
 * @param string $id
212
 * @return string
213
 */
214
function noNSorNS($id) {
215
    global $conf;
216
217
    $p = noNS($id);
218
    if ($p === $conf['start'] || $p === false || $p === '') {
219
        $p = curNS($id);
220
        if ($p === false || $p === '') {
221
            return $conf['start'];
222
        }
223
    }
224
    return $p;
225
}
226
227
/**
228
 * Creates a XHTML valid linkid from a given headline title
229
 *
230
 * @param string  $title   The headline title
231
 * @param array|bool   $check   Existing IDs
232
 * @return string the title
233
 *
234
 * @author Andreas Gohr <[email protected]>
235
 */
236
function sectionID($title,&$check) {
237
    $title = str_replace(array(':','.'),'',cleanID($title));
238
    $new = ltrim($title,'0123456789_-');
239
    if(empty($new)){
240
        $title = 'section'.preg_replace('/[^0-9]+/','',$title); //keep numbers from headline
241
    }else{
242
        $title = $new;
243
    }
244
245
    if(is_array($check)){
246
        $suffix=0;
247
        $candidateTitle = $title;
248
        while(in_array($candidateTitle, $check)){
249
          $candidateTitle = $title . ++$suffix;
250
        }
251
        $check []= $candidateTitle;
252
        return $candidateTitle;
253
    } else {
254
      return $title;
255
    }
256
}
257
258
/**
259
 * Wiki page existence check
260
 *
261
 * parameters as for wikiFN
262
 *
263
 * @author Chris Smith <[email protected]>
264
 *
265
 * @param string $id page id
266
 * @param string|int $rev empty or revision timestamp
267
 * @param bool $clean flag indicating that $id should be cleaned (see wikiFN as well)
268
 * @param bool $date_at
269
 * @return bool exists?
270
 */
271
function page_exists($id,$rev='',$clean=true, $date_at=false) {
272
    if($rev !== '' && $date_at) {
273
        $pagelog = new PageChangeLog($id);
274
        $pagelog_rev = $pagelog->getLastRevisionAt($rev);
275
        if($pagelog_rev !== false)
276
            $rev = $pagelog_rev;
277
    }
278
    return file_exists(wikiFN($id,$rev,$clean));
279
}
280
281
/**
282
 * Media existence check
283
 *
284
 * @param string $id page id
285
 * @param string|int $rev empty or revision timestamp
286
 * @param bool $clean flag indicating that $id should be cleaned (see mediaFN as well)
287
 * @param bool $date_at
288
 * @return bool exists?
289
 */
290
function media_exists($id, $rev = '', $clean = true, $date_at = false)
291
{
292
    if ($rev !== '' && $date_at) {
293
        $changeLog = new MediaChangeLog($id);
294
        $changelog_rev = $changeLog->getLastRevisionAt($rev);
295
        if ($changelog_rev !== false) {
296
            $rev = $changelog_rev;
297
        }
298
    }
299
    return file_exists(mediaFN($id, $rev, $clean));
300
}
301
302
/**
303
 * returns the full path to the datafile specified by ID and optional revision
304
 *
305
 * The filename is URL encoded to protect Unicode chars
306
 *
307
 * @param  $raw_id  string   id of wikipage
308
 * @param  $rev     int|string   page revision, empty string for current
309
 * @param  $clean   bool     flag indicating that $raw_id should be cleaned.  Only set to false
310
 *                           when $id is guaranteed to have been cleaned already.
311
 * @return string full path
312
 *
313
 * @author Andreas Gohr <[email protected]>
314
 */
315
function wikiFN($raw_id,$rev='',$clean=true){
316
    global $conf;
317
318
    global $cache_wikifn;
319
    $cache = & $cache_wikifn;
320
321
    $id = $raw_id;
322
323
    if ($clean) $id = cleanID($id);
324
    $id = str_replace(':','/',$id);
325
326
    if (isset($cache[$id]) && isset($cache[$id][$rev])) {
327
        return $cache[$id][$rev];
328
    }
329
330
    if(empty($rev)){
331
        $fn = $conf['datadir'].'/'.utf8_encodeFN($id).'.txt';
332
    }else{
333
        $fn = $conf['olddir'].'/'.utf8_encodeFN($id).'.'.$rev.'.txt';
334
        if($conf['compression']){
335
            //test for extensions here, we want to read both compressions
336
            if (file_exists($fn . '.gz')){
337
                $fn .= '.gz';
338
            }else if(file_exists($fn . '.bz2')){
339
                $fn .= '.bz2';
340
            }else{
341
                //file doesnt exist yet, so we take the configured extension
342
                $fn .= '.' . $conf['compression'];
343
            }
344
        }
345
    }
346
347
    if (!isset($cache[$id])) { $cache[$id] = array(); }
348
    $cache[$id][$rev] = $fn;
349
    return $fn;
350
}
351
352
/**
353
 * Returns the full path to the file for locking the page while editing.
354
 *
355
 * @author Ben Coburn <[email protected]>
356
 *
357
 * @param string $id page id
358
 * @return string full path
359
 */
360
function wikiLockFN($id) {
361
    global $conf;
362
    return $conf['lockdir'].'/'.md5(cleanID($id)).'.lock';
363
}
364
365
366
/**
367
 * returns the full path to the meta file specified by ID and extension
368
 *
369
 * @author Steven Danz <[email protected]>
370
 *
371
 * @param string $id   page id
372
 * @param string $ext  file extension
373
 * @return string full path
374
 */
375
function metaFN($id,$ext){
376
    global $conf;
377
    $id = cleanID($id);
378
    $id = str_replace(':','/',$id);
379
    $fn = $conf['metadir'].'/'.utf8_encodeFN($id).$ext;
380
    return $fn;
381
}
382
383
/**
384
 * returns the full path to the media's meta file specified by ID and extension
385
 *
386
 * @author Kate Arzamastseva <[email protected]>
387
 *
388
 * @param string $id   media id
389
 * @param string $ext  extension of media
390
 * @return string
391
 */
392
function mediaMetaFN($id,$ext){
393
    global $conf;
394
    $id = cleanID($id);
395
    $id = str_replace(':','/',$id);
396
    $fn = $conf['mediametadir'].'/'.utf8_encodeFN($id).$ext;
397
    return $fn;
398
}
399
400
/**
401
 * returns an array of full paths to all metafiles of a given ID
402
 *
403
 * @author Esther Brunner <[email protected]>
404
 * @author Michael Hamann <[email protected]>
405
 *
406
 * @param string $id page id
407
 * @return array
408
 */
409
function metaFiles($id){
410
    $basename = metaFN($id, '');
411
    $files    = glob($basename.'.*', GLOB_MARK);
412
    // filter files like foo.bar.meta when $id == 'foo'
413
    return    $files ? preg_grep('/^'.preg_quote($basename, '/').'\.[^.\/]*$/u', $files) : array();
414
}
415
416
/**
417
 * returns the full path to the mediafile specified by ID
418
 *
419
 * The filename is URL encoded to protect Unicode chars
420
 *
421
 * @author Andreas Gohr <[email protected]>
422
 * @author Kate Arzamastseva <[email protected]>
423
 *
424
 * @param string     $id  media id
425
 * @param string|int $rev empty string or revision timestamp
426
 * @param bool $clean
427
 *
428
 * @return string full path
429
 */
430
function mediaFN($id, $rev='', $clean=true){
431
    global $conf;
432
    if ($clean) $id = cleanID($id);
433
    $id = str_replace(':','/',$id);
434
    if(empty($rev)){
435
        $fn = $conf['mediadir'].'/'.utf8_encodeFN($id);
436
    }else{
437
        $ext = mimetype($id);
438
        $name = substr($id,0, -1*strlen($ext[0])-1);
439
        $fn = $conf['mediaolddir'].'/'.utf8_encodeFN($name .'.'.( (int) $rev ).'.'.$ext[0]);
440
    }
441
    return $fn;
442
}
443
444
/**
445
 * Returns the full filepath to a localized file if local
446
 * version isn't found the english one is returned
447
 *
448
 * @param  string $id  The id of the local file
449
 * @param  string $ext The file extension (usually txt)
450
 * @return string full filepath to localized file
451
 *
452
 * @author Andreas Gohr <[email protected]>
453
 */
454
function localeFN($id,$ext='txt'){
455
    global $conf;
456
    $file = DOKU_CONF.'lang/'.$conf['lang'].'/'.$id.'.'.$ext;
457
    if(!file_exists($file)){
458
        $file = DOKU_INC.'inc/lang/'.$conf['lang'].'/'.$id.'.'.$ext;
459
        if(!file_exists($file)){
460
            //fall back to english
461
            $file = DOKU_INC.'inc/lang/en/'.$id.'.'.$ext;
462
        }
463
    }
464
    return $file;
465
}
466
467
/**
468
 * Resolve relative paths in IDs
469
 *
470
 * Do not call directly use resolve_mediaid or resolve_pageid
471
 * instead
472
 *
473
 * Partyly based on a cleanPath function found at
474
 * http://php.net/manual/en/function.realpath.php#57016
475
 *
476
 * @deprecated 2020-09-30
477
 * @param string $ns     namespace which is context of id
478
 * @param string $id     relative id
479
 * @param bool   $clean  flag indicating that id should be cleaned
480
 * @return string
481
 */
482
function resolve_id($ns,$id,$clean=true){
483
    global $conf;
484
    dbg_deprecated(\dokuwiki\Utils\Resolver::class.' and its children');
485
486
    // some pre cleaning for useslash:
487
    if($conf['useslash']) $id = str_replace('/',':',$id);
488
489
    // if the id starts with a dot we need to handle the
490
    // relative stuff
491
    if($id && $id[0] == '.'){
492
        // normalize initial dots without a colon
493
        $id = preg_replace('/^((\.+:)*)(\.+)(?=[^:\.])/','\1\3:',$id);
494
        // prepend the current namespace
495
        $id = $ns.':'.$id;
496
497
        // cleanup relatives
498
        $result = array();
499
        $pathA  = explode(':', $id);
500
        if (!$pathA[0]) $result[] = '';
501
        foreach ($pathA AS $key => $dir) {
502
            if ($dir == '..') {
503
                if (end($result) == '..') {
504
                    $result[] = '..';
505
                } elseif (!array_pop($result)) {
506
                    $result[] = '..';
507
                }
508
            } elseif ($dir && $dir != '.') {
509
                $result[] = $dir;
510
            }
511
        }
512
        if (!end($pathA)) $result[] = '';
513
        $id = implode(':', $result);
514
    }elseif($ns !== false && strpos($id,':') === false){
515
        //if link contains no namespace. add current namespace (if any)
516
        $id = $ns.':'.$id;
517
    }
518
519
    if($clean) $id = cleanID($id);
520
    return $id;
521
}
522
523
/**
524
 * Returns a full media id
525
 *
526
 * @param string $ns namespace which is context of id
527
 * @param string &$media (reference) relative media id, updated to resolved id
528
 * @param bool &$exists (reference) updated with existance of media
529
 * @param int|string $rev
530
 * @param bool $date_at
531
 * @deprecated 2020-09-30
532
 */
533
function resolve_mediaid($ns,&$media,&$exists,$rev='',$date_at=false){
534
    dbg_deprecated(MediaResolver::class);
535
    $resolver = new MediaResolver("$ns:deprecated");
536
    $media = $resolver->resolveId($media, $rev, $date_at);
537
    $exists = media_exists($media, $rev, false, $date_at);
538
}
539
540
/**
541
 * Returns a full page id
542
 *
543
 * @deprecated 2020-09-30
544
 * @param string $ns namespace which is context of id
545
 * @param string &$page (reference) relative page id, updated to resolved id
546
 * @param bool &$exists (reference) updated with existance of media
547
 * @param string $rev
548
 * @param bool $date_at
549
 */
550
function resolve_pageid($ns,&$page,&$exists,$rev='',$date_at=false )
551
{
552
    dbg_deprecated(PageResolver::class);
553
    $resolver = new PageResolver("$ns:deprecated");
554
    $page = $resolver->resolveId($page, $rev, $date_at);
555
    $exists = page_exists($page, $rev, false, $date_at);
556
}
557
558
/**
559
 * Returns the name of a cachefile from given data
560
 *
561
 * The needed directory is created by this function!
562
 *
563
 * @author Andreas Gohr <[email protected]>
564
 *
565
 * @param string $data  This data is used to create a unique md5 name
566
 * @param string $ext   This is appended to the filename if given
567
 * @return string       The filename of the cachefile
568
 */
569
function getCacheName($data,$ext=''){
570
    global $conf;
571
    $md5  = md5($data);
572
    $file = $conf['cachedir'].'/'.$md5[0].'/'.$md5.$ext;
573
    io_makeFileDir($file);
574
    return $file;
575
}
576
577
/**
578
 * Checks a pageid against $conf['hidepages']
579
 *
580
 * @author Andreas Gohr <[email protected]>
581
 *
582
 * @param string $id page id
583
 * @return bool
584
 */
585
function isHiddenPage($id){
586
    $data = array(
587
        'id' => $id,
588
        'hidden' => false
589
    );
590
    \dokuwiki\Extension\Event::createAndTrigger('PAGEUTILS_ID_HIDEPAGE', $data, '_isHiddenPage');
591
    return $data['hidden'];
592
}
593
594
/**
595
 * callback checks if page is hidden
596
 *
597
 * @param array $data event data    - see isHiddenPage()
598
 */
599
function _isHiddenPage(&$data) {
600
    global $conf;
601
    global $ACT;
602
603
    if ($data['hidden']) return;
604
    if(empty($conf['hidepages'])) return;
605
    if($ACT == 'admin') return;
606
607
    if(preg_match('/'.$conf['hidepages'].'/ui',':'.$data['id'])){
608
        $data['hidden'] = true;
609
    }
610
}
611
612
/**
613
 * Reverse of isHiddenPage
614
 *
615
 * @author Andreas Gohr <[email protected]>
616
 *
617
 * @param string $id page id
618
 * @return bool
619
 */
620
function isVisiblePage($id){
621
    return !isHiddenPage($id);
622
}
623
624
/**
625
 * Format an id for output to a user
626
 *
627
 * Namespaces are denoted by a trailing “:*”. The root namespace is
628
 * “*”. Output is escaped.
629
 *
630
 * @author Adrian Lang <[email protected]>
631
 *
632
 * @param string $id page id
633
 * @return string
634
 */
635
function prettyprint_id($id) {
636
    if (!$id || $id === ':') {
637
        return '*';
638
    }
639
    if ((substr($id, -1, 1) === ':')) {
640
        $id .= '*';
641
    }
642
    return hsc($id);
643
}
644
645
/**
646
 * Encode a UTF-8 filename to use on any filesystem
647
 *
648
 * Uses the 'fnencode' option to determine encoding
649
 *
650
 * When the second parameter is true the string will
651
 * be encoded only if non ASCII characters are detected -
652
 * This makes it safe to run it multiple times on the
653
 * same string (default is true)
654
 *
655
 * @author Andreas Gohr <[email protected]>
656
 * @see    urlencode
657
 *
658
 * @param string $file file name
659
 * @param bool   $safe if true, only encoded when non ASCII characters detected
660
 * @return string
661
 */
662
function utf8_encodeFN($file,$safe=true){
663
    global $conf;
664
    if($conf['fnencode'] == 'utf-8') return $file;
665
666
    if($safe && preg_match('#^[a-zA-Z0-9/_\-\.%]+$#',$file)){
667
        return $file;
668
    }
669
670
    if($conf['fnencode'] == 'safe'){
671
        return SafeFN::encode($file);
672
    }
673
674
    $file = urlencode($file);
675
    $file = str_replace('%2F','/',$file);
676
    return $file;
677
}
678
679
/**
680
 * Decode a filename back to UTF-8
681
 *
682
 * Uses the 'fnencode' option to determine encoding
683
 *
684
 * @author Andreas Gohr <[email protected]>
685
 * @see    urldecode
686
 *
687
 * @param string $file file name
688
 * @return string
689
 */
690
function utf8_decodeFN($file){
691
    global $conf;
692
    if($conf['fnencode'] == 'utf-8') return $file;
693
694
    if($conf['fnencode'] == 'safe'){
695
        return SafeFN::decode($file);
696
    }
697
698
    return urldecode($file);
699
}
700
701
/**
702
 * Find a page in the current namespace (determined from $ID) or any
703
 * higher namespace that can be accessed by the current user,
704
 * this condition can be overriden by an optional parameter.
705
 *
706
 * Used for sidebars, but can be used other stuff as well
707
 *
708
 * @todo   add event hook
709
 *
710
 * @param  string $page the pagename you're looking for
711
 * @param bool $useacl only return pages readable by the current user, false to ignore ACLs
712
 * @return false|string the full page id of the found page, false if any
713
 */
714
function page_findnearest($page, $useacl = true){
715
    if ((string) $page === '') return false;
716
    global $ID;
717
718
    $ns = $ID;
719
    do {
720
        $ns = getNS($ns);
721
        $pageid = cleanID("$ns:$page");
722
        if(page_exists($pageid) && (!$useacl || auth_quickaclcheck($pageid) >= AUTH_READ)){
723
            return $pageid;
724
        }
725
    } while($ns !== false);
726
727
    return false;
728
}
729