Failed Conditions
Push — reverse-fallback-ns-linking ( 4819a4...482747 )
by Henry
03:24
created

pageutils.php ➔ utf8_encodeFN()   A

Complexity

Conditions 5
Paths 4

Size

Total Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
nc 4
nop 2
dl 0
loc 16
rs 9.4222
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
13
/**
14
 * Fetch the an ID from request
15
 *
16
 * Uses either standard $_REQUEST variable or extracts it from
17
 * the full request URI when userewrite is set to 2
18
 *
19
 * For $param='id' $conf['start'] is returned if no id was found.
20
 * If the second parameter is true (default) the ID is cleaned.
21
 *
22
 * @author Andreas Gohr <[email protected]>
23
 *
24
 * @param string $param  the $_REQUEST variable name, default 'id'
25
 * @param bool   $clean  if true, ID is cleaned
26
 * @return string
27
 */
28
function getID($param='id',$clean=true){
29
    /** @var Input $INPUT */
30
    global $INPUT;
31
    global $conf;
32
    global $ACT;
33
34
    $id = $INPUT->str($param);
35
36
    //construct page id from request URI
37
    if(empty($id) && $conf['userewrite'] == 2){
38
        $request = $INPUT->server->str('REQUEST_URI');
39
        $script = '';
40
41
        //get the script URL
42
        if($conf['basedir']){
43
            $relpath = '';
44
            if($param != 'id') {
45
                $relpath = 'lib/exe/';
46
            }
47
            $script = $conf['basedir'] . $relpath .
48
                \dokuwiki\Utf8\PhpString::basename($INPUT->server->str('SCRIPT_FILENAME'));
49
50
        }elseif($INPUT->server->str('PATH_INFO')){
51
            $request = $INPUT->server->str('PATH_INFO');
52
        }elseif($INPUT->server->str('SCRIPT_NAME')){
53
            $script = $INPUT->server->str('SCRIPT_NAME');
54
        }elseif($INPUT->server->str('DOCUMENT_ROOT') && $INPUT->server->str('SCRIPT_FILENAME')){
55
            $script = preg_replace ('/^'.preg_quote($INPUT->server->str('DOCUMENT_ROOT'),'/').'/','',
56
                    $INPUT->server->str('SCRIPT_FILENAME'));
57
            $script = '/'.$script;
58
        }
59
60
        //clean script and request (fixes a windows problem)
61
        $script  = preg_replace('/\/\/+/','/',$script);
62
        $request = preg_replace('/\/\/+/','/',$request);
63
64
        //remove script URL and Querystring to gain the id
65
        if(preg_match('/^'.preg_quote($script,'/').'(.*)/',$request, $match)){
66
            $id = preg_replace ('/\?.*/','',$match[1]);
67
        }
68
        $id = urldecode($id);
69
        //strip leading slashes
70
        $id = preg_replace('!^/+!','',$id);
71
    }
72
73
    // Namespace autolinking from URL
74
    $rewrite_type = null;
75
    $rewrite = $id;
76
    if(substr($id,-1) == ':' || ($conf['useslash'] && substr($id,-1) == '/')){
77
        // default: keep defaultstart page inside namespace
78
        $rewrite_type = 'all';
79
        $rewrite = $id.$conf['start'];
80
        if(!page_exists($rewrite)){
81
            // try alternative
82
            if(page_exists($id.noNS(cleanID($id)))){
83
                // page named like the NS inside the NS
84
                $rewrite = $id.noNS(cleanID($id));
85
            }elseif(page_exists($id)){
86
                // page like namespace exists
87
                $rewrite = substr($id,0,-1);
88
            }
89
            // default
90
        }
91
    }else if(!page_exists($id) && $conf['nsfallback']){
92
        $sep = $conf['useslash']?'/':':';
93
        // requested page not found? try reverse fallback namespace linking
94
        if(page_exists($id.$sep.$conf['start'])){
95
            // start page inside namespace
96
            $rewrite_type = 'show';
97
            $rewrite = $id.$sep.$conf['start'];
98
        }elseif(page_exists($id.$sep.noNS(cleanID($id)))){
99
            // page named like the NS inside the NS
100
            $rewrite_type = 'show';
101
            $rewrite = $id.$sep.noNS(cleanID($id));
102
        }
103
        // still not found? Show error page
104
    }
105
106
    if($rewrite_type !== null){
107
        if($rewrite_type == 'all') $id = $rewrite;
108
        if(($rewrite_type == 'all' || $rewrite_type == 'show') && isset($ACT) && $ACT === 'show') {
109
            $urlParameters = $_GET;
110
            if (isset($urlParameters['id'])) {
111
                unset($urlParameters['id']);
112
            }
113
            send_redirect(wl($rewrite, $urlParameters, true, '&'));
114
            $id = $rewrite;// during DOKUWIKI_TEST it can still return expected id
115
        }
116
    }
117
118
    if($clean) $id = cleanID($id);
119
    if($id === '' && $param=='id') $id = $conf['start'];
120
121
    return $id;
122
}
123
124
/**
125
 * Remove unwanted chars from ID
126
 *
127
 * Cleans a given ID to only use allowed characters. Accented characters are
128
 * converted to unaccented ones
129
 *
130
 * @author Andreas Gohr <[email protected]>
131
 *
132
 * @param  string  $raw_id    The pageid to clean
133
 * @param  boolean $ascii     Force ASCII
134
 * @return string cleaned id
135
 */
136
function cleanID($raw_id,$ascii=false){
137
    global $conf;
138
    static $sepcharpat = null;
139
140
    global $cache_cleanid;
141
    $cache = & $cache_cleanid;
142
143
    // check if it's already in the memory cache
144
    if (!$ascii && isset($cache[(string)$raw_id])) {
145
        return $cache[(string)$raw_id];
146
    }
147
148
    $sepchar = $conf['sepchar'];
149
    if($sepcharpat == null) // build string only once to save clock cycles
150
        $sepcharpat = '#\\'.$sepchar.'+#';
151
152
    $id = trim((string)$raw_id);
153
    $id = \dokuwiki\Utf8\PhpString::strtolower($id);
154
155
    //alternative namespace seperator
156
    if($conf['useslash']){
157
        $id = strtr($id,';/','::');
158
    }else{
159
        $id = strtr($id,';/',':'.$sepchar);
160
    }
161
162
    if($conf['deaccent'] == 2 || $ascii) $id = \dokuwiki\Utf8\Clean::romanize($id);
163
    if($conf['deaccent'] || $ascii) $id = \dokuwiki\Utf8\Clean::deaccent($id,-1);
164
165
    //remove specials
166
    $id = \dokuwiki\Utf8\Clean::stripspecials($id,$sepchar,'\*');
167
168
    if($ascii) $id = \dokuwiki\Utf8\Clean::strip($id);
169
170
    //clean up
171
    $id = preg_replace($sepcharpat,$sepchar,$id);
172
    $id = preg_replace('#:+#',':',$id);
173
    $id = trim($id,':._-');
174
    $id = preg_replace('#:[:\._\-]+#',':',$id);
175
    $id = preg_replace('#[:\._\-]+:#',':',$id);
176
177
    if (!$ascii) $cache[(string)$raw_id] = $id;
178
    return($id);
179
}
180
181
/**
182
 * Return namespacepart of a wiki ID
183
 *
184
 * @author Andreas Gohr <[email protected]>
185
 *
186
 * @param string $id
187
 * @return string|false the namespace part or false if the given ID has no namespace (root)
188
 */
189
function getNS($id){
190
    $pos = strrpos((string)$id,':');
191
    if($pos!==false){
192
        return substr((string)$id,0,$pos);
193
    }
194
    return false;
195
}
196
197
/**
198
 * Returns the ID without the namespace
199
 *
200
 * @author Andreas Gohr <[email protected]>
201
 *
202
 * @param string $id
203
 * @return string
204
 */
205
function noNS($id) {
206
    $pos = strrpos($id, ':');
207
    if ($pos!==false) {
208
        return substr($id, $pos+1);
209
    } else {
210
        return $id;
211
    }
212
}
213
214
/**
215
 * Returns the current namespace
216
 *
217
 * @author Nathan Fritz <[email protected]>
218
 *
219
 * @param string $id
220
 * @return string
221
 */
222
function curNS($id) {
223
    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...
224
}
225
226
/**
227
 * Returns the ID without the namespace or current namespace for 'start' pages
228
 *
229
 * @author Nathan Fritz <[email protected]>
230
 *
231
 * @param string $id
232
 * @return string
233
 */
234
function noNSorNS($id) {
235
    global $conf;
236
237
    $p = noNS($id);
238
    if ($p == $conf['start'] || $p == false) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $p of type string to the boolean false. If you are specifically checking for an empty string, consider using the more explicit === '' instead.
Loading history...
239
        $p = curNS($id);
240
        if ($p == false) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $p of type string to the boolean false. If you are specifically checking for an empty string, consider using the more explicit === '' instead.
Loading history...
241
            return $conf['start'];
242
        }
243
    }
244
    return $p;
245
}
246
247
/**
248
 * Creates a XHTML valid linkid from a given headline title
249
 *
250
 * @param string  $title   The headline title
251
 * @param array|bool   $check   Existing IDs (title => number)
252
 * @return string the title
253
 *
254
 * @author Andreas Gohr <[email protected]>
255
 */
256
function sectionID($title,&$check) {
257
    $title = str_replace(array(':','.'),'',cleanID($title));
258
    $new = ltrim($title,'0123456789_-');
259
    if(empty($new)){
260
        $title = 'section'.preg_replace('/[^0-9]+/','',$title); //keep numbers from headline
261
    }else{
262
        $title = $new;
263
    }
264
265
    if(is_array($check)){
266
        // make sure tiles are unique
267
        if (!array_key_exists ($title,$check)) {
268
            $check[$title] = 0;
269
        } else {
270
            $title .= ++ $check[$title];
271
        }
272
    }
273
274
    return $title;
275
}
276
277
/**
278
 * Wiki page existence check
279
 *
280
 * parameters as for wikiFN
281
 *
282
 * @author Chris Smith <[email protected]>
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 wikiFN as well)
287
 * @param bool $date_at
288
 * @return bool exists?
289
 */
290
function page_exists($id,$rev='',$clean=true, $date_at=false) {
291
    if($rev !== '' && $date_at) {
292
        $pagelog = new PageChangeLog($id);
293
        $pagelog_rev = $pagelog->getLastRevisionAt($rev);
294
        if($pagelog_rev !== false)
295
            $rev = $pagelog_rev;
296
    }
297
    return file_exists(wikiFN($id,$rev,$clean));
298
}
299
300
/**
301
 * returns the full path to the datafile specified by ID and optional revision
302
 *
303
 * The filename is URL encoded to protect Unicode chars
304
 *
305
 * @param  $raw_id  string   id of wikipage
306
 * @param  $rev     int|string   page revision, empty string for current
307
 * @param  $clean   bool     flag indicating that $raw_id should be cleaned.  Only set to false
308
 *                           when $id is guaranteed to have been cleaned already.
309
 * @return string full path
310
 *
311
 * @author Andreas Gohr <[email protected]>
312
 */
313
function wikiFN($raw_id,$rev='',$clean=true){
314
    global $conf;
315
316
    global $cache_wikifn;
317
    $cache = & $cache_wikifn;
318
319
    $id = $raw_id;
320
321
    if ($clean) $id = cleanID($id);
322
    $id = str_replace(':','/',$id);
323
324
    if (isset($cache[$id]) && isset($cache[$id][$rev])) {
325
        return $cache[$id][$rev];
326
    }
327
328
    if(empty($rev)){
329
        $fn = $conf['datadir'].'/'.utf8_encodeFN($id).'.txt';
330
    }else{
331
        $fn = $conf['olddir'].'/'.utf8_encodeFN($id).'.'.$rev.'.txt';
332
        if($conf['compression']){
333
            //test for extensions here, we want to read both compressions
334
            if (file_exists($fn . '.gz')){
335
                $fn .= '.gz';
336
            }else if(file_exists($fn . '.bz2')){
337
                $fn .= '.bz2';
338
            }else{
339
                //file doesnt exist yet, so we take the configured extension
340
                $fn .= '.' . $conf['compression'];
341
            }
342
        }
343
    }
344
345
    if (!isset($cache[$id])) { $cache[$id] = array(); }
346
    $cache[$id][$rev] = $fn;
347
    return $fn;
348
}
349
350
/**
351
 * Returns the full path to the file for locking the page while editing.
352
 *
353
 * @author Ben Coburn <[email protected]>
354
 *
355
 * @param string $id page id
356
 * @return string full path
357
 */
358
function wikiLockFN($id) {
359
    global $conf;
360
    return $conf['lockdir'].'/'.md5(cleanID($id)).'.lock';
361
}
362
363
364
/**
365
 * returns the full path to the meta file specified by ID and extension
366
 *
367
 * @author Steven Danz <[email protected]>
368
 *
369
 * @param string $id   page id
370
 * @param string $ext  file extension
371
 * @return string full path
372
 */
373
function metaFN($id,$ext){
374
    global $conf;
375
    $id = cleanID($id);
376
    $id = str_replace(':','/',$id);
377
    $fn = $conf['metadir'].'/'.utf8_encodeFN($id).$ext;
378
    return $fn;
379
}
380
381
/**
382
 * returns the full path to the media's meta file specified by ID and extension
383
 *
384
 * @author Kate Arzamastseva <[email protected]>
385
 *
386
 * @param string $id   media id
387
 * @param string $ext  extension of media
388
 * @return string
389
 */
390
function mediaMetaFN($id,$ext){
391
    global $conf;
392
    $id = cleanID($id);
393
    $id = str_replace(':','/',$id);
394
    $fn = $conf['mediametadir'].'/'.utf8_encodeFN($id).$ext;
395
    return $fn;
396
}
397
398
/**
399
 * returns an array of full paths to all metafiles of a given ID
400
 *
401
 * @author Esther Brunner <[email protected]>
402
 * @author Michael Hamann <[email protected]>
403
 *
404
 * @param string $id page id
405
 * @return array
406
 */
407
function metaFiles($id){
408
    $basename = metaFN($id, '');
409
    $files    = glob($basename.'.*', GLOB_MARK);
410
    // filter files like foo.bar.meta when $id == 'foo'
411
    return    $files ? preg_grep('/^'.preg_quote($basename, '/').'\.[^.\/]*$/u', $files) : array();
412
}
413
414
/**
415
 * returns the full path to the mediafile specified by ID
416
 *
417
 * The filename is URL encoded to protect Unicode chars
418
 *
419
 * @author Andreas Gohr <[email protected]>
420
 * @author Kate Arzamastseva <[email protected]>
421
 *
422
 * @param string     $id  media id
423
 * @param string|int $rev empty string or revision timestamp
424
 * @param bool $clean
425
 *
426
 * @return string full path
427
 */
428
function mediaFN($id, $rev='', $clean=true){
429
    global $conf;
430
    if ($clean) $id = cleanID($id);
431
    $id = str_replace(':','/',$id);
432
    if(empty($rev)){
433
        $fn = $conf['mediadir'].'/'.utf8_encodeFN($id);
434
    }else{
435
        $ext = mimetype($id);
436
        $name = substr($id,0, -1*strlen($ext[0])-1);
437
        $fn = $conf['mediaolddir'].'/'.utf8_encodeFN($name .'.'.( (int) $rev ).'.'.$ext[0]);
438
    }
439
    return $fn;
440
}
441
442
/**
443
 * Returns the full filepath to a localized file if local
444
 * version isn't found the english one is returned
445
 *
446
 * @param  string $id  The id of the local file
447
 * @param  string $ext The file extension (usually txt)
448
 * @return string full filepath to localized file
449
 *
450
 * @author Andreas Gohr <[email protected]>
451
 */
452
function localeFN($id,$ext='txt'){
453
    global $conf;
454
    $file = DOKU_CONF.'lang/'.$conf['lang'].'/'.$id.'.'.$ext;
455
    if(!file_exists($file)){
456
        $file = DOKU_INC.'inc/lang/'.$conf['lang'].'/'.$id.'.'.$ext;
457
        if(!file_exists($file)){
458
            //fall back to english
459
            $file = DOKU_INC.'inc/lang/en/'.$id.'.'.$ext;
460
        }
461
    }
462
    return $file;
463
}
464
465
/**
466
 * Resolve relative paths in IDs
467
 *
468
 * Do not call directly use resolve_mediaid or resolve_pageid
469
 * instead
470
 *
471
 * Partyly based on a cleanPath function found at
472
 * http://php.net/manual/en/function.realpath.php#57016
473
 *
474
 * @author <bart at mediawave dot nl>
475
 *
476
 * @param string $ns     namespace which is context of id
477
 * @param string $id     relative id
478
 * @param bool   $clean  flag indicating that id should be cleaned
479
 * @return string
480
 */
481
function resolve_id($ns,$id,$clean=true){
482
    global $conf;
483
484
    // some pre cleaning for useslash:
485
    if($conf['useslash']) $id = str_replace('/',':',$id);
486
487
    // if the id starts with a dot we need to handle the
488
    // relative stuff
489
    if($id && $id{0} == '.'){
490
        // normalize initial dots without a colon
491
        $id = preg_replace('/^(\.+)(?=[^:\.])/','\1:',$id);
492
        // prepend the current namespace
493
        $id = $ns.':'.$id;
494
495
        // cleanup relatives
496
        $result = array();
497
        $pathA  = explode(':', $id);
498
        if (!$pathA[0]) $result[] = '';
499
        foreach ($pathA AS $key => $dir) {
500
            if ($dir == '..') {
501
                if (end($result) == '..') {
502
                    $result[] = '..';
503
                } elseif (!array_pop($result)) {
504
                    $result[] = '..';
505
                }
506
            } elseif ($dir && $dir != '.') {
507
                $result[] = $dir;
508
            }
509
        }
510
        if (!end($pathA)) $result[] = '';
511
        $id = implode(':', $result);
512
    }elseif($ns !== false && strpos($id,':') === false){
513
        //if link contains no namespace. add current namespace (if any)
514
        $id = $ns.':'.$id;
515
    }
516
517
    if($clean) $id = cleanID($id);
518
    return $id;
519
}
520
521
/**
522
 * Returns a full media id
523
 *
524
 * @author Andreas Gohr <[email protected]>
525
 *
526
 * @param string $ns namespace which is context of id
527
 * @param string &$page (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
 */
532
function resolve_mediaid($ns,&$page,&$exists,$rev='',$date_at=false){
533
    $page   = resolve_id($ns,$page);
534
    if($rev !== '' &&  $date_at){
535
        $medialog = new MediaChangeLog($page);
536
        $medialog_rev = $medialog->getLastRevisionAt($rev);
537
        if($medialog_rev !== false) {
538
            $rev = $medialog_rev;
539
        }
540
    }
541
542
    $file   = mediaFN($page,$rev);
543
    $exists = file_exists($file);
544
}
545
546
/**
547
 * Returns a full page id
548
 *
549
 * @author Andreas Gohr <[email protected]>
550
 *
551
 * @param string $ns namespace which is context of id
552
 * @param string &$page (reference) relative page id, updated to resolved id
553
 * @param bool &$exists (reference) updated with existance of media
554
 * @param string $rev
555
 * @param bool $date_at
556
 */
557
function resolve_pageid($ns,&$page,&$exists,$rev='',$date_at=false ){
558
    global $conf;
559
    global $ID;
560
    $exists = false;
561
562
    //empty address should point to current page
563
    if ($page === "") {
564
        $page = $ID;
565
    }
566
567
    //keep hashlink if exists then clean both parts
568
    if (strpos($page,'#')) {
569
        list($page,$hash) = explode('#',$page,2);
570
    } else {
571
        $hash = '';
572
    }
573
    $hash = cleanID($hash);
574
    $page = resolve_id($ns,$page,false); // resolve but don't clean, yet
575
576
    // get filename (calls clean itself)
577
    if($rev !== '' && $date_at) {
578
        $pagelog = new PageChangeLog($page);
579
        $pagelog_rev = $pagelog->getLastRevisionAt($rev);
580
        if($pagelog_rev !== false)//something found
581
           $rev  = $pagelog_rev;
582
    }
583
    $file = wikiFN($page,$rev);
584
585
    // if ends with colon or slash we have a namespace link
586
    if(in_array(substr($page,-1), array(':', ';')) ||
587
       ($conf['useslash'] && substr($page,-1) == '/')){
588
        if(page_exists($page.$conf['start'],$rev,true,$date_at)){
589
            // start page inside namespace
590
            $page = $page.$conf['start'];
591
            $exists = true;
592
        }elseif(page_exists($page.noNS(cleanID($page)),$rev,true,$date_at)){
593
            // page named like the NS inside the NS
594
            $page = $page.noNS(cleanID($page));
595
            $exists = true;
596
        }elseif(page_exists($page,$rev,true,$date_at)){
597
            // page like namespace exists
598
            $page = $page;
0 ignored issues
show
Bug introduced by
Why assign $page to itself?

This checks looks for cases where a variable has been assigned to itself.

This assignement can be removed without consequences.

Loading history...
599
            $exists = true;
600
        }else{
601
            // fall back to default
602
            $page = $page.$conf['start'];
603
        }
604
    }else{
605
        //check alternative plural/nonplural form
606
        if(!file_exists($file)){
607
            if( $conf['autoplural'] ){
608
                if(substr($page,-1) == 's'){
609
                    $try = substr($page,0,-1);
610
                }else{
611
                    $try = $page.'s';
612
                }
613
                if(page_exists($try,$rev,true,$date_at)){
614
                    $page   = $try;
615
                    $exists = true;
616
                }
617
            }
618
        }else{
619
            $exists = true;
620
        }
621
    }
622
623
    // now make sure we have a clean page
624
    $page = cleanID($page);
625
626
    //add hash if any
627
    if(!empty($hash)) $page .= '#'.$hash;
628
}
629
630
/**
631
 * Returns the name of a cachefile from given data
632
 *
633
 * The needed directory is created by this function!
634
 *
635
 * @author Andreas Gohr <[email protected]>
636
 *
637
 * @param string $data  This data is used to create a unique md5 name
638
 * @param string $ext   This is appended to the filename if given
639
 * @return string       The filename of the cachefile
640
 */
641
function getCacheName($data,$ext=''){
642
    global $conf;
643
    $md5  = md5($data);
644
    $file = $conf['cachedir'].'/'.$md5{0}.'/'.$md5.$ext;
645
    io_makeFileDir($file);
646
    return $file;
647
}
648
649
/**
650
 * Checks a pageid against $conf['hidepages']
651
 *
652
 * @author Andreas Gohr <[email protected]>
653
 *
654
 * @param string $id page id
655
 * @return bool
656
 */
657
function isHiddenPage($id){
658
    $data = array(
659
        'id' => $id,
660
        'hidden' => false
661
    );
662
    \dokuwiki\Extension\Event::createAndTrigger('PAGEUTILS_ID_HIDEPAGE', $data, '_isHiddenPage');
663
    return $data['hidden'];
664
}
665
666
/**
667
 * callback checks if page is hidden
668
 *
669
 * @param array $data event data    - see isHiddenPage()
670
 */
671
function _isHiddenPage(&$data) {
672
    global $conf;
673
    global $ACT;
674
675
    if ($data['hidden']) return;
676
    if(empty($conf['hidepages'])) return;
677
    if($ACT == 'admin') return;
678
679
    if(preg_match('/'.$conf['hidepages'].'/ui',':'.$data['id'])){
680
        $data['hidden'] = true;
681
    }
682
}
683
684
/**
685
 * Reverse of isHiddenPage
686
 *
687
 * @author Andreas Gohr <[email protected]>
688
 *
689
 * @param string $id page id
690
 * @return bool
691
 */
692
function isVisiblePage($id){
693
    return !isHiddenPage($id);
694
}
695
696
/**
697
 * Format an id for output to a user
698
 *
699
 * Namespaces are denoted by a trailing “:*”. The root namespace is
700
 * “*”. Output is escaped.
701
 *
702
 * @author Adrian Lang <[email protected]>
703
 *
704
 * @param string $id page id
705
 * @return string
706
 */
707
function prettyprint_id($id) {
708
    if (!$id || $id === ':') {
709
        return '*';
710
    }
711
    if ((substr($id, -1, 1) === ':')) {
712
        $id .= '*';
713
    }
714
    return hsc($id);
715
}
716
717
/**
718
 * Encode a UTF-8 filename to use on any filesystem
719
 *
720
 * Uses the 'fnencode' option to determine encoding
721
 *
722
 * When the second parameter is true the string will
723
 * be encoded only if non ASCII characters are detected -
724
 * This makes it safe to run it multiple times on the
725
 * same string (default is true)
726
 *
727
 * @author Andreas Gohr <[email protected]>
728
 * @see    urlencode
729
 *
730
 * @param string $file file name
731
 * @param bool   $safe if true, only encoded when non ASCII characters detected
732
 * @return string
733
 */
734
function utf8_encodeFN($file,$safe=true){
735
    global $conf;
736
    if($conf['fnencode'] == 'utf-8') return $file;
737
738
    if($safe && preg_match('#^[a-zA-Z0-9/_\-\.%]+$#',$file)){
739
        return $file;
740
    }
741
742
    if($conf['fnencode'] == 'safe'){
743
        return SafeFN::encode($file);
744
    }
745
746
    $file = urlencode($file);
747
    $file = str_replace('%2F','/',$file);
748
    return $file;
749
}
750
751
/**
752
 * Decode a filename back to UTF-8
753
 *
754
 * Uses the 'fnencode' option to determine encoding
755
 *
756
 * @author Andreas Gohr <[email protected]>
757
 * @see    urldecode
758
 *
759
 * @param string $file file name
760
 * @return string
761
 */
762
function utf8_decodeFN($file){
763
    global $conf;
764
    if($conf['fnencode'] == 'utf-8') return $file;
765
766
    if($conf['fnencode'] == 'safe'){
767
        return SafeFN::decode($file);
768
    }
769
770
    return urldecode($file);
771
}
772
773
/**
774
 * Find a page in the current namespace (determined from $ID) or any
775
 * higher namespace that can be accessed by the current user,
776
 * this condition can be overriden by an optional parameter.
777
 *
778
 * Used for sidebars, but can be used other stuff as well
779
 *
780
 * @todo   add event hook
781
 *
782
 * @param  string $page the pagename you're looking for
783
 * @param bool $useacl only return pages readable by the current user, false to ignore ACLs
784
 * @return false|string the full page id of the found page, false if any
785
 */
786
function page_findnearest($page, $useacl = true){
787
    if ((string) $page === '') return false;
788
    global $ID;
789
790
    $ns = $ID;
791
    do {
792
        $ns = getNS($ns);
793
        $pageid = cleanID("$ns:$page");
794
        if(page_exists($pageid) && (!$useacl || auth_quickaclcheck($pageid) >= AUTH_READ)){
795
            return $pageid;
796
        }
797
    } while($ns !== false);
798
799
    return false;
800
}
801