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

inc/pageutils.php (2 issues)

Labels
Severity

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

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
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
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));
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) {
239
        $p = curNS($id);
240
        if ($p == false) {
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;
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