8000 dokuwiki/inc/common.php at hotfix · alexlehm/dokuwiki · GitHub
[go: up one dir, main page]

Skip to content

Latest commit

 

History

History
1604 lines (1451 loc) · 47.4 KB

common.php

File metadata and controls

1604 lines (1451 loc) · 47.4 KB
<?php
/**
* Common DokuWiki functions
*
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
* @author Andreas Gohr <andi@splitbrain.org>
*/
if(!defined('DOKU_INC')) die('meh.');
/**
* These constants are used with the recents function
*/
define('RECENTS_SKIP_DELETED', 2);
define('RECENTS_SKIP_MINORS', 4);
define('RECENTS_SKIP_SUBSPACES', 8);
define('RECENTS_MEDIA_CHANGES', 16);
define('RECENTS_MEDIA_PAGES_MIXED', 32);
/**
* Wrapper around htmlspecialchars()
*
* @author Andreas Gohr <andi@splitbrain.org>
* @see htmlspecialchars()
*/
function hsc($string) {
return htmlspecialchars($string, ENT_QUOTES, 'UTF-8');
}
/**
* print a newline terminated string
*
* You can give an indention as optional parameter
*
* @author Andreas Gohr <andi@splitbrain.org>
*/
function ptln($string, $indent = 0) {
echo str_repeat(' ', $indent)."$string\n";
}
/**
* strips control characters (<32) from the given string
*
* @author Andreas Gohr <andi@splitbrain.org>
*/
function stripctl($string) {
return preg_replace('/[\x00-\x1F]+/s', '', $string);
}
/**
* Return a secret token to be used for CSRF attack prevention
*
* @author Andreas Gohr <andi@splitbrain.org>
* @link http://en.wikipedia.org/wiki/Cross-site_request_forgery
* @link http://christ1an.blogspot.com/2007/04/preventing-csrf-efficiently.html
* @return string
*/
function getSecurityToken() {
return md5(auth_cookiesalt().session_id().$_SERVER['REMOTE_USER']);
}
/**
* Check the secret CSRF token
*/
function checkSecurityToken($token = null) {
global $INPUT;
if(!$_SERVER['REMOTE_USER']) return true; // no logged in user, no need for a check
if(is_null($token)) $token = $INPUT->str('sectok');
if(getSecurityToken() != $token) {
msg('Security Token did not match. Possible CSRF attack.', -1);
return false;
}
return true;
}
/**
* Print a hidden form field with a secret CSRF token
*
* @author Andreas Gohr <andi@splitbrain.org>
*/
function formSecurityToken($print = true) {
$ret = '<div class="no"><input type="hidden" name="sectok" value="'.getSecurityToken().'" /></div>'."\n";
if($print) echo $ret;
return $ret;
}
/**
* Return info about the current document as associative
* array.
*
* @author Andreas Gohr <andi@splitbrain.org>
*/
function pageinfo() {
global $ID;
global $REV;
global $RANGE;
global $USERINFO;
global $lang;
// include ID & REV not redundant, as some parts of DokuWiki may temporarily change $ID, e.g. p_wiki_xhtml
// FIXME ... perhaps it would be better to ensure the temporary changes weren't necessary
$info['id'] = $ID;
$info['rev'] = $REV;
// set info about manager/admin status.
$info['isadmin'] = false;
$info['ismanager'] = false;
if(isset($_SERVER['REMOTE_USER'])) {
$sub = new Subscription();
$info['userinfo'] = $USERINFO;
$info['perm'] = auth_quickaclcheck($ID);
$info['subscribed'] = $sub->user_subscription();
$info['client'] = $_SERVER['REMOTE_USER'];
if($info['perm'] == AUTH_ADMIN) {
$info['isadmin'] = true;
$info['ismanager'] = true;
} elseif(auth_ismanager()) {
$info['ismanager'] = true;
}
// if some outside auth were used only REMOTE_USER is set
if(!$info['userinfo']['name']) {
$info['userinfo']['name'] = $_SERVER['REMOTE_USER'];
}
} else {
$info['perm'] = auth_aclcheck($ID, '', null);
$info['subscribed'] = false;
$info['client'] = clientIP(true);
}
$info['namespace'] = getNS($ID);
$info['locked'] = checklock($ID);
$info['filepath'] = fullpath(wikiFN($ID));
$info['exists'] = @file_exists($info['filepath']);
if($REV) {
//check if current revision was meant
if($info['exists'] && (@filemtime($info['filepath']) == $REV)) {
$REV = '';
} elseif($RANGE) {
//section editing does not work with old revisions!
$REV = '';
$RANGE = '';
msg($lang['nosecedit'], 0);
} else {
//really use old revision
$info['filepath'] = fullpath(wikiFN($ID, $REV));
$info['exists'] = @file_exists($info['filepath']);
}
}
$info['rev'] = $REV;
if($info['exists']) {
$info['writable'] = (is_writable($info['filepath']) &&
($info['perm'] >= AUTH_EDIT));
} else {
$info['writable'] = ($info['perm'] >= AUTH_CREATE);
}
$info['editable'] = ($info['writable'] && empty($info['locked']));
$info['lastmod'] = @filemtime($info['filepath']);
//load page meta data
$info['meta'] = p_get_metadata($ID);
//who's the editor
if($REV) {
$revinfo = getRevisionInfo($ID, $REV, 1024);
} else {
if(is_array($info['meta']['last_change'])) {
$revinfo = $info['meta']['last_change'];
} else {
$revinfo = getRevisionInfo($ID, $info['lastmod'], 1024);
// cache most recent changelog line in metadata if missing and still valid
if($revinfo !== false) {
$info['meta']['last_change'] = $revinfo;
p_set_metadata($ID, array('last_change' => $revinfo));
}
}
}
//and check for an external edit
if($revinfo !== false && $revinfo['date'] != $info['lastmod']) {
// cached changelog line no longer valid
$revinfo = false;
$info['meta']['last_change'] = $revinfo;
p_set_metadata($ID, array('last_change' => $revinfo));
}
$info['ip'] = $revinfo['ip'];
$info['user'] = $revinfo['user'];
$info['sum'] = $revinfo['sum'];
// See also $INFO['meta']['last_change'] which is the most recent log line for page $ID.
// Use $INFO['meta']['last_change']['type']===DOKU_CHANGE_TYPE_MINOR_EDIT in place of $info['minor'].
if($revinfo['user']) {
$info['editor'] = $revinfo['user'];
} else {
$info['editor'] = $revinfo['ip'];
}
// draft
$draft = getCacheName($info['client'].$ID, '.draft');
if(@file_exists($draft)) {
if(@filemtime($draft) < @filemtime(wikiFN($ID))) {
// remove stale draft
@unlink($draft);
} else {
$info['draft'] = $draft;
}
}
// mobile detection
$info['ismobile'] = clientismobile();
return $info;
}
/**
* Build an string of URL parameters
*
* @author Andreas Gohr
*/
function buildURLparams($params, $sep = '&amp;') {
$url = '';
$amp = false;
foreach($params as $key => $val) {
if($amp) $url .= $sep;
$url .= rawurlencode($key).'=';
$url .= rawurlencode((string) $val);
$amp = true;
}
return $url;
}
/**
* Build an string of html tag attributes
*
* Skips keys starting with '_', values get HTML encoded
*
* @author Andreas Gohr
*/
function buildAttributes($params, $skipempty = false) {
$url = '';
$white = false;
foreach($params as $key => $val) {
if($key{0} == '_') continue;
if($val === '' && $skipempty) continue;
if($white) $url .= ' ';
$url .= $key.'="';
$url .= htmlspecialchars($val);
$url .= '"';
$white = true;
}
return $url;
}
/**
* This builds the breadcrumb trail and returns it as array
*
* @author Andreas Gohr <andi@splitbrain.org>
*/
function breadcrumbs() {
// we prepare the breadcrumbs early for quick session closing
static $crumbs = null;
if($crumbs != null) return $crumbs;
global $ID;
global $ACT;
global $conf;
//first visit?
$crumbs = isset($_SESSION[DOKU_COOKIE]['bc']) ? $_SESSION[DOKU_COOKIE]['bc'] : array();
//we only save on show and existing wiki documents
$file = wikiFN($ID);
if($ACT != 'show' || !@file_exists($file)) {
$_SESSION[DOKU_COOKIE]['bc'] = $crumbs;
return $crumbs;
}
// page names
$name = noNSorNS($ID);
if(useHeading('navigation')) {
// get page title
$title = p_get_first_heading($ID, METADATA_RENDER_USING_SIMPLE_CACHE);
if($title) {
$name = $title;
}
}
//remove ID from array
if(isset($crumbs[$ID])) {
unset($crumbs[$ID]);
}
//add to array
$crumbs[$ID] = $name;
//reduce size
while(count($crumbs) > $conf['breadcrumbs']) {
array_shift($crumbs);
}
//save to session
$_SESSION[DOKU_COOKIE]['bc'] = $crumbs;
r 10BC8 eturn $crumbs;
}
/**
* Filter for page IDs
*
* This is run on a ID before it is outputted somewhere
* currently used to replace the colon with something else
* on Windows (non-IIS) systems and to have proper URL encoding
*
* See discussions at https://github.com/splitbrain/dokuwiki/pull/84 and
* https://github.com/splitbrain/dokuwiki/pull/173 why we use a whitelist of
* unaffected servers instead of blacklisting affected servers here.
*
* Urlencoding is ommitted when the second parameter is false
*
* @author Andreas Gohr <andi@splitbrain.org>
*/
function idfilter($id, $ue = true) {
global $conf;
if($conf['useslash'] && $conf['userewrite']) {
$id = strtr($id, ':', '/');
} elseif(strtoupper(substr(PHP_OS, 0, 3)) === 'WIN' &&
$conf['userewrite'] &&
strpos($_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS') === false
) {
$id = strtr($id, ':', ';');
}
if($ue) {
$id = rawurlencode($id);
$id = str_replace('%3A', ':', $id); //keep as colon
$id = str_replace('%2F', '/', $id); //keep as slash
}
return $id;
}
/**
* This builds a link to a wikipage
*
* It handles URL rewriting and adds additional parameter if
* given in $more
*
* @author Andreas Gohr <andi@splitbrain.org>
*/
function wl($id = '', $urlParameters = '', $absolute = false, $separator = '&amp;') {
global $conf;
if(is_array($urlParameters)) {
$urlParameters = buildURLparams($urlParameters, $separator);
} else {
$urlParameters = str_replace(',', $separator, $urlParameters);
}
if($id === '') {
$id = $conf['start'];
}
$id = idfilter($id);
if($absolute) {
$xlink = DOKU_URL;
} else {
$xlink = DOKU_BASE;
}
if($conf['userewrite'] == 2) {
$xlink .= DOKU_SCRIPT.'/'.$id;
if($urlParameters) $xlink .= '?'.$urlParameters;
} elseif($conf['userewrite']) {
$xlink .= $id;
if($urlParameters) $xlink .= '?'.$urlParameters;
} elseif($id) {
$xlink .= DOKU_SCRIPT.'?id='.$id;
if($urlParameters) $xlink .= $separator.$urlParameters;
} else {
$xlink .= DOKU_SCRIPT;
if($urlParameters) $xlink .= '?'.$urlParameters;
}
return $xlink;
}
/**
* This builds a link to an alternate page format
*
* Handles URL rewriting if enabled. Follows the style of wl().
*
* @author Ben Coburn <btcoburn@silicodon.net>
*/
function exportlink($id = '', $format = 'raw', $more = '', $abs = false, $sep = '&amp;') {
global $conf;
if(is_array($more)) {
$more = buildURLparams($more, $sep);
} else {
$more = str_replace(',', $sep, $more);
}
$format = rawurlencode($format);
$id = idfilter($id);
if($abs) {
$xlink = DOKU_URL;
} else {
$xlink = DOKU_BASE;
}
if($conf['userewrite'] == 2) {
$xlink .= DOKU_SCRIPT.'/'.$id.'?do=export_'.$format;
if($more) $xlink .= $sep.$more;
} elseif($conf['userewrite'] == 1) {
$xlink .= '_export/'.$format.'/'.$id;
if($more) $xlink .= '?'.$more;
} else {
$xlink .= DOKU_SCRIPT.'?do=export_'.$format.$sep.'id='.$id;
if($more) $xlink .= $sep.$more;
}
return $xlink;
}
/**
* Build a link to a media file
*
* Will return a link to the detail page if $direct is false
*
* The $more parameter should always be given as array, the function then
* will strip default parameters to produce even cleaner URLs
*
* @param string $id the media file id or URL
* @param mixed $more string or array with additional parameters
* @param bool $direct link to detail page if false
* @param string $sep URL parameter separator
* @param bool $abs Create an absolute URL
* @return string
*/
function ml($id = '', $more = '', $direct = true, $sep = '&amp;', $abs = false) {
global $conf;
$isexternalimage = preg_match('#^(https?|ftp)://#i', $id);
if(!$isexternalimage) {
$id = cleanID($id);
}
if(is_array($more)) {
// add token for resized images
if($more['w'] || $more['h']){
$more['tok'] = media_get_token($id,$more['w'],$more['h']);
}
// strip defaults for shorter URLs
if(isset($more['cache']) && $more['cache'] == 'cache') unset($more['cache']);
if(!$more['w']) unset($more['w']);
if(!$more['h']) unset($more['h']);
if(isset($more['id']) && $direct) unset($more['id']);
$more = buildURLparams($more, $sep);
} else {
$matches = array();
if (preg_match_all('/\b(w|h)=(\d*)\b/',$more,$matches,PREG_SET_ORDER)){
$resize = array('w'=>0, 'h'=>0);
foreach ($matches as $match){
$resize[$match[1]] = $match[2];
}
$more .= $sep.'tok='.media_get_token($id,$resize['w'],$resize['h']);
}
$more = str_replace('cache=cache', '', $more); //skip default
$more = str_replace(',,', ',', $more);
$more = str_replace(',', $sep, $more);
}
if($abs) {
$xlink = DOKU_URL;
} else {
$xlink = DOKU_BASE;
}
// external URLs are always direct without rewriting
if($isexternalimage) {
$xlink .= 'lib/exe/fetch.php';
// add hash:
$xlink .= '?hash='.substr(md5(auth_cookiesalt().$id), 0, 6);
if($more) {
$xlink .= $sep.$more;
$xlink .= $sep.'media='.rawurlencode($id);
} else {
$xlink .= $sep.'media='.rawurlencode($id);
}
return $xlink;
}
$id = idfilter($id);
// decide on scriptname
if($direct) {
if($conf['userewrite'] == 1) {
$script = '_media';
} else {
$script = 'lib/exe/fetch.php';
}
} else {
if($conf['userewrite'] == 1) {
$script = '_detail';
} else {
$script = 'lib/exe/detail.php';
}
}
// build URL based on rewrite mode
if($conf['userewrite']) {
$xlink .= $script.'/'.$id;
if($more) $xlink .= '?'.$more;
} else {
if($more) {
$xlink .= $script.'?'.$more;
$xlink .= $sep.'media='.$id;
} else {
$xlink .= $script.'?media='.$id;
}
}
return $xlink;
}
/**
* Returns the URL to the DokuWiki base script
*
* Consider using wl() instead, unless you absoutely need the doku.php endpoint
*
* @author Andreas Gohr <andi@splitbrain.org>
*/
function script() {
return DOKU_BASE.DOKU_SCRIPT;
}
/**
* Spamcheck against wordlist
*
* Checks the wikitext against a list of blocked expressions
* returns true if the text contains any bad words
*
* Triggers COMMON_WORDBLOCK_BLOCKED
*
* Action Plugins can use this event to inspect the blocked data
* and gain information about the user who was blocked.
*
* Event data:
* data['matches'] - array of matches
* data['userinfo'] - information about the blocked user
* [ip] - ip address
* [user] - username (if logged in)
* [mail] - mail address (if logged in)
* [name] - real name (if logged in)
*
* @author Andreas Gohr <andi@splitbrain.org>
* @author Michael Klier <chi@chimeric.de>
* @param string $text - optional text to check, if not given the globals are used
* @return bool - true if a spam word was found
*/
function checkwordblock($text = '') {
global $TEXT;
global $PRE;
global $SUF;
global $conf;
global $INFO;
if(!$conf['usewordblock']) return false;
if(!$text) $text = "$PRE $TEXT $SUF";
// we prepare the text a tiny bit to prevent spammers circumventing URL checks
$text = preg_replace('!(\b)(www\.[\w.:?\-;,]+?\.[\w.:?\-;,]+?[\w/\#~:.?+=&%@\!\-.:?\-;,]+?)([.:?\-;,]*[^\w/\#~:.?+=&%@\!\-.:?\-;,])!i', '\1http://\2 \2\3', $text);
$wordblocks = getWordblocks();
// how many lines to read at once (to work around some PCRE limits)
if(version_compare(phpversion(), '4.3.0', '<')) {
// old versions of PCRE define a maximum of parenthesises even if no
// backreferences are used - the maximum is 99
// this is very bad performancewise and may even be too high still
$chunksize = 40;
} else {
// read file in chunks of 200 - this should work around the
// MAX_PATTERN_SIZE in modern PCRE
$chunksize = 200;
}
while($blocks = array_splice($wordblocks, 0, $chunksize)) {
$re = array();
// build regexp from blocks
foreach($blocks as $block) {
$block = preg_replace('/#.*$/', '', $block);
$block = trim($block);
if(empty($block)) continue;
$re[] = $block;
}
if(count($re) && preg_match('#('.join('|', $re).')#si', $text, $matches)) {
// prepare event data
$data['matches'] = $matches;
$data['userinfo']['ip'] = $_SERVER['REMOTE_ADDR'];
if($_SERVER['REMOTE_USER']) {
$data['userinfo']['user'] = $_SERVER['REMOTE_USER'];
$data['userinfo']['name'] = $INFO['userinfo']['name'];
$data['userinfo']['mail'] = $INFO['userinfo']['mail'];
}
$callback = create_function('', 'return true;');
return trigger_event('COMMON_WORDBLOCK_BLOCKED', $data, $callback, true);
}
}
return false;
}
/**
* Return the IP of the client
*
* Honours X-Forwarded-For and X-Real-IP Proxy Headers
*
* It returns a comma separated list of IPs if the above mentioned
* headers are set. If the single parameter is set, it tries to return
* a routable public address, prefering the ones suplied in the X
* headers
*
* @author Andreas Gohr <andi@splitbrain.org>
* @param boolean $single If set only a single IP is returned
* @return string
*/
function clientIP($single = false) {
$ip = array();
$ip[] = $_SERVER['REMOTE_ADDR'];
if(!empty($_SERVER['HTTP_X_FORWARDED_FOR']))
$ip = array_merge($ip, explode(',', str_replace(' ', '', $_SERVER['HTTP_X_FORWARDED_FOR'])));
if(!empty($_SERVER['HTTP_X_REAL_IP']))
$ip = array_merge($ip, explode(',', str_replace(' ', '', $_SERVER['HTTP_X_REAL_IP'])));
// some IPv4/v6 regexps borrowed from Feyd
// see: http://forums.devnetwork.net/viewtopic.php?f=38&t=53479
$dec_octet = '(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|[0-9])';
$hex_digit = '[A-Fa-f0-9]';
$h16 = "{$hex_digit}{1,4}";
$IPv4Address = "$dec_octet\\.$dec_octet\\.$dec_octet\\.$dec_octet";
$ls32 = "(?:$h16:$h16|$IPv4Address)";
$IPv6Address =
"(?:(?:{$IPv4Address})|(?:".
"(?:$h16:){6}$ls32".
"|::(?:$h16:){5}$ls32".
"|(?:$h16)?::(?:$h16:){4}$ls32".
"|(?:(?:$h16:){0,1}$h16)?::(?:$h16:){3}$ls32".
"|(?:(?:$h16:){0,2}$h16)?::(?:$h16:){2}$ls32".
"|(?:(?:$h16:){0,3}$h16)?::(?:$h16:){1}$ls32".
"|(?:(?:$h16:){0,4}$h16)?::$ls32".
"|(?:(?:$h16:){0,5}$h16)?::$h16".
"|(?:(?:$h16:){0,6}$h16)?::".
")(?:\\/(?:12[0-8]|1[0-1][0-9]|[1-9][0-9]|[0-9]))?)";
// remove any non-IP stuff
$cnt = count($ip);
$match = array();
for($i = 0; $i < $cnt; $i++) {
if(preg_match("/^$IPv4Address$/", $ip[$i], $match) || preg_match("/^$IPv6Address$/", $ip[$i], $match)) {
$ip[$i] = $match[0];
} else {
$ip[$i] = '';
}
if(empty($ip[$i])) unset($ip[$i]);
}
$ip = array_values(array_unique($ip));
if(!$ip[0]) $ip[0] = '0.0.0.0'; // for some strange reason we don't have a IP
if(!$single) return join(',', $ip);
// decide which IP to use, trying to avoid local addresses
$ip = array_reverse($ip);
foreach($ip as $i) {
if(preg_match('/^(::1|[fF][eE]80:|127\.|10\.|192\.168\.|172\.((1[6-9])|(2[0-9])|(3[0-1]))\.)/', $i)) {
continue;
} else {
return $i;
}
}
// still here? just use the first (last) address
return $ip[0];
}
/**
* Check if the browser is on a mobile device
*
* Adapted from the example code at url below
*
* @link http://www.brainhandles.com/2007/10/15/detecting-mobile-browsers/#code
*/
function clientismobile() {
if(isset($_SERVER['HTTP_X_WAP_PROFILE'])) return true;
if(preg_match('/wap\.|\.wap/i', $_SERVER['HTTP_ACCEPT'])) return true;
if(!isset($_SERVER['HTTP_USER_AGENT'])) return false;
$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';
if(preg_match("/$uamatches/i", $_SERVER['HTTP_USER_AGENT'])) return true;
return false;
}
/**
* Convert one or more comma separated IPs to hostnames
*
* If $conf['dnslookups'] is disabled it simply returns the input string
*
* @author Glen Harris <astfgl@iamnota.org>
* @param string $ips comma separated list of IP addresses
* @return string a comma separated list of hostnames
*/
function gethostsbyaddrs($ips) {
global $conf;
if(!$conf['dnslookups']) return $ips;
$hosts = array();
$ips = explode(',', $ips);
if(is_array($ips)) {
foreach($ips as $ip) {
$hosts[] = gethostbyaddr(trim($ip));
}
return join(',', $hosts);
} else {
return gethostbyaddr(trim($ips));
}
}
/**
* Checks if a given page is currently locked.
*
* removes stale lockfiles
*
* @author Andreas Gohr <andi@splitbrain.org>
*/
function checklock($id) {
global $conf;
$lock = wikiLockFN($id);
//no lockfile
if(!@file_exists($lock)) return false;
//lockfile expired
if((time() - filemtime($lock)) > $conf['locktime']) {
@unlink($lock);
return false;
}
//my own lock
list($ip, $session) = explode("\n", io_readFile($lock));
if($ip == $_SERVER['REMOTE_USER'] || $ip == clientIP() || $session == session_id()) {
return false;
}
return $ip;
}
/**
* Lock a page for editing
*
* @author Andreas Gohr <andi@splitbrain.org>
*/
function lock($id) {
global $conf;
if($conf['locktime'] == 0) {
return;
}
$lock = wikiLockFN($id);
if($_SERVER['REMOTE_USER']) {
io_saveFile($lock, $_SERVER['REMOTE_USER']);
} else {
io_saveFile($lock, clientIP()."\n".session_id());
}
}
/**
* Unlock a page if it was locked by the user
*
* @author Andreas Gohr <andi@splitbrain.org>
* @param string $id page id to unlock
* @return bool true if a lock was removed
*/
function unlock($id) {
$lock = wikiLockFN($id);
if(@file_exists($lock)) {
list($ip, $session) = explode("\n", io_readFile($lock));
if($ip == $_SERVER['REMOTE_USER'] || $ip == clientIP() || $session == session_id()) {
@unlink($lock);
return true;
}
}
return false;
}
/**
* convert line ending to unix format
*
* also makes sure the given text is valid UTF-8
*
* @see formText() for 2crlf conversion
* @author Andreas Gohr <andi@splitbrain.org>
*/
function cleanText($text) {
$text = preg_replace("/(\015\012)|(\015)/", "\012", $text);
// if the text is not valid UTF-8 we simply assume latin1
// this won't break any worse than it breaks with the wrong encoding
// but might actually fix the problem in many cases
if(!utf8_check($text)) $text = utf8_encode($text);
return $text;
}
/**
* Prepares text for print in Webforms by encoding special chars.
* It also converts line endings to Windows format which is
* pseudo standard for webforms.
*
* @see cleanText() for 2unix conversion
* @author Andreas Gohr <andi@splitbrain.org>
*/
function formText($text) {
$text = str_replace("\012", "\015\012", $text);
return htmlspecialchars($text);
}
/**
* Returns the specified local text in raw format
*
* @author Andreas Gohr <andi@splitbrain.org>
*/
function rawLocale($id, $ext = 'txt') {
return io_readFile(localeFN($id, $ext));
}
/**
* Returns the raw WikiText
*
* @author Andreas Gohr <andi@splitbrain.org>
*/
function rawWiki($id, $rev = '') {
return io_readWikiPage(wikiFN($id, $rev), $id, $rev);
}
/**
* Returns the pagetemplate contents for the ID's namespace
*
* @triggers COMMON_PAGETPL_LOAD
* @author Andreas Gohr <andi@splitbrain.org>
*/
function pageTemplate($id) {
global $conf;
if(is_array($id)) $id = $id[0];
// prepare initial event data
$data = array(
'id' => $id, // the id of the page to be created
'tpl' => '', // the text used as template
'tplfile' => '', // the file above text was/should be loaded from
'doreplace' => true // should wildcard replacements be done on the text?
);
$evt = new Doku_Event('COMMON_PAGETPL_LOAD', $data);
if($evt->advise_before(true)) {
// the before event might have loaded the content already
if(empty($data['tpl'])) {
// if the before event did not set a template file, try to find one
if(empty($data['tplfile'])) {
$path = dirname(wikiFN($id));
if(@file_exists($path.'/_template.txt')) {
$data['tplfile'] = $path.'/_template.txt';
} else {
// search upper namespaces for templates
$len = strlen(rtrim($conf['datadir'], '/'));
while(strlen($path) >= $len) {
if(@file_exists($path.'/__template.txt')) {
$data['tplfile'] = $path.'/__template.txt';
break;
}
$path = substr($path, 0, strrpos($path, '/'));
}
}
}
// load the content
$data['tpl'] = io_readFile($data['tplfile']);
}
if($data['doreplace']) parsePageTemplate($data);
}
$evt->advise_after();
unset($evt);
return $data['tpl'];
}
/**
* Performs common page template replacements
* This works on data from COMMON_PAGETPL_LOAD
*
* @author Andreas Gohr <andi@splitbrain.org>
*/
function parsePageTemplate(&$data) {
/**
* @var string $id the id of the page to be created
* @var string $tpl the text used as template
* @var string $tplfile the file above text was/should be loaded from
* @var bool $doreplace should wildcard replacements be done on the text?
*/
extract($data);
global $USERINFO;
global $conf;
// replace placeholders
$file = noNS($id);
$page = strtr($file, $conf['sepchar'], ' ');
$tpl = str_replace(
array(
'@ID@',
'@NS@',
'@FILE@',
'@!FILE@',
'@!FILE!@',
'@PAGE@',
'@!PAGE@',
'@!!PAGE@',
'@!PAGE!@',
'@USER@',
'@NAME@',
'@MAIL@',
'@DATE@',
),
array(
$id,
getNS($id),
$file,
utf8_ucfirst($file),
utf8_strtoupper($file),
$page,
utf8_ucfirst($page),
utf8_ucwords($page),
utf8_strtoupper($page),
$_SERVER['REMOTE_USER'],
$USERINFO['name'],
$USERINFO['mail'],
$conf['dformat'],
), $tpl
);
// we need the callback to work around strftime's char limit
$tpl = preg_replace_callback('/%./', create_function('$m', 'return strftime($m[0]);'), $tpl);
$data['tpl'] = $tpl;
return $tpl;
}
/**
* Returns the raw Wiki Text in three slices.
*
* The range parameter needs to have the form "from-to"
* and gives the range of the section in bytes - no
* UTF-8 awareness is needed.
* The returned order is prefix, section and suffix.
*
* @author Andreas Gohr <andi@splitbrain.org>
*/
function rawWikiSlices($range, $id, $rev = '') {
$text = io_readWikiPage(wikiFN($id, $rev), $id, $rev);
// Parse range
list($from, $to) = explode('-', $range, 2);
// Make range zero-based, use defaults if marker is missing
$from = !$from ? 0 : ($from - 1);
$to = !$to ? strlen($text) : ($to - 1);
$slices[0] = substr($text, 0, $from);
$slices[1] = substr($text, $from, $to - $from);
$slices[2] = substr($text, $to);
return $slices;
}
/**
* Joins wiki text slices
*
* function to join the text slices.
* When the pretty parameter is set to true it adds additional empty
* lines between sections if needed (used on saving).
*
* @author Andreas Gohr <andi@splitbrain.org>
*/
function con($pre, $text, $suf, $pretty = false) {
if($pretty) {
if($pre !== '' && substr($pre, -1) !== "\n" &&
substr($text, 0, 1) !== "\n"
) {
$pre .= "\n";
}
if($suf !== '' && substr($text, -1) !== "\n" &&
substr($suf, 0, 1) !== "\n"
) {
$text .= "\n";
0