~ubuntu-branches/ubuntu/karmic/dokuwiki/karmic

« back to all changes in this revision

Viewing changes to inc/pageutils.php

  • Committer: Bazaar Package Importer
  • Author(s): Mohammed Adnène Trojette
  • Date: 2007-03-29 19:44:52 UTC
  • mfrom: (2.1.6 feisty)
  • Revision ID: james.westby@ubuntu.com-20070329194452-8r2w798oo21ago6l
Tags: 0.0.20061106-6
* High-urgency upload for fixing RC bug.
* Make fr.po's translation of "global" consistent. (Closes: #416509)
* Remove /etc/apache*/conf.d/ on purge. (Closes: #387974)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
<?php
 
2
/**
 
3
 * Utilities for handling pagenames
 
4
 *
 
5
 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
 
6
 * @author     Andreas Gohr <andi@splitbrain.org>
 
7
 * @todo       Combine similar functions like {wiki,media,meta}FN()
 
8
 */
 
9
 
 
10
/**
 
11
 * Fetch the an ID from request
 
12
 *
 
13
 * Uses either standard $_REQUEST variable or extracts it from
 
14
 * the full request URI when userewrite is set to 2
 
15
 *
 
16
 * For $param='id' $conf['start'] is returned if no id was found.
 
17
 * If the second parameter is true (default) the ID is cleaned.
 
18
 *
 
19
 * @author Andreas Gohr <andi@splitbrain.org>
 
20
 */
 
21
function getID($param='id',$clean=true){
 
22
  global $conf;
 
23
 
 
24
  $id = isset($_REQUEST[$param]) ? $_REQUEST[$param] : null;
 
25
 
 
26
  //construct page id from request URI
 
27
  if(empty($id) && $conf['userewrite'] == 2){
 
28
    //get the script URL
 
29
    if($conf['basedir']){
 
30
      $relpath = '';
 
31
      if($param != 'id') {
 
32
        $relpath = 'lib/exe/';
 
33
      }
 
34
      $script = $conf['basedir'].$relpath.basename($_SERVER['SCRIPT_FILENAME']);
 
35
    }elseif($_SERVER['DOCUMENT_ROOT'] && $_SERVER['SCRIPT_FILENAME']){
 
36
      $script = preg_replace ('/^'.preg_quote($_SERVER['DOCUMENT_ROOT'],'/').'/','',
 
37
                              $_SERVER['SCRIPT_FILENAME']);
 
38
      $script = '/'.$script;
 
39
    }else{
 
40
      $script = $_SERVER['SCRIPT_NAME'];
 
41
    }
 
42
 
 
43
    //clean script and request (fixes a windows problem)
 
44
    $script  = preg_replace('/\/\/+/','/',$script);
 
45
    $request = preg_replace('/\/\/+/','/',$_SERVER['REQUEST_URI']);
 
46
 
 
47
    //remove script URL and Querystring to gain the id
 
48
    if(preg_match('/^'.preg_quote($script,'/').'(.*)/',$request, $match)){
 
49
      $id = preg_replace ('/\?.*/','',$match[1]);
 
50
    }
 
51
    $id = urldecode($id);
 
52
    //strip leading slashes
 
53
    $id = preg_replace('!^/+!','',$id);
 
54
  }
 
55
  if($clean) $id = cleanID($id);
 
56
  if(empty($id) && $param=='id') $id = $conf['start'];
 
57
 
 
58
  return $id;
 
59
}
 
60
 
 
61
/**
 
62
 * Remove unwanted chars from ID
 
63
 *
 
64
 * Cleans a given ID to only use allowed characters. Accented characters are
 
65
 * converted to unaccented ones
 
66
 *
 
67
 * @author Andreas Gohr <andi@splitbrain.org>
 
68
 * @param  string  $raw_id    The pageid to clean
 
69
 * @param  boolean $ascii     Force ASCII
 
70
 */
 
71
function cleanID($raw_id,$ascii=false){
 
72
  global $conf;
 
73
  global $lang;
 
74
  static $sepcharpat = null;
 
75
 
 
76
  global $cache_cleanid;
 
77
  $cache = & $cache_cleanid;
 
78
 
 
79
  // check if it's already in the memory cache
 
80
  if (isset($cache[$raw_id])) {
 
81
    return $cache[$raw_id];
 
82
        }
 
83
 
 
84
  $sepchar = $conf['sepchar'];
 
85
  if($sepcharpat == null) // build string only once to save clock cycles
 
86
    $sepcharpat = '#\\'.$sepchar.'+#';
 
87
 
 
88
  $id = trim($raw_id);
 
89
  $id = utf8_strtolower($id);
 
90
 
 
91
  //alternative namespace seperator
 
92
  $id = strtr($id,';',':');
 
93
  if($conf['useslash']){
 
94
    $id = strtr($id,'/',':');
 
95
  }else{
 
96
    $id = strtr($id,'/',$sepchar);
 
97
  }
 
98
 
 
99
  if($conf['deaccent'] == 2 || $ascii) $id = utf8_romanize($id);
 
100
  if($conf['deaccent'] || $ascii) $id = utf8_deaccent($id,-1);
 
101
 
 
102
  //remove specials
 
103
  $id = utf8_stripspecials($id,$sepchar,'\*');
 
104
 
 
105
  if($ascii) $id = utf8_strip($id);
 
106
 
 
107
  //clean up
 
108
  $id = preg_replace($sepcharpat,$sepchar,$id);
 
109
  $id = preg_replace('#:+#',':',$id);
 
110
  $id = trim($id,':._-');
 
111
  $id = preg_replace('#:[:\._\-]+#',':',$id);
 
112
 
 
113
  $cache[$raw_id] = $id;
 
114
  return($id);
 
115
}
 
116
 
 
117
/**
 
118
 * Return namespacepart of a wiki ID
 
119
 *
 
120
 * @author Andreas Gohr <andi@splitbrain.org>
 
121
 */
 
122
function getNS($id){
 
123
  $pos = strrpos($id,':');
 
124
  if($pos!==false){
 
125
    return substr($id,0,$pos);
 
126
  }
 
127
  return false;
 
128
}
 
129
 
 
130
/**
 
131
 * Returns the ID without the namespace
 
132
 *
 
133
 * @author Andreas Gohr <andi@splitbrain.org>
 
134
 */
 
135
function noNS($id) {
 
136
  $pos = strrpos($id, ':');
 
137
  if ($pos!==false) {
 
138
    return substr($id, $pos+1);
 
139
  } else {
 
140
    return $id;
 
141
  }
 
142
}
 
143
 
 
144
/**
 
145
 * returns the full path to the datafile specified by ID and
 
146
 * optional revision
 
147
 *
 
148
 * The filename is URL encoded to protect Unicode chars
 
149
 *
 
150
 * @author Andreas Gohr <andi@splitbrain.org>
 
151
 */
 
152
function wikiFN($raw_id,$rev='',$clean=true){
 
153
  global $conf;
 
154
 
 
155
  global $cache_wikifn;
 
156
  $cache = & $cache_wikifn;
 
157
 
 
158
  if (isset($cache[$raw_id]) && isset($cache[$raw_id][$rev])) {
 
159
    return $cache[$raw_id][$rev];
 
160
  }
 
161
 
 
162
  $id = $raw_id;
 
163
 
 
164
  if ($clean) $id = cleanID($id);
 
165
  $id = str_replace(':','/',$id);
 
166
  if(empty($rev)){
 
167
    $fn = $conf['datadir'].'/'.utf8_encodeFN($id).'.txt';
 
168
  }else{
 
169
    $fn = $conf['olddir'].'/'.utf8_encodeFN($id).'.'.$rev.'.txt';
 
170
    if($conf['compression']){
 
171
      //test for extensions here, we want to read both compressions
 
172
       if (@file_exists($fn . '.gz')){
 
173
          $fn .= '.gz';
 
174
       }else if(@file_exists($fn . '.bz2')){
 
175
          $fn .= '.bz2';
 
176
       }else{
 
177
          //file doesnt exist yet, so we take the configured extension
 
178
          $fn .= '.' . $conf['compression'];
 
179
       }
 
180
    }
 
181
  }
 
182
 
 
183
  if (!isset($cache[$raw_id])) { $cache[$raw_id] = array(); }
 
184
  $cache[$raw_id][$rev] = $fn;
 
185
  return $fn;
 
186
}
 
187
 
 
188
/**
 
189
 * Returns the full path to the file for locking the page while editing.
 
190
 *
 
191
 * @author Ben Coburn <btcoburn@silicodon.net>
 
192
 */
 
193
function wikiLockFN($id) {
 
194
  global $conf;
 
195
  return $conf['lockdir'].'/'.md5(cleanID($id)).'.lock';
 
196
}
 
197
 
 
198
 
 
199
/**
 
200
 * returns the full path to the meta file specified by ID and extension
 
201
 *
 
202
 * The filename is URL encoded to protect Unicode chars
 
203
 *
 
204
 * @author Steven Danz <steven-danz@kc.rr.com>
 
205
 */
 
206
function metaFN($id,$ext){
 
207
  global $conf;
 
208
  $id = cleanID($id);
 
209
  $id = str_replace(':','/',$id);
 
210
  $fn = $conf['metadir'].'/'.utf8_encodeFN($id).$ext;
 
211
  return $fn;
 
212
}
 
213
 
 
214
/**
 
215
 * returns an array of full paths to all metafiles of a given ID
 
216
 *
 
217
 * @author Esther Brunner <esther@kaffeehaus.ch>
 
218
 */
 
219
function metaFiles($id){
 
220
   $name   = noNS($id);
 
221
   $dir    = metaFN(getNS($id),'');
 
222
   $files  = array();
 
223
 
 
224
   $dh = @opendir($dir);
 
225
   if(!$dh) return $files;
 
226
   while(($file = readdir($dh)) !== false){
 
227
     if(strpos($file,$name.'.') === 0 && !is_dir($dir.$file))
 
228
       $files[] = $dir.$file;
 
229
   }
 
230
   closedir($dh);
 
231
 
 
232
   return $files;
 
233
}
 
234
 
 
235
/**
 
236
 * returns the full path to the mediafile specified by ID
 
237
 *
 
238
 * The filename is URL encoded to protect Unicode chars
 
239
 *
 
240
 * @author Andreas Gohr <andi@splitbrain.org>
 
241
 */
 
242
function mediaFN($id){
 
243
  global $conf;
 
244
  $id = cleanID($id);
 
245
  $id = str_replace(':','/',$id);
 
246
    $fn = $conf['mediadir'].'/'.utf8_encodeFN($id);
 
247
  return $fn;
 
248
}
 
249
 
 
250
/**
 
251
 * Returns the full filepath to a localized textfile if local
 
252
 * version isn't found the english one is returned
 
253
 *
 
254
 * @author Andreas Gohr <andi@splitbrain.org>
 
255
 */
 
256
function localeFN($id){
 
257
  global $conf;
 
258
  $file = DOKU_INC.'inc/lang/'.$conf['lang'].'/'.$id.'.txt';
 
259
  if(!@file_exists($file)){
 
260
    //fall back to english
 
261
    $file = DOKU_INC.'inc/lang/en/'.$id.'.txt';
 
262
  }
 
263
  return $file;
 
264
}
 
265
 
 
266
/**
 
267
 * Resolve relative paths in IDs
 
268
 *
 
269
 * Do not call directly use resolve_mediaid or resolve_pageid
 
270
 * instead
 
271
 *
 
272
 * Partyly based on a cleanPath function found at
 
273
 * http://www.php.net/manual/en/function.realpath.php#57016
 
274
 *
 
275
 * @author <bart at mediawave dot nl>
 
276
 */
 
277
function resolve_id($ns,$id,$clean=true){
 
278
  // if the id starts with a dot we need to handle the
 
279
  // relative stuff
 
280
  if($id{0} == '.'){
 
281
    // normalize initial dots without a colon
 
282
    $id = preg_replace('/^(\.+)(?=[^:\.])/','\1:',$id);
 
283
    // prepend the current namespace
 
284
    $id = $ns.':'.$id;
 
285
 
 
286
    // cleanup relatives
 
287
    $result = array();
 
288
    $pathA  = explode(':', $id);
 
289
    if (!$pathA[0]) $result[] = '';
 
290
    foreach ($pathA AS $key => $dir) {
 
291
      if ($dir == '..') {
 
292
        if (end($result) == '..') {
 
293
          $result[] = '..';
 
294
        } elseif (!array_pop($result)) {
 
295
          $result[] = '..';
 
296
        }
 
297
      } elseif ($dir && $dir != '.') {
 
298
        $result[] = $dir;
 
299
      }
 
300
    }
 
301
    if (!end($pathA)) $result[] = '';
 
302
    $id = implode(':', $result);
 
303
  }elseif($ns !== false && strpos($id,':') === false){
 
304
    //if link contains no namespace. add current namespace (if any)
 
305
    $id = $ns.':'.$id;
 
306
  }
 
307
 
 
308
  if($clean) $id = cleanID($id);
 
309
  return $id;
 
310
}
 
311
 
 
312
/**
 
313
 * Returns a full media id
 
314
 *
 
315
 * @author Andreas Gohr <andi@splitbrain.org>
 
316
 */
 
317
function resolve_mediaid($ns,&$page,&$exists){
 
318
  $page   = resolve_id($ns,$page);
 
319
  $file   = mediaFN($page);
 
320
  $exists = @file_exists($file);
 
321
}
 
322
 
 
323
/**
 
324
 * Returns a full page id
 
325
 *
 
326
 * @author Andreas Gohr <andi@splitbrain.org>
 
327
 */
 
328
function resolve_pageid($ns,&$page,&$exists){
 
329
  global $conf;
 
330
  $exists = false;
 
331
 
 
332
  //keep hashlink if exists then clean both parts
 
333
  if (strpos($page,'#')) {
 
334
    list($page,$hash) = split('#',$page,2);
 
335
  } else {
 
336
    $hash = '';
 
337
  }
 
338
  $hash = cleanID($hash);
 
339
  $page = resolve_id($ns,$page,false); // resolve but don't clean, yet
 
340
 
 
341
  // get filename (calls clean itself)
 
342
  $file = wikiFN($page);
 
343
 
 
344
  // if ends with colon we have a namespace link
 
345
  if(substr($page,-1) == ':'){
 
346
    if(@file_exists(wikiFN($page.$conf['start']))){
 
347
      // start page inside namespace
 
348
      $page = $page.$conf['start'];
 
349
      $exists = true;
 
350
    }elseif(@file_exists(wikiFN($page.noNS(cleanID($page))))){
 
351
      // page named like the NS inside the NS
 
352
      $page = $page.noNS(cleanID($page));
 
353
      $exists = true;
 
354
    }elseif(@file_exists(wikiFN($page))){
 
355
      // page like namespace exists
 
356
      $page = $page;
 
357
      $exists = true;
 
358
    }else{
 
359
      // fall back to default
 
360
      $page = $page.$conf['start'];
 
361
    }
 
362
  }else{
 
363
    //check alternative plural/nonplural form
 
364
    if(!@file_exists($file)){
 
365
      if( $conf['autoplural'] ){
 
366
        if(substr($page,-1) == 's'){
 
367
          $try = substr($page,0,-1);
 
368
        }else{
 
369
          $try = $page.'s';
 
370
        }
 
371
        if(@file_exists(wikiFN($try))){
 
372
          $page   = $try;
 
373
          $exists = true;
 
374
        }
 
375
      }
 
376
    }else{
 
377
      $exists = true;
 
378
    }
 
379
  }
 
380
 
 
381
  // now make sure we have a clean page
 
382
  $page = cleanID($page);
 
383
 
 
384
  //add hash if any
 
385
  if(!empty($hash)) $page .= '#'.$hash;
 
386
}
 
387
 
 
388
/**
 
389
 * Returns the name of a cachefile from given data
 
390
 *
 
391
 * The needed directory is created by this function!
 
392
 *
 
393
 * @author Andreas Gohr <andi@splitbrain.org>
 
394
 *
 
395
 * @param string $data  This data is used to create a unique md5 name
 
396
 * @param string $ext   This is appended to the filename if given
 
397
 * @return string       The filename of the cachefile
 
398
 */
 
399
function getCacheName($data,$ext=''){
 
400
  global $conf;
 
401
  $md5  = md5($data);
 
402
  $file = $conf['cachedir'].'/'.$md5{0}.'/'.$md5.$ext;
 
403
  io_makeFileDir($file);
 
404
  return $file;
 
405
}
 
406
 
 
407
/**
 
408
 * Checks a pageid against $conf['hidepages']
 
409
 *
 
410
 * @author Andreas Gohr <gohr@cosmocode.de>
 
411
 */
 
412
function isHiddenPage($id){
 
413
  global $conf;
 
414
  if(empty($conf['hidepages'])) return false;
 
415
 
 
416
  if(preg_match('/'.$conf['hidepages'].'/ui',':'.$id)){
 
417
    return true;
 
418
  }
 
419
  return false;
 
420
}
 
421
 
 
422
/**
 
423
 * Reverse of isHiddenPage
 
424
 *
 
425
 * @author Andreas Gohr <gohr@cosmocode.de>
 
426
 */
 
427
function isVisiblePage($id){
 
428
  return !isHiddenPage($id);
 
429
}
 
430
 
 
431
/**
 
432
 * Checks and sets HTTP headers for conditional HTTP requests
 
433
 *
 
434
 * @author   Simon Willison <swillison@gmail.com>
 
435
 * @link     http://simon.incutio.com/archive/2003/04/23/conditionalGet
 
436
 * @param    timestamp $timestamp lastmodified time of the cache file
 
437
 * @returns  void or void with previously header() commands executed
 
438
 */
 
439
function http_conditionalRequest($timestamp){
 
440
  // A PHP implementation of conditional get, see
 
441
  //   http://fishbowl.pastiche.org/archives/001132.html
 
442
  $last_modified = substr(date('r', $timestamp), 0, -5).'GMT';
 
443
  $etag = '"'.md5($last_modified).'"';
 
444
  // Send the headers
 
445
  header("Last-Modified: $last_modified");
 
446
  header("ETag: $etag");
 
447
  // See if the client has provided the required headers
 
448
  if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])){
 
449
    $if_modified_since = stripslashes($_SERVER['HTTP_IF_MODIFIED_SINCE']);
 
450
  }else{
 
451
    $if_modified_since = false;
 
452
  }
 
453
 
 
454
  if (isset($_SERVER['HTTP_IF_NONE_MATCH'])){
 
455
    $if_none_match = stripslashes($_SERVER['HTTP_IF_NONE_MATCH']);
 
456
  }else{
 
457
    $if_none_match = false;
 
458
  }
 
459
 
 
460
  if (!$if_modified_since && !$if_none_match){
 
461
    return;
 
462
  }
 
463
 
 
464
  // At least one of the headers is there - check them
 
465
  if ($if_none_match && $if_none_match != $etag) {
 
466
    return; // etag is there but doesn't match
 
467
  }
 
468
 
 
469
  if ($if_modified_since && $if_modified_since != $last_modified) {
 
470
    return; // if-modified-since is there but doesn't match
 
471
  }
 
472
 
 
473
  // Nothing has changed since their last request - serve a 304 and exit
 
474
  header('HTTP/1.0 304 Not Modified');
 
475
  exit;
 
476
}
 
477
 
 
478
//Setup VIM: ex: et ts=2 enc=utf-8 :