1 | <?php
|
---|
2 | /**
|
---|
3 | * Common DokuWiki functions
|
---|
4 | *
|
---|
5 | * @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
|
---|
6 | * @author Andreas Gohr <[email protected]>
|
---|
7 | */
|
---|
8 |
|
---|
9 | if(!defined('DOKU_INC')) die('meh.');
|
---|
10 |
|
---|
11 | /**
|
---|
12 | * These constants are used with the recents function
|
---|
13 | */
|
---|
14 | define('RECENTS_SKIP_DELETED',2);
|
---|
15 | define('RECENTS_SKIP_MINORS',4);
|
---|
16 | define('RECENTS_SKIP_SUBSPACES',8);
|
---|
17 | define('RECENTS_MEDIA_CHANGES',16);
|
---|
18 |
|
---|
19 | /**
|
---|
20 | * Wrapper around htmlspecialchars()
|
---|
21 | *
|
---|
22 | * @author Andreas Gohr <[email protected]>
|
---|
23 | * @see htmlspecialchars()
|
---|
24 | */
|
---|
25 | function hsc($string){
|
---|
26 | return htmlspecialchars($string, ENT_QUOTES, 'UTF-8');
|
---|
27 | }
|
---|
28 |
|
---|
29 | /**
|
---|
30 | * print a newline terminated string
|
---|
31 | *
|
---|
32 | * You can give an indention as optional parameter
|
---|
33 | *
|
---|
34 | * @author Andreas Gohr <[email protected]>
|
---|
35 | */
|
---|
36 | function ptln($string,$indent=0){
|
---|
37 | echo str_repeat(' ', $indent)."$string\n";
|
---|
38 | }
|
---|
39 |
|
---|
40 | /**
|
---|
41 | * strips control characters (<32) from the given string
|
---|
42 | *
|
---|
43 | * @author Andreas Gohr <[email protected]>
|
---|
44 | */
|
---|
45 | function stripctl($string){
|
---|
46 | return preg_replace('/[\x00-\x1F]+/s','',$string);
|
---|
47 | }
|
---|
48 |
|
---|
49 | /**
|
---|
50 | * Return a secret token to be used for CSRF attack prevention
|
---|
51 | *
|
---|
52 | * @author Andreas Gohr <[email protected]>
|
---|
53 | * @link http://en.wikipedia.org/wiki/Cross-site_request_forgery
|
---|
54 | * @link http://christ1an.blogspot.com/2007/04/preventing-csrf-efficiently.html
|
---|
55 | * @return string
|
---|
56 | */
|
---|
57 | function getSecurityToken(){
|
---|
58 | return md5(auth_cookiesalt().session_id());
|
---|
59 | }
|
---|
60 |
|
---|
61 | /**
|
---|
62 | * Check the secret CSRF token
|
---|
63 | */
|
---|
64 | function checkSecurityToken($token=null){
|
---|
65 | if(!$_SERVER['REMOTE_USER']) return true; // no logged in user, no need for a check
|
---|
66 |
|
---|
67 | if(is_null($token)) $token = $_REQUEST['sectok'];
|
---|
68 | if(getSecurityToken() != $token){
|
---|
69 | msg('Security Token did not match. Possible CSRF attack.',-1);
|
---|
70 | return false;
|
---|
71 | }
|
---|
72 | return true;
|
---|
73 | }
|
---|
74 |
|
---|
75 | /**
|
---|
76 | * Print a hidden form field with a secret CSRF token
|
---|
77 | *
|
---|
78 | * @author Andreas Gohr <[email protected]>
|
---|
79 | */
|
---|
80 | function formSecurityToken($print=true){
|
---|
81 | $ret = '<div class="no"><input type="hidden" name="sectok" value="'.getSecurityToken().'" /></div>'."\n";
|
---|
82 | if($print){
|
---|
83 | echo $ret;
|
---|
84 | }else{
|
---|
85 | return $ret;
|
---|
86 | }
|
---|
87 | }
|
---|
88 |
|
---|
89 | /**
|
---|
90 | * Return info about the current document as associative
|
---|
91 | * array.
|
---|
92 | *
|
---|
93 | * @author Andreas Gohr <[email protected]>
|
---|
94 | */
|
---|
95 | function pageinfo(){
|
---|
96 | global $ID;
|
---|
97 | global $REV;
|
---|
98 | global $RANGE;
|
---|
99 | global $USERINFO;
|
---|
100 | global $lang;
|
---|
101 |
|
---|
102 | // include ID & REV not redundant, as some parts of DokuWiki may temporarily change $ID, e.g. p_wiki_xhtml
|
---|
103 | // FIXME ... perhaps it would be better to ensure the temporary changes weren't necessary
|
---|
104 | $info['id'] = $ID;
|
---|
105 | $info['rev'] = $REV;
|
---|
106 |
|
---|
107 | // set info about manager/admin status.
|
---|
108 | $info['isadmin'] = false;
|
---|
109 | $info['ismanager'] = false;
|
---|
110 | if(isset($_SERVER['REMOTE_USER'])){
|
---|
111 | $info['userinfo'] = $USERINFO;
|
---|
112 | $info['perm'] = auth_quickaclcheck($ID);
|
---|
113 | $info['subscribed'] = get_info_subscribed();
|
---|
114 | $info['client'] = $_SERVER['REMOTE_USER'];
|
---|
115 |
|
---|
116 | if($info['perm'] == AUTH_ADMIN){
|
---|
117 | $info['isadmin'] = true;
|
---|
118 | $info['ismanager'] = true;
|
---|
119 | }elseif(auth_ismanager()){
|
---|
120 | $info['ismanager'] = true;
|
---|
121 | }
|
---|
122 |
|
---|
123 | // if some outside auth were used only REMOTE_USER is set
|
---|
124 | if(!$info['userinfo']['name']){
|
---|
125 | $info['userinfo']['name'] = $_SERVER['REMOTE_USER'];
|
---|
126 | }
|
---|
127 |
|
---|
128 | }else{
|
---|
129 | $info['perm'] = auth_aclcheck($ID,'',null);
|
---|
130 | $info['subscribed'] = false;
|
---|
131 | $info['client'] = clientIP(true);
|
---|
132 | }
|
---|
133 |
|
---|
134 | $info['namespace'] = getNS($ID);
|
---|
135 | $info['locked'] = checklock($ID);
|
---|
136 | $info['filepath'] = fullpath(wikiFN($ID));
|
---|
137 | $info['exists'] = @file_exists($info['filepath']);
|
---|
138 | if($REV){
|
---|
139 | //check if current revision was meant
|
---|
140 | if($info['exists'] && (@filemtime($info['filepath'])==$REV)){
|
---|
141 | $REV = '';
|
---|
142 | }elseif($RANGE){
|
---|
143 | //section editing does not work with old revisions!
|
---|
144 | $REV = '';
|
---|
145 | $RANGE = '';
|
---|
146 | msg($lang['nosecedit'],0);
|
---|
147 | }else{
|
---|
148 | //really use old revision
|
---|
149 | $info['filepath'] = fullpath(wikiFN($ID,$REV));
|
---|
150 | $info['exists'] = @file_exists($info['filepath']);
|
---|
151 | }
|
---|
152 | }
|
---|
153 | $info['rev'] = $REV;
|
---|
154 | if($info['exists']){
|
---|
155 | $info['writable'] = (is_writable($info['filepath']) &&
|
---|
156 | ($info['perm'] >= AUTH_EDIT));
|
---|
157 | }else{
|
---|
158 | $info['writable'] = ($info['perm'] >= AUTH_CREATE);
|
---|
159 | }
|
---|
160 | $info['editable'] = ($info['writable'] && empty($info['locked']));
|
---|
161 | $info['lastmod'] = @filemtime($info['filepath']);
|
---|
162 |
|
---|
163 | //load page meta data
|
---|
164 | $info['meta'] = p_get_metadata($ID);
|
---|
165 |
|
---|
166 | //who's the editor
|
---|
167 | if($REV){
|
---|
168 | $revinfo = getRevisionInfo($ID, $REV, 1024);
|
---|
169 | }else{
|
---|
170 | if (is_array($info['meta']['last_change'])) {
|
---|
171 | $revinfo = $info['meta']['last_change'];
|
---|
172 | } else {
|
---|
173 | $revinfo = getRevisionInfo($ID, $info['lastmod'], 1024);
|
---|
174 | // cache most recent changelog line in metadata if missing and still valid
|
---|
175 | if ($revinfo!==false) {
|
---|
176 | $info['meta']['last_change'] = $revinfo;
|
---|
177 | p_set_metadata($ID, array('last_change' => $revinfo));
|
---|
178 | }
|
---|
179 | }
|
---|
180 | }
|
---|
181 | //and check for an external edit
|
---|
182 | if($revinfo!==false && $revinfo['date']!=$info['lastmod']){
|
---|
183 | // cached changelog line no longer valid
|
---|
184 | $revinfo = false;
|
---|
185 | $info['meta']['last_change'] = $revinfo;
|
---|
186 | p_set_metadata($ID, array('last_change' => $revinfo));
|
---|
187 | }
|
---|
188 |
|
---|
189 | $info['ip'] = $revinfo['ip'];
|
---|
190 | $info['user'] = $revinfo['user'];
|
---|
191 | $info['sum'] = $revinfo['sum'];
|
---|
192 | // See also $INFO['meta']['last_change'] which is the most recent log line for page $ID.
|
---|
193 | // Use $INFO['meta']['last_change']['type']===DOKU_CHANGE_TYPE_MINOR_EDIT in place of $info['minor'].
|
---|
194 |
|
---|
195 | if($revinfo['user']){
|
---|
196 | $info['editor'] = $revinfo['user'];
|
---|
197 | }else{
|
---|
198 | $info['editor'] = $revinfo['ip'];
|
---|
199 | }
|
---|
200 |
|
---|
201 | // draft
|
---|
202 | $draft = getCacheName($info['client'].$ID,'.draft');
|
---|
203 | if(@file_exists($draft)){
|
---|
204 | if(@filemtime($draft) < @filemtime(wikiFN($ID))){
|
---|
205 | // remove stale draft
|
---|
206 | @unlink($draft);
|
---|
207 | }else{
|
---|
208 | $info['draft'] = $draft;
|
---|
209 | }
|
---|
210 | }
|
---|
211 |
|
---|
212 | // mobile detection
|
---|
213 | $info['ismobile'] = clientismobile();
|
---|
214 |
|
---|
215 | return $info;
|
---|
216 | }
|
---|
217 |
|
---|
218 | /**
|
---|
219 | * Build an string of URL parameters
|
---|
220 | *
|
---|
221 | * @author Andreas Gohr
|
---|
222 | */
|
---|
223 | function buildURLparams($params, $sep='&'){
|
---|
224 | $url = '';
|
---|
225 | $amp = false;
|
---|
226 | foreach($params as $key => $val){
|
---|
227 | if($amp) $url .= $sep;
|
---|
228 |
|
---|
229 | $url .= rawurlencode($key).'=';
|
---|
230 | $url .= rawurlencode((string)$val);
|
---|
231 | $amp = true;
|
---|
232 | }
|
---|
233 | return $url;
|
---|
234 | }
|
---|
235 |
|
---|
236 | /**
|
---|
237 | * Build an string of html tag attributes
|
---|
238 | *
|
---|
239 | * Skips keys starting with '_', values get HTML encoded
|
---|
240 | *
|
---|
241 | * @author Andreas Gohr
|
---|
242 | */
|
---|
243 | function buildAttributes($params,$skipempty=false){
|
---|
244 | $url = '';
|
---|
245 | $white = false;
|
---|
246 | foreach($params as $key => $val){
|
---|
247 | if($key{0} == '_') continue;
|
---|
248 | if($val === '' && $skipempty) continue;
|
---|
249 | if($white) $url .= ' ';
|
---|
250 |
|
---|
251 | $url .= $key.'="';
|
---|
252 | $url .= htmlspecialchars ($val);
|
---|
253 | $url .= '"';
|
---|
254 | $white = true;
|
---|
255 | }
|
---|
256 | return $url;
|
---|
257 | }
|
---|
258 |
|
---|
259 |
|
---|
260 | /**
|
---|
261 | * This builds the breadcrumb trail and returns it as array
|
---|
262 | *
|
---|
263 | * @author Andreas Gohr <[email protected]>
|
---|
264 | */
|
---|
265 | function breadcrumbs(){
|
---|
266 | // we prepare the breadcrumbs early for quick session closing
|
---|
267 | static $crumbs = null;
|
---|
268 | if($crumbs != null) return $crumbs;
|
---|
269 |
|
---|
270 | global $ID;
|
---|
271 | global $ACT;
|
---|
272 | global $conf;
|
---|
273 |
|
---|
274 | //first visit?
|
---|
275 | $crumbs = isset($_SESSION[DOKU_COOKIE]['bc']) ? $_SESSION[DOKU_COOKIE]['bc'] : array();
|
---|
276 | //we only save on show and existing wiki documents
|
---|
277 | $file = wikiFN($ID);
|
---|
278 | if($ACT != 'show' || !@file_exists($file)){
|
---|
279 | $_SESSION[DOKU_COOKIE]['bc'] = $crumbs;
|
---|
280 | return $crumbs;
|
---|
281 | }
|
---|
282 |
|
---|
283 | // page names
|
---|
284 | $name = noNSorNS($ID);
|
---|
285 | if (useHeading('navigation')) {
|
---|
286 | // get page title
|
---|
287 | $title = p_get_first_heading($ID,METADATA_RENDER_USING_SIMPLE_CACHE);
|
---|
288 | if ($title) {
|
---|
289 | $name = $title;
|
---|
290 | }
|
---|
291 | }
|
---|
292 |
|
---|
293 | //remove ID from array
|
---|
294 | if (isset($crumbs[$ID])) {
|
---|
295 | unset($crumbs[$ID]);
|
---|
296 | }
|
---|
297 |
|
---|
298 | //add to array
|
---|
299 | $crumbs[$ID] = $name;
|
---|
300 | //reduce size
|
---|
301 | while(count($crumbs) > $conf['breadcrumbs']){
|
---|
302 | array_shift($crumbs);
|
---|
303 | }
|
---|
304 | //save to session
|
---|
305 | $_SESSION[DOKU_COOKIE]['bc'] = $crumbs;
|
---|
306 | return $crumbs;
|
---|
307 | }
|
---|
308 |
|
---|
309 | /**
|
---|
310 | * Filter for page IDs
|
---|
311 | *
|
---|
312 | * This is run on a ID before it is outputted somewhere
|
---|
313 | * currently used to replace the colon with something else
|
---|
314 | * on Windows systems and to have proper URL encoding
|
---|
315 | *
|
---|
316 | * Urlencoding is ommitted when the second parameter is false
|
---|
317 | *
|
---|
318 | * @author Andreas Gohr <[email protected]>
|
---|
319 | */
|
---|
320 | function idfilter($id,$ue=true){
|
---|
321 | global $conf;
|
---|
322 | if ($conf['useslash'] && $conf['userewrite']){
|
---|
323 | $id = strtr($id,':','/');
|
---|
324 | }elseif (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN' &&
|
---|
325 | $conf['userewrite']) {
|
---|
326 | $id = strtr($id,':',';');
|
---|
327 | }
|
---|
328 | if($ue){
|
---|
329 | $id = rawurlencode($id);
|
---|
330 | $id = str_replace('%3A',':',$id); //keep as colon
|
---|
331 | $id = str_replace('%2F','/',$id); //keep as slash
|
---|
332 | }
|
---|
333 | return $id;
|
---|
334 | }
|
---|
335 |
|
---|
336 | /**
|
---|
337 | * This builds a link to a wikipage
|
---|
338 | *
|
---|
339 | * It handles URL rewriting and adds additional parameter if
|
---|
340 | * given in $more
|
---|
341 | *
|
---|
342 | * @author Andreas Gohr <[email protected]>
|
---|
343 | */
|
---|
344 | function wl($id='',$more='',$abs=false,$sep='&'){
|
---|
345 | global $conf;
|
---|
346 | if(is_array($more)){
|
---|
347 | $more = buildURLparams($more,$sep);
|
---|
348 | }else{
|
---|
349 | $more = str_replace(',',$sep,$more);
|
---|
350 | }
|
---|
351 |
|
---|
352 | $id = idfilter($id);
|
---|
353 | if($abs){
|
---|
354 | $xlink = DOKU_URL;
|
---|
355 | }else{
|
---|
356 | $xlink = DOKU_BASE;
|
---|
357 | }
|
---|
358 |
|
---|
359 | if($conf['userewrite'] == 2){
|
---|
360 | $xlink .= DOKU_SCRIPT.'/'.$id;
|
---|
361 | if($more) $xlink .= '?'.$more;
|
---|
362 | }elseif($conf['userewrite']){
|
---|
363 | $xlink .= $id;
|
---|
364 | if($more) $xlink .= '?'.$more;
|
---|
365 | }elseif($id){
|
---|
366 | $xlink .= DOKU_SCRIPT.'?id='.$id;
|
---|
367 | if($more) $xlink .= $sep.$more;
|
---|
368 | }else{
|
---|
369 | $xlink .= DOKU_SCRIPT;
|
---|
370 | if($more) $xlink .= '?'.$more;
|
---|
371 | }
|
---|
372 |
|
---|
373 | return $xlink;
|
---|
374 | }
|
---|
375 |
|
---|
376 | /**
|
---|
377 | * This builds a link to an alternate page format
|
---|
378 | *
|
---|
379 | * Handles URL rewriting if enabled. Follows the style of wl().
|
---|
380 | *
|
---|
381 | * @author Ben Coburn <[email protected]>
|
---|
382 | */
|
---|
383 | function exportlink($id='',$format='raw',$more='',$abs=false,$sep='&'){
|
---|
384 | global $conf;
|
---|
385 | if(is_array($more)){
|
---|
386 | $more = buildURLparams($more,$sep);
|
---|
387 | }else{
|
---|
388 | $more = str_replace(',',$sep,$more);
|
---|
389 | }
|
---|
390 |
|
---|
391 | $format = rawurlencode($format);
|
---|
392 | $id = idfilter($id);
|
---|
393 | if($abs){
|
---|
394 | $xlink = DOKU_URL;
|
---|
395 | }else{
|
---|
396 | $xlink = DOKU_BASE;
|
---|
397 | }
|
---|
398 |
|
---|
399 | if($conf['userewrite'] == 2){
|
---|
400 | $xlink .= DOKU_SCRIPT.'/'.$id.'?do=export_'.$format;
|
---|
401 | if($more) $xlink .= $sep.$more;
|
---|
402 | }elseif($conf['userewrite'] == 1){
|
---|
403 | $xlink .= '_export/'.$format.'/'.$id;
|
---|
404 | if($more) $xlink .= '?'.$more;
|
---|
405 | }else{
|
---|
406 | $xlink .= DOKU_SCRIPT.'?do=export_'.$format.$sep.'id='.$id;
|
---|
407 | if($more) $xlink .= $sep.$more;
|
---|
408 | }
|
---|
409 |
|
---|
410 | return $xlink;
|
---|
411 | }
|
---|
412 |
|
---|
413 | /**
|
---|
414 | * Build a link to a media file
|
---|
415 | *
|
---|
416 | * Will return a link to the detail page if $direct is false
|
---|
417 | *
|
---|
418 | * The $more parameter should always be given as array, the function then
|
---|
419 | * will strip default parameters to produce even cleaner URLs
|
---|
420 | *
|
---|
421 | * @param string $id - the media file id or URL
|
---|
422 | * @param mixed $more - string or array with additional parameters
|
---|
423 | * @param boolean $direct - link to detail page if false
|
---|
424 | * @param string $sep - URL parameter separator
|
---|
425 | * @param boolean $abs - Create an absolute URL
|
---|
426 | */
|
---|
427 | function ml($id='',$more='',$direct=true,$sep='&',$abs=false){
|
---|
428 | global $conf;
|
---|
429 | if(is_array($more)){
|
---|
430 | // strip defaults for shorter URLs
|
---|
431 | if(isset($more['cache']) && $more['cache'] == 'cache') unset($more['cache']);
|
---|
432 | if(!$more['w']) unset($more['w']);
|
---|
433 | if(!$more['h']) unset($more['h']);
|
---|
434 | if(isset($more['id']) && $direct) unset($more['id']);
|
---|
435 | $more = buildURLparams($more,$sep);
|
---|
436 | }else{
|
---|
437 | $more = str_replace('cache=cache','',$more); //skip default
|
---|
438 | $more = str_replace(',,',',',$more);
|
---|
439 | $more = str_replace(',',$sep,$more);
|
---|
440 | }
|
---|
441 |
|
---|
442 | if($abs){
|
---|
443 | $xlink = DOKU_URL;
|
---|
444 | }else{
|
---|
445 | $xlink = DOKU_BASE;
|
---|
446 | }
|
---|
447 |
|
---|
448 | // external URLs are always direct without rewriting
|
---|
449 | if(preg_match('#^(https?|ftp)://#i',$id)){
|
---|
450 | $xlink .= 'lib/exe/fetch.php';
|
---|
451 | // add hash:
|
---|
452 | $xlink .= '?hash='.substr(md5(auth_cookiesalt().$id),0,6);
|
---|
453 | if($more){
|
---|
454 | $xlink .= $sep.$more;
|
---|
455 | $xlink .= $sep.'media='.rawurlencode($id);
|
---|
456 | }else{
|
---|
457 | $xlink .= $sep.'media='.rawurlencode($id);
|
---|
458 | }
|
---|
459 | return $xlink;
|
---|
460 | }
|
---|
461 |
|
---|
462 | $id = idfilter($id);
|
---|
463 |
|
---|
464 | // decide on scriptname
|
---|
465 | if($direct){
|
---|
466 | if($conf['userewrite'] == 1){
|
---|
467 | $script = '_media';
|
---|
468 | }else{
|
---|
469 | $script = 'lib/exe/fetch.php';
|
---|
470 | }
|
---|
471 | }else{
|
---|
472 | if($conf['userewrite'] == 1){
|
---|
473 | $script = '_detail';
|
---|
474 | }else{
|
---|
475 | $script = 'lib/exe/detail.php';
|
---|
476 | }
|
---|
477 | }
|
---|
478 |
|
---|
479 | // build URL based on rewrite mode
|
---|
480 | if($conf['userewrite']){
|
---|
481 | $xlink .= $script.'/'.$id;
|
---|
482 | if($more) $xlink .= '?'.$more;
|
---|
483 | }else{
|
---|
484 | if($more){
|
---|
485 | $xlink .= $script.'?'.$more;
|
---|
486 | $xlink .= $sep.'media='.$id;
|
---|
487 | }else{
|
---|
488 | $xlink .= $script.'?media='.$id;
|
---|
489 | }
|
---|
490 | }
|
---|
491 |
|
---|
492 | return $xlink;
|
---|
493 | }
|
---|
494 |
|
---|
495 |
|
---|
496 |
|
---|
497 | /**
|
---|
498 | * Just builds a link to a script
|
---|
499 | *
|
---|
500 | * @todo maybe obsolete
|
---|
501 | * @author Andreas Gohr <[email protected]>
|
---|
502 | */
|
---|
503 | function script($script='doku.php'){
|
---|
504 | return DOKU_BASE.DOKU_SCRIPT;
|
---|
505 | }
|
---|
506 |
|
---|
507 | /**
|
---|
508 | * Spamcheck against wordlist
|
---|
509 | *
|
---|
510 | * Checks the wikitext against a list of blocked expressions
|
---|
511 | * returns true if the text contains any bad words
|
---|
512 | *
|
---|
513 | * Triggers COMMON_WORDBLOCK_BLOCKED
|
---|
514 | *
|
---|
515 | * Action Plugins can use this event to inspect the blocked data
|
---|
516 | * and gain information about the user who was blocked.
|
---|
517 | *
|
---|
518 | * Event data:
|
---|
519 | * data['matches'] - array of matches
|
---|
520 | * data['userinfo'] - information about the blocked user
|
---|
521 | * [ip] - ip address
|
---|
522 | * [user] - username (if logged in)
|
---|
523 | * [mail] - mail address (if logged in)
|
---|
524 | * [name] - real name (if logged in)
|
---|
525 | *
|
---|
526 | * @author Andreas Gohr <[email protected]>
|
---|
527 | * @author Michael Klier <[email protected]>
|
---|
528 | * @param string $text - optional text to check, if not given the globals are used
|
---|
529 | * @return bool - true if a spam word was found
|
---|
530 | */
|
---|
531 | function checkwordblock($text=''){
|
---|
532 | global $TEXT;
|
---|
533 | global $PRE;
|
---|
534 | global $SUF;
|
---|
535 | global $conf;
|
---|
536 | global $INFO;
|
---|
537 |
|
---|
538 | if(!$conf['usewordblock']) return false;
|
---|
539 |
|
---|
540 | if(!$text) $text = "$PRE $TEXT $SUF";
|
---|
541 |
|
---|
542 | // we prepare the text a tiny bit to prevent spammers circumventing URL checks
|
---|
543 | $text = preg_replace('!(\b)(www\.[\w.:?\-;,]+?\.[\w.:?\-;,]+?[\w/\#~:.?+=&%@\!\-.:?\-;,]+?)([.:?\-;,]*[^\w/\#~:.?+=&%@\!\-.:?\-;,])!i','\1http://\2 \2\3',$text);
|
---|
544 |
|
---|
545 | $wordblocks = getWordblocks();
|
---|
546 | // how many lines to read at once (to work around some PCRE limits)
|
---|
547 | if(version_compare(phpversion(),'4.3.0','<')){
|
---|
548 | // old versions of PCRE define a maximum of parenthesises even if no
|
---|
549 | // backreferences are used - the maximum is 99
|
---|
550 | // this is very bad performancewise and may even be too high still
|
---|
551 | $chunksize = 40;
|
---|
552 | }else{
|
---|
553 | // read file in chunks of 200 - this should work around the
|
---|
554 | // MAX_PATTERN_SIZE in modern PCRE
|
---|
555 | $chunksize = 200;
|
---|
556 | }
|
---|
557 | while($blocks = array_splice($wordblocks,0,$chunksize)){
|
---|
558 | $re = array();
|
---|
559 | // build regexp from blocks
|
---|
560 | foreach($blocks as $block){
|
---|
561 | $block = preg_replace('/#.*$/','',$block);
|
---|
562 | $block = trim($block);
|
---|
563 | if(empty($block)) continue;
|
---|
564 | $re[] = $block;
|
---|
565 | }
|
---|
566 | if(count($re) && preg_match('#('.join('|',$re).')#si',$text,$matches)) {
|
---|
567 | // prepare event data
|
---|
568 | $data['matches'] = $matches;
|
---|
569 | $data['userinfo']['ip'] = $_SERVER['REMOTE_ADDR'];
|
---|
570 | if($_SERVER['REMOTE_USER']) {
|
---|
571 | $data['userinfo']['user'] = $_SERVER['REMOTE_USER'];
|
---|
572 | $data['userinfo']['name'] = $INFO['userinfo']['name'];
|
---|
573 | $data['userinfo']['mail'] = $INFO['userinfo']['mail'];
|
---|
574 | }
|
---|
575 | $callback = create_function('', 'return true;');
|
---|
576 | return trigger_event('COMMON_WORDBLOCK_BLOCKED', $data, $callback, true);
|
---|
577 | }
|
---|
578 | }
|
---|
579 | return false;
|
---|
580 | }
|
---|
581 |
|
---|
582 | /**
|
---|
583 | * Return the IP of the client
|
---|
584 | *
|
---|
585 | * Honours X-Forwarded-For and X-Real-IP Proxy Headers
|
---|
586 | *
|
---|
587 | * It returns a comma separated list of IPs if the above mentioned
|
---|
588 | * headers are set. If the single parameter is set, it tries to return
|
---|
589 | * a routable public address, prefering the ones suplied in the X
|
---|
590 | * headers
|
---|
591 | *
|
---|
592 | * @param boolean $single If set only a single IP is returned
|
---|
593 | * @author Andreas Gohr <[email protected]>
|
---|
594 | */
|
---|
595 | function clientIP($single=false){
|
---|
596 | $ip = array();
|
---|
597 | $ip[] = $_SERVER['REMOTE_ADDR'];
|
---|
598 | if(!empty($_SERVER['HTTP_X_FORWARDED_FOR']))
|
---|
599 | $ip = array_merge($ip,explode(',',str_replace(' ','',$_SERVER['HTTP_X_FORWARDED_FOR'])));
|
---|
600 | if(!empty($_SERVER['HTTP_X_REAL_IP']))
|
---|
601 | $ip = array_merge($ip,explode(',',str_replace(' ','',$_SERVER['HTTP_X_REAL_IP'])));
|
---|
602 |
|
---|
603 | // some IPv4/v6 regexps borrowed from Feyd
|
---|
604 | // see: http://forums.devnetwork.net/viewtopic.php?f=38&t=53479
|
---|
605 | $dec_octet = '(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|[0-9])';
|
---|
606 | $hex_digit = '[A-Fa-f0-9]';
|
---|
607 | $h16 = "{$hex_digit}{1,4}";
|
---|
608 | $IPv4Address = "$dec_octet\\.$dec_octet\\.$dec_octet\\.$dec_octet";
|
---|
609 | $ls32 = "(?:$h16:$h16|$IPv4Address)";
|
---|
610 | $IPv6Address =
|
---|
611 | "(?:(?:{$IPv4Address})|(?:".
|
---|
612 | "(?:$h16:){6}$ls32" .
|
---|
613 | "|::(?:$h16:){5}$ls32" .
|
---|
614 | "|(?:$h16)?::(?:$h16:){4}$ls32" .
|
---|
615 | "|(?:(?:$h16:){0,1}$h16)?::(?:$h16:){3}$ls32" .
|
---|
616 | "|(?:(?:$h16:){0,2}$h16)?::(?:$h16:){2}$ls32" .
|
---|
617 | "|(?:(?:$h16:){0,3}$h16)?::(?:$h16:){1}$ls32" .
|
---|
618 | "|(?:(?:$h16:){0,4}$h16)?::$ls32" .
|
---|
619 | "|(?:(?:$h16:){0,5}$h16)?::$h16" .
|
---|
620 | "|(?:(?:$h16:){0,6}$h16)?::" .
|
---|
621 | ")(?:\\/(?:12[0-8]|1[0-1][0-9]|[1-9][0-9]|[0-9]))?)";
|
---|
622 |
|
---|
623 | // remove any non-IP stuff
|
---|
624 | $cnt = count($ip);
|
---|
625 | $match = array();
|
---|
626 | for($i=0; $i<$cnt; $i++){
|
---|
627 | if(preg_match("/^$IPv4Address$/",$ip[$i],$match) || preg_match("/^$IPv6Address$/",$ip[$i],$match)) {
|
---|
628 | $ip[$i] = $match[0];
|
---|
629 | } else {
|
---|
630 | $ip[$i] = '';
|
---|
631 | }
|
---|
632 | if(empty($ip[$i])) unset($ip[$i]);
|
---|
633 | }
|
---|
634 | $ip = array_values(array_unique($ip));
|
---|
635 | if(!$ip[0]) $ip[0] = '0.0.0.0'; // for some strange reason we don't have a IP
|
---|
636 |
|
---|
637 | if(!$single) return join(',',$ip);
|
---|
638 |
|
---|
639 | // decide which IP to use, trying to avoid local addresses
|
---|
640 | $ip = array_reverse($ip);
|
---|
641 | foreach($ip as $i){
|
---|
642 | if(preg_match('/^(::1|[fF][eE]80:|127\.|10\.|192\.168\.|172\.((1[6-9])|(2[0-9])|(3[0-1]))\.)/',$i)){
|
---|
643 | continue;
|
---|
644 | }else{
|
---|
645 | return $i;
|
---|
646 | }
|
---|
647 | }
|
---|
648 | // still here? just use the first (last) address
|
---|
649 | return $ip[0];
|
---|
650 | }
|
---|
651 |
|
---|
652 | /**
|
---|
653 | * Check if the browser is on a mobile device
|
---|
654 | *
|
---|
655 | * Adapted from the example code at url below
|
---|
656 | *
|
---|
657 | * @link http://www.brainhandles.com/2007/10/15/detecting-mobile-browsers/#code
|
---|
658 | */
|
---|
659 | function clientismobile(){
|
---|
660 |
|
---|
661 | if(isset($_SERVER['HTTP_X_WAP_PROFILE'])) return true;
|
---|
662 |
|
---|
663 | if(preg_match('/wap\.|\.wap/i',$_SERVER['HTTP_ACCEPT'])) return true;
|
---|
664 |
|
---|
665 | if(!isset($_SERVER['HTTP_USER_AGENT'])) return false;
|
---|
666 |
|
---|
667 | $uamatches = 'midp|j2me|avantg|docomo|novarra|palmos|palmsource|240x320|opwv|chtml|pda|windows ce|mmp\/|blackberry|mib\/|symbian|wireless|nokia|hand|mobi|phone|cdm|up\.b|audio|SIE\-|SEC\-|samsung|HTC|mot\-|mitsu|sagem|sony|alcatel|lg|erics|vx|NEC|philips|mmm|xx|panasonic|sharp|wap|sch|rover|pocket|benq|java|pt|pg|vox|amoi|bird|compal|kg|voda|sany|kdd|dbt|sendo|sgh|gradi|jb|\d\d\di|moto';
|
---|
668 |
|
---|
669 | if(preg_match("/$uamatches/i",$_SERVER['HTTP_USER_AGENT'])) return true;
|
---|
670 |
|
---|
671 | return false;
|
---|
672 | }
|
---|
673 |
|
---|
674 |
|
---|
675 | /**
|
---|
676 | * Convert one or more comma separated IPs to hostnames
|
---|
677 | *
|
---|
678 | * @author Glen Harris <[email protected]>
|
---|
679 | * @returns a comma separated list of hostnames
|
---|
680 | */
|
---|
681 | function gethostsbyaddrs($ips){
|
---|
682 | $hosts = array();
|
---|
683 | $ips = explode(',',$ips);
|
---|
684 |
|
---|
685 | if(is_array($ips)) {
|
---|
686 | foreach($ips as $ip){
|
---|
687 | $hosts[] = gethostbyaddr(trim($ip));
|
---|
688 | }
|
---|
689 | return join(',',$hosts);
|
---|
690 | } else {
|
---|
691 | return gethostbyaddr(trim($ips));
|
---|
692 | }
|
---|
693 | }
|
---|
694 |
|
---|
695 | /**
|
---|
696 | * Checks if a given page is currently locked.
|
---|
697 | *
|
---|
698 | * removes stale lockfiles
|
---|
699 | *
|
---|
700 | * @author Andreas Gohr <[email protected]>
|
---|
701 | */
|
---|
702 | function checklock($id){
|
---|
703 | global $conf;
|
---|
704 | $lock = wikiLockFN($id);
|
---|
705 |
|
---|
706 | //no lockfile
|
---|
707 | if(!@file_exists($lock)) return false;
|
---|
708 |
|
---|
709 | //lockfile expired
|
---|
710 | if((time() - filemtime($lock)) > $conf['locktime']){
|
---|
711 | @unlink($lock);
|
---|
712 | return false;
|
---|
713 | }
|
---|
714 |
|
---|
715 | //my own lock
|
---|
716 | $ip = io_readFile($lock);
|
---|
717 | if( ($ip == clientIP()) || ($ip == $_SERVER['REMOTE_USER']) ){
|
---|
718 | return false;
|
---|
719 | }
|
---|
720 |
|
---|
721 | return $ip;
|
---|
722 | }
|
---|
723 |
|
---|
724 | /**
|
---|
725 | * Lock a page for editing
|
---|
726 | *
|
---|
727 | * @author Andreas Gohr <[email protected]>
|
---|
728 | */
|
---|
729 | function lock($id){
|
---|
730 | global $conf;
|
---|
731 |
|
---|
732 | if($conf['locktime'] == 0){
|
---|
733 | return;
|
---|
734 | }
|
---|
735 |
|
---|
736 | $lock = wikiLockFN($id);
|
---|
737 | if($_SERVER['REMOTE_USER']){
|
---|
738 | io_saveFile($lock,$_SERVER['REMOTE_USER']);
|
---|
739 | }else{
|
---|
740 | io_saveFile($lock,clientIP());
|
---|
741 | }
|
---|
742 | }
|
---|
743 |
|
---|
744 | /**
|
---|
745 | * Unlock a page if it was locked by the user
|
---|
746 | *
|
---|
747 | * @author Andreas Gohr <[email protected]>
|
---|
748 | * @return bool true if a lock was removed
|
---|
749 | */
|
---|
750 | function unlock($id){
|
---|
751 | $lock = wikiLockFN($id);
|
---|
752 | if(@file_exists($lock)){
|
---|
753 | $ip = io_readFile($lock);
|
---|
754 | if( ($ip == clientIP()) || ($ip == $_SERVER['REMOTE_USER']) ){
|
---|
755 | @unlink($lock);
|
---|
756 | return true;
|
---|
757 | }
|
---|
758 | }
|
---|
759 | return false;
|
---|
760 | }
|
---|
761 |
|
---|
762 | /**
|
---|
763 | * convert line ending to unix format
|
---|
764 | *
|
---|
765 | * @see formText() for 2crlf conversion
|
---|
766 | * @author Andreas Gohr <[email protected]>
|
---|
767 | */
|
---|
768 | function cleanText($text){
|
---|
769 | $text = preg_replace("/(\015\012)|(\015)/","\012",$text);
|
---|
770 | return $text;
|
---|
771 | }
|
---|
772 |
|
---|
773 | /**
|
---|
774 | * Prepares text for print in Webforms by encoding special chars.
|
---|
775 | * It also converts line endings to Windows format which is
|
---|
776 | * pseudo standard for webforms.
|
---|
777 | *
|
---|
778 | * @see cleanText() for 2unix conversion
|
---|
779 | * @author Andreas Gohr <[email protected]>
|
---|
780 | */
|
---|
781 | function formText($text){
|
---|
782 | $text = str_replace("\012","\015\012",$text);
|
---|
783 | return htmlspecialchars($text);
|
---|
784 | }
|
---|
785 |
|
---|
786 | /**
|
---|
787 | * Returns the specified local text in raw format
|
---|
788 | *
|
---|
789 | * @author Andreas Gohr <[email protected]>
|
---|
790 | */
|
---|
791 | function rawLocale($id){
|
---|
792 | return io_readFile(localeFN($id));
|
---|
793 | }
|
---|
794 |
|
---|
795 | /**
|
---|
796 | * Returns the raw WikiText
|
---|
797 | *
|
---|
798 | * @author Andreas Gohr <[email protected]>
|
---|
799 | */
|
---|
800 | function rawWiki($id,$rev=''){
|
---|
801 | return io_readWikiPage(wikiFN($id, $rev), $id, $rev);
|
---|
802 | }
|
---|
803 |
|
---|
804 | /**
|
---|
805 | * Returns the pagetemplate contents for the ID's namespace
|
---|
806 | *
|
---|
807 | * @triggers COMMON_PAGETPL_LOAD
|
---|
808 | * @author Andreas Gohr <[email protected]>
|
---|
809 | */
|
---|
810 | function pageTemplate($id){
|
---|
811 | global $conf;
|
---|
812 |
|
---|
813 | if (is_array($id)) $id = $id[0];
|
---|
814 |
|
---|
815 | // prepare initial event data
|
---|
816 | $data = array(
|
---|
817 | 'id' => $id, // the id of the page to be created
|
---|
818 | 'tpl' => '', // the text used as template
|
---|
819 | 'tplfile' => '', // the file above text was/should be loaded from
|
---|
820 | 'doreplace' => true // should wildcard replacements be done on the text?
|
---|
821 | );
|
---|
822 |
|
---|
823 | $evt = new Doku_Event('COMMON_PAGETPL_LOAD',$data);
|
---|
824 | if($evt->advise_before(true)){
|
---|
825 | // the before event might have loaded the content already
|
---|
826 | if(empty($data['tpl'])){
|
---|
827 | // if the before event did not set a template file, try to find one
|
---|
828 | if(empty($data['tplfile'])){
|
---|
829 | $path = dirname(wikiFN($id));
|
---|
830 | $tpl = '';
|
---|
831 | if(@file_exists($path.'/_template.txt')){
|
---|
832 | $data['tplfile'] = $path.'/_template.txt';
|
---|
833 | }else{
|
---|
834 | // search upper namespaces for templates
|
---|
835 | $len = strlen(rtrim($conf['datadir'],'/'));
|
---|
836 | while (strlen($path) >= $len){
|
---|
837 | if(@file_exists($path.'/__template.txt')){
|
---|
838 | $data['tplfile'] = $path.'/__template.txt';
|
---|
839 | break;
|
---|
840 | }
|
---|
841 | $path = substr($path, 0, strrpos($path, '/'));
|
---|
842 | }
|
---|
843 | }
|
---|
844 | }
|
---|
845 | // load the content
|
---|
846 | $data['tpl'] = io_readFile($data['tplfile']);
|
---|
847 | }
|
---|
848 | if($data['doreplace']) parsePageTemplate($data);
|
---|
849 | }
|
---|
850 | $evt->advise_after();
|
---|
851 | unset($evt);
|
---|
852 |
|
---|
853 | return $data['tpl'];
|
---|
854 | }
|
---|
855 |
|
---|
856 | /**
|
---|
857 | * Performs common page template replacements
|
---|
858 | * This works on data from COMMON_PAGETPL_LOAD
|
---|
859 | *
|
---|
860 | * @author Andreas Gohr <[email protected]>
|
---|
861 | */
|
---|
862 | function parsePageTemplate(&$data) {
|
---|
863 | extract($data);
|
---|
864 |
|
---|
865 | global $USERINFO;
|
---|
866 | global $conf;
|
---|
867 |
|
---|
868 | // replace placeholders
|
---|
869 | $file = noNS($id);
|
---|
870 | $page = strtr($file, $conf['sepchar'], ' ');
|
---|
871 |
|
---|
872 | $tpl = str_replace(array(
|
---|
873 | '@ID@',
|
---|
874 | '@NS@',
|
---|
875 | '@FILE@',
|
---|
876 | '@!FILE@',
|
---|
877 | '@!FILE!@',
|
---|
878 | '@PAGE@',
|
---|
879 | '@!PAGE@',
|
---|
880 | '@!!PAGE@',
|
---|
881 | '@!PAGE!@',
|
---|
882 | '@USER@',
|
---|
883 | '@NAME@',
|
---|
884 | '@MAIL@',
|
---|
885 | '@DATE@',
|
---|
886 | ),
|
---|
887 | array(
|
---|
888 | $id,
|
---|
889 | getNS($id),
|
---|
890 | $file,
|
---|
891 | utf8_ucfirst($file),
|
---|
892 | utf8_strtoupper($file),
|
---|
893 | $page,
|
---|
894 | utf8_ucfirst($page),
|
---|
895 | utf8_ucwords($page),
|
---|
896 | utf8_strtoupper($page),
|
---|
897 | $_SERVER['REMOTE_USER'],
|
---|
898 | $USERINFO['name'],
|
---|
899 | $USERINFO['mail'],
|
---|
900 | $conf['dformat'],
|
---|
901 | ), $tpl);
|
---|
902 |
|
---|
903 | // we need the callback to work around strftime's char limit
|
---|
904 | $tpl = preg_replace_callback('/%./',create_function('$m','return strftime($m[0]);'),$tpl);
|
---|
905 | $data['tpl'] = $tpl;
|
---|
906 | return $tpl;
|
---|
907 | }
|
---|
908 |
|
---|
909 | /**
|
---|
910 | * Returns the raw Wiki Text in three slices.
|
---|
911 | *
|
---|
912 | * The range parameter needs to have the form "from-to"
|
---|
913 | * and gives the range of the section in bytes - no
|
---|
914 | * UTF-8 awareness is needed.
|
---|
915 | * The returned order is prefix, section and suffix.
|
---|
916 | *
|
---|
917 | * @author Andreas Gohr <[email protected]>
|
---|
918 | */
|
---|
919 | function rawWikiSlices($range,$id,$rev=''){
|
---|
920 | $text = io_readWikiPage(wikiFN($id, $rev), $id, $rev);
|
---|
921 |
|
---|
922 | // Parse range
|
---|
923 | list($from,$to) = explode('-',$range,2);
|
---|
924 | // Make range zero-based, use defaults if marker is missing
|
---|
925 | $from = !$from ? 0 : ($from - 1);
|
---|
926 | $to = !$to ? strlen($text) : ($to - 1);
|
---|
927 |
|
---|
928 | $slices[0] = substr($text, 0, $from);
|
---|
929 | $slices[1] = substr($text, $from, $to-$from);
|
---|
930 | $slices[2] = substr($text, $to);
|
---|
931 | return $slices;
|
---|
932 | }
|
---|
933 |
|
---|
934 | /**
|
---|
935 | * Joins wiki text slices
|
---|
936 | *
|
---|
937 | * function to join the text slices.
|
---|
938 | * When the pretty parameter is set to true it adds additional empty
|
---|
939 | * lines between sections if needed (used on saving).
|
---|
940 | *
|
---|
941 | * @author Andreas Gohr <[email protected]>
|
---|
942 | */
|
---|
943 | function con($pre,$text,$suf,$pretty=false){
|
---|
944 | if($pretty){
|
---|
945 | if ($pre !== '' && substr($pre, -1) !== "\n" &&
|
---|
946 | substr($text, 0, 1) !== "\n") {
|
---|
947 | $pre .= "\n";
|
---|
948 | }
|
---|
949 | if ($suf !== '' && substr($text, -1) !== "\n" &&
|
---|
950 | substr($suf, 0, 1) !== "\n") {
|
---|
951 | $text .= "\n";
|
---|
952 | }
|
---|
953 | }
|
---|
954 |
|
---|
955 | return $pre.$text.$suf;
|
---|
956 | }
|
---|
957 |
|
---|
958 | /**
|
---|
959 | * Saves a wikitext by calling io_writeWikiPage.
|
---|
960 | * Also directs changelog and attic updates.
|
---|
961 | *
|
---|
962 | * @author Andreas Gohr <[email protected]>
|
---|
963 | * @author Ben Coburn <[email protected]>
|
---|
964 | */
|
---|
965 | function saveWikiText($id,$text,$summary,$minor=false){
|
---|
966 | /* Note to developers:
|
---|
967 | This code is subtle and delicate. Test the behavior of
|
---|
968 | the attic and changelog with dokuwiki and external edits
|
---|
969 | after any changes. External edits change the wiki page
|
---|
970 | directly without using php or dokuwiki.
|
---|
971 | */
|
---|
972 | global $conf;
|
---|
973 | global $lang;
|
---|
974 | global $REV;
|
---|
975 | // ignore if no changes were made
|
---|
976 | // - [jmt12] unless the approved checkbox is set
|
---|
977 | if(!$_POST['approved'] && $text == rawWiki($id,'')){
|
---|
978 | return;
|
---|
979 | }
|
---|
980 |
|
---|
981 | $file = wikiFN($id);
|
---|
982 | $old = @filemtime($file); // from page
|
---|
983 | $wasRemoved = empty($text);
|
---|
984 | $wasCreated = !@file_exists($file);
|
---|
985 | $wasReverted = ($REV==true);
|
---|
986 | $newRev = false;
|
---|
987 | $oldRev = getRevisions($id, -1, 1, 1024); // from changelog
|
---|
988 | $oldRev = (int)(empty($oldRev)?0:$oldRev[0]);
|
---|
989 | if(!@file_exists(wikiFN($id, $old)) && @file_exists($file) && $old>=$oldRev) {
|
---|
990 | // add old revision to the attic if missing
|
---|
991 | saveOldRevision($id);
|
---|
992 | // add a changelog entry if this edit came from outside dokuwiki
|
---|
993 | if ($old>$oldRev) {
|
---|
994 | addLogEntry($old, $id, DOKU_CHANGE_TYPE_EDIT, $lang['external_edit'], '', array('ExternalEdit'=>true));
|
---|
995 | // remove soon to be stale instructions
|
---|
996 | $cache = new cache_instructions($id, $file);
|
---|
997 | $cache->removeCache();
|
---|
998 | }
|
---|
999 | }
|
---|
1000 |
|
---|
1001 | if ($wasRemoved){
|
---|
1002 | // Send "update" event with empty data, so plugins can react to page deletion
|
---|
1003 | $data = array(array($file, '', false), getNS($id), noNS($id), false);
|
---|
1004 | trigger_event('IO_WIKIPAGE_WRITE', $data);
|
---|
1005 | // pre-save deleted revision
|
---|
1006 | @touch($file);
|
---|
1007 | clearstatcache();
|
---|
1008 | $newRev = saveOldRevision($id);
|
---|
1009 | // remove empty file
|
---|
1010 | @unlink($file);
|
---|
1011 | // remove old meta info...
|
---|
1012 | $mfiles = metaFiles($id);
|
---|
1013 | $changelog = metaFN($id, '.changes');
|
---|
1014 | $metadata = metaFN($id, '.meta');
|
---|
1015 | $subscribers = metaFN($id, '.mlist');
|
---|
1016 | foreach ($mfiles as $mfile) {
|
---|
1017 | // but keep per-page changelog to preserve page history, keep subscriber list and keep meta data
|
---|
1018 | if (@file_exists($mfile) && $mfile!==$changelog && $mfile!==$metadata && $mfile!==$subscribers) { @unlink($mfile); }
|
---|
1019 | }
|
---|
1020 | // purge meta data
|
---|
1021 | p_purge_metadata($id);
|
---|
1022 | $del = true;
|
---|
1023 | // autoset summary on deletion
|
---|
1024 | if(empty($summary)) $summary = $lang['deleted'];
|
---|
1025 | // remove empty namespaces
|
---|
1026 | io_sweepNS($id, 'datadir');
|
---|
1027 | io_sweepNS($id, 'mediadir');
|
---|
1028 | }else{
|
---|
1029 | // save file (namespace dir is created in io_writeWikiPage)
|
---|
1030 | io_writeWikiPage($file, $text, $id);
|
---|
1031 | // pre-save the revision, to keep the attic in sync
|
---|
1032 | $newRev = saveOldRevision($id);
|
---|
1033 | $del = false;
|
---|
1034 | }
|
---|
1035 |
|
---|
1036 | // select changelog line type
|
---|
1037 | $extra = '';
|
---|
1038 | $type = DOKU_CHANGE_TYPE_EDIT;
|
---|
1039 | if ($wasReverted) {
|
---|
1040 | $type = DOKU_CHANGE_TYPE_REVERT;
|
---|
1041 | $extra = $REV;
|
---|
1042 | }
|
---|
1043 | else if ($wasCreated) { $type = DOKU_CHANGE_TYPE_CREATE; }
|
---|
1044 | else if ($wasRemoved) { $type = DOKU_CHANGE_TYPE_DELETE; }
|
---|
1045 | else if ($minor && $conf['useacl'] && $_SERVER['REMOTE_USER']) { $type = DOKU_CHANGE_TYPE_MINOR_EDIT; } //minor edits only for logged in users
|
---|
1046 |
|
---|
1047 | addLogEntry($newRev, $id, $type, $summary, $extra);
|
---|
1048 | // send notify mails
|
---|
1049 | notify($id,'admin',$old,$summary,$minor);
|
---|
1050 | notify($id,'subscribers',$old,$summary,$minor);
|
---|
1051 |
|
---|
1052 | // update the purgefile (timestamp of the last time anything within the wiki was changed)
|
---|
1053 | io_saveFile($conf['cachedir'].'/purgefile',time());
|
---|
1054 |
|
---|
1055 | // if useheading is enabled, purge the cache of all linking pages
|
---|
1056 | if(useHeading('content')){
|
---|
1057 | $pages = ft_backlinks($id);
|
---|
1058 | foreach ($pages as $page) {
|
---|
1059 | $cache = new cache_renderer($page, wikiFN($page), 'xhtml');
|
---|
1060 | $cache->removeCache();
|
---|
1061 | }
|
---|
1062 | }
|
---|
1063 | }
|
---|
1064 |
|
---|
1065 | /**
|
---|
1066 | * moves the current version to the attic and returns its
|
---|
1067 | * revision date
|
---|
1068 | *
|
---|
1069 | * @author Andreas Gohr <[email protected]>
|
---|
1070 | */
|
---|
1071 | function saveOldRevision($id){
|
---|
1072 | global $conf;
|
---|
1073 | $oldf = wikiFN($id);
|
---|
1074 | if(!@file_exists($oldf)) return '';
|
---|
1075 | $date = filemtime($oldf);
|
---|
1076 | $newf = wikiFN($id,$date);
|
---|
1077 | io_writeWikiPage($newf, rawWiki($id), $id, $date);
|
---|
1078 | return $date;
|
---|
1079 | }
|
---|
1080 |
|
---|
1081 | /**
|
---|
1082 | * Sends a notify mail on page change or registration
|
---|
1083 | *
|
---|
1084 | * @param string $id The changed page
|
---|
1085 | * @param string $who Who to notify (admin|subscribers|register)
|
---|
1086 | * @param int $rev Old page revision
|
---|
1087 | * @param string $summary What changed
|
---|
1088 | * @param boolean $minor Is this a minor edit?
|
---|
1089 | * @param array $replace Additional string substitutions, @KEY@ to be replaced by value
|
---|
1090 | *
|
---|
1091 | * @author Andreas Gohr <[email protected]>
|
---|
1092 | */
|
---|
1093 | function notify($id,$who,$rev='',$summary='',$minor=false,$replace=array()){
|
---|
1094 | global $lang;
|
---|
1095 | global $conf;
|
---|
1096 | global $INFO;
|
---|
1097 |
|
---|
1098 | // decide if there is something to do
|
---|
1099 | if($who == 'admin'){
|
---|
1100 | if(empty($conf['notify'])) return; //notify enabled?
|
---|
1101 | $text = rawLocale('mailtext');
|
---|
1102 | $to = $conf['notify'];
|
---|
1103 | $bcc = '';
|
---|
1104 | }elseif($who == 'subscribers'){
|
---|
1105 | if(!$conf['subscribers']) return; //subscribers enabled?
|
---|
1106 | if($conf['useacl'] && $_SERVER['REMOTE_USER'] && $minor) return; //skip minors
|
---|
1107 | $data = array('id' => $id, 'addresslist' => '', 'self' => false);
|
---|
1108 | trigger_event('COMMON_NOTIFY_ADDRESSLIST', $data,
|
---|
1109 | 'subscription_addresslist');
|
---|
1110 | $bcc = $data['addresslist'];
|
---|
1111 | if(empty($bcc)) return;
|
---|
1112 | $to = '';
|
---|
1113 | $text = rawLocale('subscr_single');
|
---|
1114 | }elseif($who == 'register'){
|
---|
1115 | if(empty($conf['registernotify'])) return;
|
---|
1116 | $text = rawLocale('registermail');
|
---|
1117 | $to = $conf['registernotify'];
|
---|
1118 | $bcc = '';
|
---|
1119 | }else{
|
---|
1120 | return; //just to be safe
|
---|
1121 | }
|
---|
1122 |
|
---|
1123 | $ip = clientIP();
|
---|
1124 | $text = str_replace('@DATE@',dformat(),$text);
|
---|
1125 | $text = str_replace('@BROWSER@',$_SERVER['HTTP_USER_AGENT'],$text);
|
---|
1126 | $text = str_replace('@IPADDRESS@',$ip,$text);
|
---|
1127 | $text = str_replace('@HOSTNAME@',gethostsbyaddrs($ip),$text);
|
---|
1128 | $text = str_replace('@NEWPAGE@',wl($id,'',true,'&'),$text);
|
---|
1129 | $text = str_replace('@PAGE@',$id,$text);
|
---|
1130 | $text = str_replace('@TITLE@',$conf['title'],$text);
|
---|
1131 | $text = str_replace('@DOKUWIKIURL@',DOKU_URL,$text);
|
---|
1132 | $text = str_replace('@SUMMARY@',$summary,$text);
|
---|
1133 | $text = str_replace('@USER@',$_SERVER['REMOTE_USER'],$text);
|
---|
1134 | $text = str_replace('@NAME@',$INFO['userinfo']['name'],$text);
|
---|
1135 | $text = str_replace('@MAIL@',$INFO['userinfo']['mail'],$text);
|
---|
1136 |
|
---|
1137 | foreach ($replace as $key => $substitution) {
|
---|
1138 | $text = str_replace('@'.strtoupper($key).'@',$substitution, $text);
|
---|
1139 | }
|
---|
1140 |
|
---|
1141 | if($who == 'register'){
|
---|
1142 | $subject = $lang['mail_new_user'].' '.$summary;
|
---|
1143 | }elseif($rev){
|
---|
1144 | $subject = $lang['mail_changed'].' '.$id;
|
---|
1145 | $text = str_replace('@OLDPAGE@',wl($id,"rev=$rev",true,'&'),$text);
|
---|
1146 | $df = new Diff(explode("\n",rawWiki($id,$rev)),
|
---|
1147 | explode("\n",rawWiki($id)));
|
---|
1148 | $dformat = new UnifiedDiffFormatter();
|
---|
1149 | $diff = $dformat->format($df);
|
---|
1150 | }else{
|
---|
1151 | $subject=$lang['mail_newpage'].' '.$id;
|
---|
1152 | $text = str_replace('@OLDPAGE@','none',$text);
|
---|
1153 | $diff = rawWiki($id);
|
---|
1154 | }
|
---|
1155 | $text = str_replace('@DIFF@',$diff,$text);
|
---|
1156 | if(empty($conf['mailprefix'])) {
|
---|
1157 | if(utf8_strlen($conf['title']) < 20) {
|
---|
1158 | $subject = '['.$conf['title'].'] '.$subject;
|
---|
1159 | }else{
|
---|
1160 | $subject = '['.utf8_substr($conf['title'], 0, 20).'...] '.$subject;
|
---|
1161 | }
|
---|
1162 | }else{
|
---|
1163 | $subject = '['.$conf['mailprefix'].'] '.$subject;
|
---|
1164 | }
|
---|
1165 | mail_send($to,$subject,$text,$conf['mailfrom'],'',$bcc);
|
---|
1166 | }
|
---|
1167 |
|
---|
1168 | /**
|
---|
1169 | * extracts the query from a search engine referrer
|
---|
1170 | *
|
---|
1171 | * @author Andreas Gohr <[email protected]>
|
---|
1172 | * @author Todd Augsburger <[email protected]>
|
---|
1173 | */
|
---|
1174 | function getGoogleQuery(){
|
---|
1175 | if (!isset($_SERVER['HTTP_REFERER'])) {
|
---|
1176 | return '';
|
---|
1177 | }
|
---|
1178 | $url = parse_url($_SERVER['HTTP_REFERER']);
|
---|
1179 |
|
---|
1180 | $query = array();
|
---|
1181 |
|
---|
1182 | // temporary workaround against PHP bug #49733
|
---|
1183 | // see http://bugs.php.net/bug.php?id=49733
|
---|
1184 | if(UTF8_MBSTRING) $enc = mb_internal_encoding();
|
---|
1185 | parse_str($url['query'],$query);
|
---|
1186 | if(UTF8_MBSTRING) mb_internal_encoding($enc);
|
---|
1187 |
|
---|
1188 | $q = '';
|
---|
1189 | if(isset($query['q']))
|
---|
1190 | $q = $query['q']; // google, live/msn, aol, ask, altavista, alltheweb, gigablast
|
---|
1191 | elseif(isset($query['p']))
|
---|
1192 | $q = $query['p']; // yahoo
|
---|
1193 | elseif(isset($query['query']))
|
---|
1194 | $q = $query['query']; // lycos, netscape, clusty, hotbot
|
---|
1195 | elseif(preg_match("#a9\.com#i",$url['host'])) // a9
|
---|
1196 | $q = urldecode(ltrim($url['path'],'/'));
|
---|
1197 |
|
---|
1198 | if($q === '') return '';
|
---|
1199 | $q = preg_split('/[\s\'"\\\\`()\]\[?:!\.{};,#+*<>\\/]+/',$q,-1,PREG_SPLIT_NO_EMPTY);
|
---|
1200 | return $q;
|
---|
1201 | }
|
---|
1202 |
|
---|
1203 | /**
|
---|
1204 | * Try to set correct locale
|
---|
1205 | *
|
---|
1206 | * @deprecated No longer used
|
---|
1207 | * @author Andreas Gohr <[email protected]>
|
---|
1208 | */
|
---|
1209 | function setCorrectLocale(){
|
---|
1210 | global $conf;
|
---|
1211 | global $lang;
|
---|
1212 |
|
---|
1213 | $enc = strtoupper($lang['encoding']);
|
---|
1214 | foreach ($lang['locales'] as $loc){
|
---|
1215 | //try locale
|
---|
1216 | if(@setlocale(LC_ALL,$loc)) return;
|
---|
1217 | //try loceale with encoding
|
---|
1218 | if(@setlocale(LC_ALL,"$loc.$enc")) return;
|
---|
1219 | }
|
---|
1220 | //still here? try to set from environment
|
---|
1221 | @setlocale(LC_ALL,"");
|
---|
1222 | }
|
---|
1223 |
|
---|
1224 | /**
|
---|
1225 | * Return the human readable size of a file
|
---|
1226 | *
|
---|
1227 | * @param int $size A file size
|
---|
1228 | * @param int $dec A number of decimal places
|
---|
1229 | * @author Martin Benjamin <[email protected]>
|
---|
1230 | * @author Aidan Lister <[email protected]>
|
---|
1231 | * @version 1.0.0
|
---|
1232 | */
|
---|
1233 | function filesize_h($size, $dec = 1){
|
---|
1234 | $sizes = array('B', 'KB', 'MB', 'GB');
|
---|
1235 | $count = count($sizes);
|
---|
1236 | $i = 0;
|
---|
1237 |
|
---|
1238 | while ($size >= 1024 && ($i < $count - 1)) {
|
---|
1239 | $size /= 1024;
|
---|
1240 | $i++;
|
---|
1241 | }
|
---|
1242 |
|
---|
1243 | return round($size, $dec) . ' ' . $sizes[$i];
|
---|
1244 | }
|
---|
1245 |
|
---|
1246 | /**
|
---|
1247 | * Return the given timestamp as human readable, fuzzy age
|
---|
1248 | *
|
---|
1249 | * @author Andreas Gohr <[email protected]>
|
---|
1250 | */
|
---|
1251 | function datetime_h($dt){
|
---|
1252 | global $lang;
|
---|
1253 |
|
---|
1254 | $ago = time() - $dt;
|
---|
1255 | if($ago > 24*60*60*30*12*2){
|
---|
1256 | return sprintf($lang['years'], round($ago/(24*60*60*30*12)));
|
---|
1257 | }
|
---|
1258 | if($ago > 24*60*60*30*2){
|
---|
1259 | return sprintf($lang['months'], round($ago/(24*60*60*30)));
|
---|
1260 | }
|
---|
1261 | if($ago > 24*60*60*7*2){
|
---|
1262 | return sprintf($lang['weeks'], round($ago/(24*60*60*7)));
|
---|
1263 | }
|
---|
1264 | if($ago > 24*60*60*2){
|
---|
1265 | return sprintf($lang['days'], round($ago/(24*60*60)));
|
---|
1266 | }
|
---|
1267 | if($ago > 60*60*2){
|
---|
1268 | return sprintf($lang['hours'], round($ago/(60*60)));
|
---|
1269 | }
|
---|
1270 | if($ago > 60*2){
|
---|
1271 | return sprintf($lang['minutes'], round($ago/(60)));
|
---|
1272 | }
|
---|
1273 | return sprintf($lang['seconds'], $ago);
|
---|
1274 | }
|
---|
1275 |
|
---|
1276 | /**
|
---|
1277 | * Wraps around strftime but provides support for fuzzy dates
|
---|
1278 | *
|
---|
1279 | * The format default to $conf['dformat']. It is passed to
|
---|
1280 | * strftime - %f can be used to get the value from datetime_h()
|
---|
1281 | *
|
---|
1282 | * @see datetime_h
|
---|
1283 | * @author Andreas Gohr <[email protected]>
|
---|
1284 | */
|
---|
1285 | function dformat($dt=null,$format=''){
|
---|
1286 | global $conf;
|
---|
1287 |
|
---|
1288 | if(is_null($dt)) $dt = time();
|
---|
1289 | $dt = (int) $dt;
|
---|
1290 | if(!$format) $format = $conf['dformat'];
|
---|
1291 |
|
---|
1292 | $format = str_replace('%f',datetime_h($dt),$format);
|
---|
1293 | return strftime($format,$dt);
|
---|
1294 | }
|
---|
1295 |
|
---|
1296 | /**
|
---|
1297 | * Formats a timestamp as ISO 8601 date
|
---|
1298 | *
|
---|
1299 | * @author <ungu at terong dot com>
|
---|
1300 | * @link http://www.php.net/manual/en/function.date.php#54072
|
---|
1301 | */
|
---|
1302 | function date_iso8601($int_date) {
|
---|
1303 | //$int_date: current date in UNIX timestamp
|
---|
1304 | $date_mod = date('Y-m-d\TH:i:s', $int_date);
|
---|
1305 | $pre_timezone = date('O', $int_date);
|
---|
1306 | $time_zone = substr($pre_timezone, 0, 3).":".substr($pre_timezone, 3, 2);
|
---|
1307 | $date_mod .= $time_zone;
|
---|
1308 | return $date_mod;
|
---|
1309 | }
|
---|
1310 |
|
---|
1311 | /**
|
---|
1312 | * return an obfuscated email address in line with $conf['mailguard'] setting
|
---|
1313 | *
|
---|
1314 | * @author Harry Fuecks <[email protected]>
|
---|
1315 | * @author Christopher Smith <[email protected]>
|
---|
1316 | */
|
---|
1317 | function obfuscate($email) {
|
---|
1318 | global $conf;
|
---|
1319 |
|
---|
1320 | switch ($conf['mailguard']) {
|
---|
1321 | case 'visible' :
|
---|
1322 | $obfuscate = array('@' => ' [at] ', '.' => ' [dot] ', '-' => ' [dash] ');
|
---|
1323 | return strtr($email, $obfuscate);
|
---|
1324 |
|
---|
1325 | case 'hex' :
|
---|
1326 | $encode = '';
|
---|
1327 | $len = strlen($email);
|
---|
1328 | for ($x=0; $x < $len; $x++){
|
---|
1329 | $encode .= '&#x' . bin2hex($email{$x}).';';
|
---|
1330 | }
|
---|
1331 | return $encode;
|
---|
1332 |
|
---|
1333 | case 'none' :
|
---|
1334 | default :
|
---|
1335 | return $email;
|
---|
1336 | }
|
---|
1337 | }
|
---|
1338 |
|
---|
1339 | /**
|
---|
1340 | * Removes quoting backslashes
|
---|
1341 | *
|
---|
1342 | * @author Andreas Gohr <[email protected]>
|
---|
1343 | */
|
---|
1344 | function unslash($string,$char="'"){
|
---|
1345 | return str_replace('\\'.$char,$char,$string);
|
---|
1346 | }
|
---|
1347 |
|
---|
1348 | /**
|
---|
1349 | * Convert php.ini shorthands to byte
|
---|
1350 | *
|
---|
1351 | * @author <gilthans dot NO dot SPAM at gmail dot com>
|
---|
1352 | * @link http://de3.php.net/manual/en/ini.core.php#79564
|
---|
1353 | */
|
---|
1354 | function php_to_byte($v){
|
---|
1355 | $l = substr($v, -1);
|
---|
1356 | $ret = substr($v, 0, -1);
|
---|
1357 | switch(strtoupper($l)){
|
---|
1358 | case 'P':
|
---|
1359 | $ret *= 1024;
|
---|
1360 | case 'T':
|
---|
1361 | $ret *= 1024;
|
---|
1362 | case 'G':
|
---|
1363 | $ret *= 1024;
|
---|
1364 | case 'M':
|
---|
1365 | $ret *= 1024;
|
---|
1366 | case 'K':
|
---|
1367 | $ret *= 1024;
|
---|
1368 | break;
|
---|
1369 | default;
|
---|
1370 | $ret *= 10;
|
---|
1371 | break;
|
---|
1372 | }
|
---|
1373 | return $ret;
|
---|
1374 | }
|
---|
1375 |
|
---|
1376 | /**
|
---|
1377 | * Wrapper around preg_quote adding the default delimiter
|
---|
1378 | */
|
---|
1379 | function preg_quote_cb($string){
|
---|
1380 | return preg_quote($string,'/');
|
---|
1381 | }
|
---|
1382 |
|
---|
1383 | /**
|
---|
1384 | * Shorten a given string by removing data from the middle
|
---|
1385 | *
|
---|
1386 | * You can give the string in two parts, the first part $keep
|
---|
1387 | * will never be shortened. The second part $short will be cut
|
---|
1388 | * in the middle to shorten but only if at least $min chars are
|
---|
1389 | * left to display it. Otherwise it will be left off.
|
---|
1390 | *
|
---|
1391 | * @param string $keep the part to keep
|
---|
1392 | * @param string $short the part to shorten
|
---|
1393 | * @param int $max maximum chars you want for the whole string
|
---|
1394 | * @param int $min minimum number of chars to have left for middle shortening
|
---|
1395 | * @param string $char the shortening character to use
|
---|
1396 | */
|
---|
1397 | function shorten($keep,$short,$max,$min=9,$char='âŠ'){
|
---|
1398 | $max = $max - utf8_strlen($keep);
|
---|
1399 | if($max < $min) return $keep;
|
---|
1400 | $len = utf8_strlen($short);
|
---|
1401 | if($len <= $max) return $keep.$short;
|
---|
1402 | $half = floor($max/2);
|
---|
1403 | return $keep.utf8_substr($short,0,$half-1).$char.utf8_substr($short,$len-$half);
|
---|
1404 | }
|
---|
1405 |
|
---|
1406 | /**
|
---|
1407 | * Return the users realname or e-mail address for use
|
---|
1408 | * in page footer and recent changes pages
|
---|
1409 | *
|
---|
1410 | * @author Andy Webber <dokuwiki AT andywebber DOT com>
|
---|
1411 | */
|
---|
1412 | function editorinfo($username){
|
---|
1413 | global $conf;
|
---|
1414 | global $auth;
|
---|
1415 |
|
---|
1416 | switch($conf['showuseras']){
|
---|
1417 | case 'username':
|
---|
1418 | case 'email':
|
---|
1419 | case 'email_link':
|
---|
1420 | if($auth) $info = $auth->getUserData($username);
|
---|
1421 | break;
|
---|
1422 | default:
|
---|
1423 | return hsc($username);
|
---|
1424 | }
|
---|
1425 |
|
---|
1426 | if(isset($info) && $info) {
|
---|
1427 | switch($conf['showuseras']){
|
---|
1428 | case 'username':
|
---|
1429 | return hsc($info['name']);
|
---|
1430 | case 'email':
|
---|
1431 | return obfuscate($info['mail']);
|
---|
1432 | case 'email_link':
|
---|
1433 | $mail=obfuscate($info['mail']);
|
---|
1434 | return '<a href="mailto:'.$mail.'">'.$mail.'</a>';
|
---|
1435 | default:
|
---|
1436 | return hsc($username);
|
---|
1437 | }
|
---|
1438 | } else {
|
---|
1439 | return hsc($username);
|
---|
1440 | }
|
---|
1441 | }
|
---|
1442 |
|
---|
1443 | /**
|
---|
1444 | * Returns the path to a image file for the currently chosen license.
|
---|
1445 | * When no image exists, returns an empty string
|
---|
1446 | *
|
---|
1447 | * @author Andreas Gohr <[email protected]>
|
---|
1448 | * @param string $type - type of image 'badge' or 'button'
|
---|
1449 | */
|
---|
1450 | function license_img($type){
|
---|
1451 | global $license;
|
---|
1452 | global $conf;
|
---|
1453 | if(!$conf['license']) return '';
|
---|
1454 | if(!is_array($license[$conf['license']])) return '';
|
---|
1455 | $lic = $license[$conf['license']];
|
---|
1456 | $try = array();
|
---|
1457 | $try[] = 'lib/images/license/'.$type.'/'.$conf['license'].'.png';
|
---|
1458 | $try[] = 'lib/images/license/'.$type.'/'.$conf['license'].'.gif';
|
---|
1459 | if(substr($conf['license'],0,3) == 'cc-'){
|
---|
1460 | $try[] = 'lib/images/license/'.$type.'/cc.png';
|
---|
1461 | }
|
---|
1462 | foreach($try as $src){
|
---|
1463 | if(@file_exists(DOKU_INC.$src)) return $src;
|
---|
1464 | }
|
---|
1465 | return '';
|
---|
1466 | }
|
---|
1467 |
|
---|
1468 | /**
|
---|
1469 | * Checks if the given amount of memory is available
|
---|
1470 | *
|
---|
1471 | * If the memory_get_usage() function is not available the
|
---|
1472 | * function just assumes $bytes of already allocated memory
|
---|
1473 | *
|
---|
1474 | * @param int $mem Size of memory you want to allocate in bytes
|
---|
1475 | * @param int $used already allocated memory (see above)
|
---|
1476 | * @author Filip Oscadal <[email protected]>
|
---|
1477 | * @author Andreas Gohr <[email protected]>
|
---|
1478 | */
|
---|
1479 | function is_mem_available($mem,$bytes=1048576){
|
---|
1480 | $limit = trim(ini_get('memory_limit'));
|
---|
1481 | if(empty($limit)) return true; // no limit set!
|
---|
1482 |
|
---|
1483 | // parse limit to bytes
|
---|
1484 | $limit = php_to_byte($limit);
|
---|
1485 |
|
---|
1486 | // get used memory if possible
|
---|
1487 | if(function_exists('memory_get_usage')){
|
---|
1488 | $used = memory_get_usage();
|
---|
1489 | }else{
|
---|
1490 | $used = $bytes;
|
---|
1491 | }
|
---|
1492 |
|
---|
1493 | if($used+$mem > $limit){
|
---|
1494 | return false;
|
---|
1495 | }
|
---|
1496 |
|
---|
1497 | return true;
|
---|
1498 | }
|
---|
1499 |
|
---|
1500 | /**
|
---|
1501 | * Send a HTTP redirect to the browser
|
---|
1502 | *
|
---|
1503 | * Works arround Microsoft IIS cookie sending bug. Exits the script.
|
---|
1504 | *
|
---|
1505 | * @link http://support.microsoft.com/kb/q176113/
|
---|
1506 | * @author Andreas Gohr <[email protected]>
|
---|
1507 | */
|
---|
1508 | function send_redirect($url){
|
---|
1509 | //are there any undisplayed messages? keep them in session for display
|
---|
1510 | global $MSG;
|
---|
1511 | if (isset($MSG) && count($MSG) && !defined('NOSESSION')){
|
---|
1512 | //reopen session, store data and close session again
|
---|
1513 | @session_start();
|
---|
1514 | $_SESSION[DOKU_COOKIE]['msg'] = $MSG;
|
---|
1515 | }
|
---|
1516 |
|
---|
1517 | // always close the session
|
---|
1518 | session_write_close();
|
---|
1519 |
|
---|
1520 | // work around IE bug
|
---|
1521 | // http://www.ianhoar.com/2008/11/16/internet-explorer-6-and-redirected-anchor-links/
|
---|
1522 | list($url,$hash) = explode('#',$url);
|
---|
1523 | if($hash){
|
---|
1524 | if(strpos($url,'?')){
|
---|
1525 | $url = $url.'&#'.$hash;
|
---|
1526 | }else{
|
---|
1527 | $url = $url.'?&#'.$hash;
|
---|
1528 | }
|
---|
1529 | }
|
---|
1530 |
|
---|
1531 | // check if running on IIS < 6 with CGI-PHP
|
---|
1532 | if( isset($_SERVER['SERVER_SOFTWARE']) && isset($_SERVER['GATEWAY_INTERFACE']) &&
|
---|
1533 | (strpos($_SERVER['GATEWAY_INTERFACE'],'CGI') !== false) &&
|
---|
1534 | (preg_match('|^Microsoft-IIS/(\d)\.\d$|', trim($_SERVER['SERVER_SOFTWARE']), $matches)) &&
|
---|
1535 | $matches[1] < 6 ){
|
---|
1536 | header('Refresh: 0;url='.$url);
|
---|
1537 | }else{
|
---|
1538 | header('Location: '.$url);
|
---|
1539 | }
|
---|
1540 | exit;
|
---|
1541 | }
|
---|
1542 |
|
---|
1543 | /**
|
---|
1544 | * Validate a value using a set of valid values
|
---|
1545 | *
|
---|
1546 | * This function checks whether a specified value is set and in the array
|
---|
1547 | * $valid_values. If not, the function returns a default value or, if no
|
---|
1548 | * default is specified, throws an exception.
|
---|
1549 | *
|
---|
1550 | * @param string $param The name of the parameter
|
---|
1551 | * @param array $valid_values A set of valid values; Optionally a default may
|
---|
1552 | * be marked by the key âdefaultâ.
|
---|
1553 | * @param array $array The array containing the value (typically $_POST
|
---|
1554 | * or $_GET)
|
---|
1555 | * @param string $exc The text of the raised exception
|
---|
1556 | *
|
---|
1557 | * @author Adrian Lang <[email protected]>
|
---|
1558 | */
|
---|
1559 | function valid_input_set($param, $valid_values, $array, $exc = '') {
|
---|
1560 | if (isset($array[$param]) && in_array($array[$param], $valid_values)) {
|
---|
1561 | return $array[$param];
|
---|
1562 | } elseif (isset($valid_values['default'])) {
|
---|
1563 | return $valid_values['default'];
|
---|
1564 | } else {
|
---|
1565 | throw new Exception($exc);
|
---|
1566 | }
|
---|
1567 | }
|
---|
1568 |
|
---|
1569 | //Setup VIM: ex: et ts=2 :
|
---|