1 <?php rcs_id('$Id: stdlib.php,v 1.29 2001-02-13 05:54:38 dairiki Exp $');
5 Standard functions for Wiki functionality
7 WikiURL($pagename, $args, $abs)
9 LinkExistingWikiWord($wikiword, $linktext)
10 LinkUnknownWikiWord($wikiword, $linktext)
11 LinkURL($url, $linktext)
13 LinkInterWikiLink($link, $linktext)
14 CookSpaces($pagearray)
15 class Stack (push(), pop(), cnt(), top())
16 SetHTMLOutputMode($newmode, $depth)
17 UpdateRecentChanges($dbi, $pagename, $isnewpage)
18 ParseAndLink($bracketlink)
19 ExtractWikiPageLinks($content)
20 LinkRelatedPages($dbi, $pagename)
21 GeneratePage($template, $content, $name, $hash)
24 function fix_magic_quotes_gpc (&$text)
26 if (get_magic_quotes_gpc()) {
27 $text = stripslashes($text);
33 function get_remote_host () {
34 // Apache won't show REMOTE_HOST unless the admin configured it
35 // properly. We'll be nice and see if it's there.
36 if (getenv('REMOTE_HOST'))
37 return getenv('REMOTE_HOST');
38 $host = getenv('REMOTE_ADDR');
39 if (ENABLE_REVERSE_DNS)
40 return gethostbyaddr($host);
44 function SearchPath ($file, $missing_ok = false, $path = false)
46 if (ereg('^/', $file))
47 return $file; // absolute path.
50 $path = $GLOBALS['DataPath'];
52 while (list($i, $dir) = each($path))
54 if (file_exists("$dir/$file"))
59 ExitWiki("$file: file not found");
62 function arrays_equal ($a, $b)
64 if (sizeof($a) != sizeof($b))
66 for ($i = 0; $i < sizeof($a); $i++)
76 function DataURL($url) {
77 if (preg_match('@^(\w+:|/)@', $url))
79 return SERVER_URL . DATA_PATH . "/$url";
82 function WikiURL($pagename, $args = '') {
87 while (list ($key, $val) = each($args)) {
88 $enc_args[] = urlencode($key) . '=' . urlencode($val);
90 $args = join('&', $enc_args);
94 $url = rawurlencode($pagename);
99 $url = basename(SCRIPT_NAME) .
100 "?pagename=" . rawurlencode($pagename);
108 define('NO_END_TAG_PAT',
109 '/^' . join('|', array('area', 'base', 'basefont',
110 'br', 'col', 'frame',
111 'hr', 'image', 'input',
112 'isindex', 'link', 'meta',
115 function Element($tag, $args = '', $content = '')
120 while (list($key, $val) = each($args))
122 if (is_string($val) || is_numeric($val))
123 $html .= sprintf(' %s="%s"', $key, htmlspecialchars($val));
132 if (!preg_match(NO_END_TAG_PAT, $tag))
135 $html .= "</$tag>\n";//FIXME: newline might not always be desired.
140 function QElement($tag, $args = '', $content = '')
143 return Element($tag, $args, htmlspecialchars($content));
147 return Element($tag, htmlspecialchars($content));
151 function LinkURL($url, $linktext='') {
152 // FIXME: Is this needed (or sufficient?)
153 if(ereg("[<>\"]", $url)) {
154 return "<b><u>BAD URL -- remove all of <, >, "</u></b>";
156 return QElement('a', array('href' => $url), ($linktext ? $linktext : $url));
159 function LinkExistingWikiWord($wikiword, $linktext='', $class = 'wikiword') {
160 return Element('a', array('href' => WikiURL($wikiword),
162 $linktext ? $linktext : $wikiword);
165 function LinkUnknownWikiWord($wikiword, $linktext='', $class = 'unknownwikiword') {
166 if (empty($linktext))
167 $linktext = $wikiword;
169 return Element('span', array('class' => 'unknownwikiword'),
170 QElement('u', array('class' => 'unknownwikiword'), $linktext) .
172 array('href' => WikiURL($wikiword, array('action' => 'edit')),
173 'class' => 'unknownwikiword'),
178 function LinkImage($url, $alt='[External Image]') {
179 // FIXME: Is this needed (or sufficient?)
180 // As long as the src in htmlspecialchars()ed I think it's safe.
181 if(ereg('[<>"]', $url)) {
182 return "<b><u>BAD URL -- remove all of <, >, "</u></b>";
184 return Element('img', array('src' => $url, 'alt' => $alt));
188 // converts spaces to tabs
189 function CookSpaces($pagearray) {
190 return preg_replace("/ {3,8}/", "\t", $pagearray);
195 var $items = array();
198 function push($item) {
199 $this->items[$this->size] = $item;
205 if ($this->size == 0) {
206 return false; // stack is empty
209 return $this->items[$this->size];
218 return $this->items[$this->size - 1];
224 // end class definition
227 function MakeWikiForm ($pagename, $args, $button_text = '')
229 $formargs['action'] = USE_PATH_INFO ? WikiURL($pagename) : SCRIPT_NAME;
230 $formargs['method'] = 'post';
234 while (list($key, $val) = each($args))
236 $a = array('name' => $key, 'value' => $val, 'type' => 'hidden');
238 if (preg_match('/^ (\d*) \( (.*) \) ((upload)?) $/xi', $val, $m))
242 $a['size'] = $m[1] ? $m[1] : 30;
247 $formargs['enctype'] = 'multipart/form-data';
248 $contents .= Element('input',
249 array('name' => 'MAX_FILE_SIZE',
250 'value' => MAX_UPLOAD_SIZE,
251 'type' => 'hidden'));
255 $contents .= Element('input', $a);
258 $row = Element('td', $contents);
260 if (!empty($button_text)) {
261 $row .= Element('td', Element('input', array('type' => 'submit',
262 'value' => $button_text)));
265 return Element('form', $formargs,
267 Element('tr', $row)));
270 function SplitQueryArgs ($query_args = '')
272 $split_args = split('&', $query_args);
274 while (list($key, $val) = each($split_args))
275 if (preg_match('/^ ([^=]+) =? (.*) /x', $val, $m))
276 $args[$m[1]] = $m[2];
280 function LinkPhpwikiURL($url, $text = '') {
285 if (!preg_match('/^ phpwiki: ([^?]*) [?]? (.*) $/x', $url, $m))
286 return "<b><u>BAD phpwiki: URL</u></b>";
289 $page = urldecode($m[1]);
292 if (!$page && preg_match('/^(diff|edit|links|info|diff)=([^&]+)$/', $qargs, $m))
294 // Convert old style links (to not break diff links in RecentChanges).
295 $page = urldecode($m[2]);
296 $args = array("action" => $m[1]);
300 $args = SplitQueryArgs($qargs);
304 // FIXME: ug, don't like this
306 if (!empty($args['action']) && !IsSafeAction($args['action']))
308 // Don't allow administrative links on unlocked pages.
310 if (($pagehash['flags'] & FLAG_PAGE_LOCKED) == 0)
311 return QElement('u', gettext('Lock page to enable link'));
314 // FIXME: ug, don't like this
315 if (preg_match('/=\d*\(/', $qargs))
316 return MakeWikiForm($page, $args, $text);
318 return LinkURL(WikiURL($page, $args), $text ? $text : $url);
321 function ParseAndLink($bracketlink) {
322 global $dbi, $AllowedProtocols, $InlineImages;
323 global $InterWikiLinkRegexp;
325 // $bracketlink will start and end with brackets; in between
326 // will be either a page name, a URL or both separated by a pipe.
328 // strip brackets and leading space
329 preg_match("/(\[\s*)(.+?)(\s*\])/", $bracketlink, $match);
330 // match the contents
331 preg_match("/([^|]+)(\|)?([^|]+)?/", $match[2], $matches);
333 if (isset($matches[3])) {
334 // named link of the form "[some link name | http://blippy.com/]"
335 $URL = trim($matches[3]);
336 $linkname = trim($matches[1]);
339 // unnamed link of the form "[http://blippy.com/] or [wiki page]"
340 $URL = trim($matches[1]);
342 $linktype = 'simple';
345 if (IsWikiPage($dbi, $URL)) {
346 $link['type'] = "wiki-$linktype";
347 $link['link'] = LinkExistingWikiWord($URL, $linkname);
348 } elseif (preg_match("#^($AllowedProtocols):#", $URL)) {
349 // if it's an image, embed it; otherwise, it's a regular link
350 if (preg_match("/($InlineImages)$/i", $URL)) {
351 $link['type'] = "image-$linktype";
352 $link['link'] = LinkImage($URL, $linkname);
354 $link['type'] = "url-$linktype";
355 $link['link'] = LinkURL($URL, $linkname);
357 } elseif (preg_match("#^phpwiki:(.*)#", $URL, $match)) {
358 $link['type'] = "url-wiki-$linktype";
359 $link['link'] = LinkPhpwikiURL($URL, $linkname);
360 } elseif (preg_match("#^\d+$#", $URL)) {
361 $link['type'] = "footnote-$linktype";
362 $link['link'] = $URL;
363 } elseif (function_exists('LinkInterWikiLink') &&
364 preg_match("#^$InterWikiLinkRegexp:#", $URL)) {
365 $link['type'] = "interwiki-$linktype";
366 $link['link'] = LinkInterWikiLink($URL, $linkname);
368 $link['type'] = "wiki-unknown-$linktype";
369 $link['link'] = LinkUnknownWikiWord($URL, $linkname);
376 function ExtractWikiPageLinks($content)
378 global $WikiNameRegexp;
380 $wikilinks = array();
381 $numlines = count($content);
382 for($l = 0; $l < $numlines; $l++)
384 // remove escaped '['
385 $line = str_replace('[[', ' ', $content[$l]);
387 // bracket links (only type wiki-* is of interest)
388 $numBracketLinks = preg_match_all("/\[\s*([^\]|]+\|)?\s*(.+?)\s*\]/", $line, $brktlinks);
389 for ($i = 0; $i < $numBracketLinks; $i++) {
390 $link = ParseAndLink($brktlinks[0][$i]);
391 if (preg_match("#^wiki#", $link['type']))
392 $wikilinks[$brktlinks[2][$i]] = 1;
394 $brktlink = preg_quote($brktlinks[0][$i]);
395 $line = preg_replace("|$brktlink|", '', $line);
398 // BumpyText old-style wiki links
399 if (preg_match_all("/!?$WikiNameRegexp/", $line, $link)) {
400 for ($i = 0; isset($link[0][$i]); $i++) {
401 if($link[0][$i][0] <> '!')
402 $wikilinks[$link[0][$i]] = 1;
409 function LinkRelatedPages($dbi, $pagename)
411 // currently not supported everywhere
412 if(!function_exists('GetWikiPageLinks'))
415 $links = GetWikiPageLinks($dbi, $pagename);
418 $txt .= sprintf (gettext ("%d best incoming links:"), NUM_RELATED_PAGES);
420 for($i = 0; $i < NUM_RELATED_PAGES; $i++) {
421 if(isset($links['in'][$i])) {
422 list($name, $score) = $links['in'][$i];
423 $txt .= LinkExistingWikiWord($name) . " ($score), ";
428 $txt .= sprintf (gettext ("%d best outgoing links:"), NUM_RELATED_PAGES);
430 for($i = 0; $i < NUM_RELATED_PAGES; $i++) {
431 if(isset($links['out'][$i])) {
432 list($name, $score) = $links['out'][$i];
433 if(IsWikiPage($dbi, $name))
434 $txt .= LinkExistingWikiWord($name) . " ($score), ";
439 $txt .= sprintf (gettext ("%d most popular nearby:"), NUM_RELATED_PAGES);
441 for($i = 0; $i < NUM_RELATED_PAGES; $i++) {
442 if(isset($links['popular'][$i])) {
443 list($name, $score) = $links['popular'][$i];
444 $txt .= LinkExistingWikiWord($name) . " ($score), ";
452 # GeneratePage() -- takes $content and puts it in the template $template
453 # this function contains all the template logic
455 # $template ... name of the template (see config.php for list of names)
456 # $content ... html content to put into the page
457 # $name ... page title
458 # $hash ... if called while creating a wiki page, $hash points to
459 # the $pagehash array of that wiki page.
461 function GeneratePage($template, $content, $name, $hash)
464 global $datetimeformat, $dbi, $logo, $FieldSeparator;
465 global $user, $pagename;
467 if (!is_array($hash))
470 function _dotoken ($id, $val, &$page) {
471 global $FieldSeparator;
472 $page = str_replace("$FieldSeparator#$id$FieldSeparator#",
476 function _iftoken ($id, $condition, &$page) {
477 global $FieldSeparator;
479 // line based IF directive
480 $lineyes = "$FieldSeparator#IF $id$FieldSeparator#";
481 $lineno = "$FieldSeparator#IF !$id$FieldSeparator#";
482 // block based IF directive
483 $blockyes = "$FieldSeparator#IF:$id$FieldSeparator#";
484 $blockyesend = "$FieldSeparator#ENDIF:$id$FieldSeparator#";
485 $blockno = "$FieldSeparator#IF:!$id$FieldSeparator#";
486 $blocknoend = "$FieldSeparator#ENDIF:!$id$FieldSeparator#";
489 $page = str_replace($lineyes, '', $page);
490 $page = str_replace($blockyes, '', $page);
491 $page = str_replace($blockyesend, '', $page);
492 $page = preg_replace("/$blockno(.*?)$blocknoend/s", '', $page);
493 $page = ereg_replace("${lineno}[^\n]*\n", '', $page);
495 $page = str_replace($lineno, '', $page);
496 $page = str_replace($blockno, '', $page);
497 $page = str_replace($blocknoend, '', $page);
498 $page = preg_replace("/$blockyes(.*?)$blockyesend/s", '', $page);
499 $page = ereg_replace("${lineyes}[^\n]*\n", '', $page);
503 $page = join('', file(SearchPath($templates[$template])));
504 $page = str_replace('###', "$FieldSeparator#", $page);
506 // valid for all pagetypes
507 _iftoken('COPY', isset($hash['copy']), $page);
508 _iftoken('LOCK', (isset($hash['flags']) &&
509 ($hash['flags'] & FLAG_PAGE_LOCKED)), $page);
510 _iftoken('ADMIN', $user->is_admin(), $page);
511 _iftoken('ANONYMOUS', !$user->is_authenticated(), $page);
513 if (empty($hash['minor_edit_checkbox']))
514 $hash['minor_edit_checkbox'] = '';
515 _iftoken('MINOR_EDIT_CHECKBOX', $hash['minor_edit_checkbox'], $page);
517 _dotoken('MINOR_EDIT_CHECKBOX', $hash['minor_edit_checkbox'], $page);
519 _dotoken('USERID', htmlspecialchars($user->id()), $page);
520 _dotoken('PAGE', htmlspecialchars($name), $page);
521 _dotoken('LOGO', htmlspecialchars(DataURL($logo)), $page);
523 _dotoken('RCS_IDS', $GLOBALS['RCS_IDS'], $page);
525 $prefs = $user->getPreferences();
526 _dotoken('EDIT_AREA_WIDTH', $prefs['edit_area.width'], $page);
527 _dotoken('EDIT_AREA_HEIGHT', $prefs['edit_area.height'], $page);
529 // FIXME: Clean up this stuff
530 $browse_page = WikiURL($name);
531 _dotoken('BROWSE_PAGE', $browse_page, $page);
532 $arg_sep = strstr($browse_page, '?') ? '&' : '?';
533 _dotoken('ACTION', $browse_page . $arg_sep . "action=", $page);
534 _dotoken('BROWSE', WikiURL(''), $page);
538 SERVER_URL . VIRTUAL_PATH . "/" . WikiURL($pagename), $page);
540 _dotoken('BASE_URL', SERVER_URL . SCRIPT_NAME, $page);
542 // invalid for messages (search results, error messages)
543 if ($template != 'MESSAGE') {
544 _dotoken('PAGEURL', rawurlencode($name), $page);
545 if (!empty($hash['lastmodified']))
546 _dotoken('LASTMODIFIED',
547 date($datetimeformat, $hash['lastmodified']), $page);
548 if (!empty($hash['author']))
549 _dotoken('LASTAUTHOR', $hash['author'], $page);
550 if (!empty($hash['version']))
551 _dotoken('VERSION', $hash['version'], $page);
552 if (strstr($page, "$FieldSeparator#HITS$FieldSeparator#")) {
553 _dotoken('HITS', GetHitCount($dbi, $name), $page);
555 if (strstr($page, "$FieldSeparator#RELATEDPAGES$FieldSeparator#")) {
556 _dotoken('RELATEDPAGES', LinkRelatedPages($dbi, $name), $page);
560 _dotoken('CONTENT', $content, $page);
564 function UpdateRecentChanges($dbi, $pagename, $isnewpage)
568 global $WikiPageStore;
570 $recentchanges = RetrievePage($dbi, gettext ("RecentChanges"), $WikiPageStore);
572 // this shouldn't be necessary, since PhpWiki loads
573 // default pages if this is a new baby Wiki
574 if ($recentchanges == -1) {
575 $recentchanges = array();
579 $today = date($dateformat, $now);
581 if (date($dateformat, $recentchanges['lastmodified']) != $today) {
583 $recentchanges['lastmodified'] = $now;
588 $numlines = sizeof($recentchanges['content']);
592 // scroll through the page to the first date and break
593 // dates are marked with "____" at the beginning of the line
594 for ($i = 0; $i < $numlines; $i++) {
595 if (preg_match("/^____/",
596 $recentchanges['content'][$i])) {
599 $newpage[$k++] = $recentchanges['content'][$i];
603 // if it's a new date, insert it
604 $newpage[$k++] = $isNewDay ? "____$today\r"
605 : $recentchanges['content'][$i++];
607 $userid = $user->id();
609 // add the updated page's name to the array
611 $newpage[$k++] = "* [$pagename] (new) ..... $userid\r";
613 $diffurl = "phpwiki:" . rawurlencode($pagename) . "?action=diff";
614 $newpage[$k++] = "* [$pagename] ([diff|$diffurl]) ..... $userid\r";
617 $newpage[$k++] = "\r";
619 // copy the rest of the page into the new array
620 // and skip previous entry for $pagename
621 $pagename = preg_quote($pagename);
622 for (; $i < $numlines; $i++) {
623 if (!preg_match("|\[$pagename\]|", $recentchanges['content'][$i])) {
624 $newpage[$k++] = $recentchanges['content'][$i];
628 $recentchanges['content'] = $newpage;
630 InsertPage($dbi, gettext ("RecentChanges"), $recentchanges);
636 // c-file-style: "ellemtel"