source: documentation/trunk/packages/dokuwiki-2011-05-25a/inc/common.php@ 25027

Last change on this file since 25027 was 25027, checked in by jmt12, 12 years ago

Adding the packages directory, and within it a configured version of dokuwiki all ready to run

File size: 45.8 KB
Line 
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
9if(!defined('DOKU_INC')) die('meh.');
10
11/**
12 * These constants are used with the recents function
13 */
14define('RECENTS_SKIP_DELETED',2);
15define('RECENTS_SKIP_MINORS',4);
16define('RECENTS_SKIP_SUBSPACES',8);
17define('RECENTS_MEDIA_CHANGES',16);
18
19/**
20 * Wrapper around htmlspecialchars()
21 *
22 * @author Andreas Gohr <[email protected]>
23 * @see htmlspecialchars()
24 */
25function 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 */
36function 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 */
45function 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 */
57function getSecurityToken(){
58 return md5(auth_cookiesalt().session_id());
59}
60
61/**
62 * Check the secret CSRF token
63 */
64function 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 */
80function 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 */
95function 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 */
223function buildURLparams($params, $sep='&amp;'){
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 */
243function 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 */
265function 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 */
320function 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 */
344function wl($id='',$more='',$abs=false,$sep='&amp;'){
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 */
383function exportlink($id='',$format='raw',$more='',$abs=false,$sep='&amp;'){
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 */
427function ml($id='',$more='',$direct=true,$sep='&amp;',$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 */
503function 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 */
531function 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 */
595function 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 */
659function 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 */
681function 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 */
702function 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 */
729function 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 */
750function 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 */
768function 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 */
781function 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 */
791function rawLocale($id){
792 return io_readFile(localeFN($id));
793}
794
795/**
796 * Returns the raw WikiText
797 *
798 * @author Andreas Gohr <[email protected]>
799 */
800function 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 */
810function 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 */
862function 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 */
919function 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 */
943function 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 */
965function 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 */
1071function 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 */
1093function 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 */
1174function 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 */
1209function 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 */
1233function 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 */
1251function 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 */
1285function 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 */
1302function 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 */
1317function 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 */
1344function 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 */
1354function 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 */
1379function 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 */
1397function 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 */
1412function 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 */
1450function 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 */
1479function 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 */
1508function 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 */
1559function 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 :
Note: See TracBrowser for help on using the repository browser.