1 <?php rcs_id('$Id: stdlib.php,v 1.41 2001-08-12 23:57:37 wainstead Exp $');
5 Standard functions for Wiki functionality
6 WikiURL($pagename, $args, $abs)
8 LinkExistingWikiWord($wikiword, $linktext)
9 LinkUnknownWikiWord($wikiword, $linktext)
10 LinkURL($url, $linktext)
12 LinkInterWikiLink($link, $linktext)
13 CookSpaces($pagearray)
14 class Stack (push(), pop(), cnt(), top())
15 UpdateRecentChanges($dbi, $pagename, $isnewpage)
16 ParseAndLink($bracketlink)
17 ExtractWikiPageLinks($content)
18 LinkRelatedPages($dbi, $pagename)
19 GeneratePage($template, $content, $name, $hash)
22 function fix_magic_quotes_gpc (&$text)
24 if (get_magic_quotes_gpc()) {
25 $text = stripslashes($text);
30 function arrays_equal ($a, $b)
32 if (sizeof($a) != sizeof($b))
34 for ($i = 0; $i < sizeof($a); $i++)
41 function DataURL($url) {
42 if (preg_match('@^(\w+:|/)@', $url))
44 return SERVER_URL . DATA_PATH . "/$url";
47 function WikiURL($pagename, $args = '') {
52 while (list ($key, $val) = each($args)) {
53 $enc_args[] = urlencode($key) . '=' . urlencode($val);
55 $args = join('&', $enc_args);
59 $url = rawurlencode($pagename);
64 $url = basename(SCRIPT_NAME) .
65 "?pagename=" . rawurlencode($pagename);
73 function StartTag($tag, $args = '')
78 while (list($key, $val) = each($args))
80 if (is_string($val) || is_numeric($val))
81 $s .= sprintf(' %s="%s"', $key, htmlspecialchars($val));
90 define('NO_END_TAG_PAT',
91 '/^' . join('|', array('area', 'base', 'basefont',
93 'hr', 'image', 'input',
94 'isindex', 'link', 'meta',
97 function Element($tag, $args = '', $content = '')
100 if (!is_array($args))
105 $html = StartTag($tag, $args);
106 if (!preg_match(NO_END_TAG_PAT, $tag))
109 $html .= "</$tag>";//FIXME: newline might not always be desired.
114 function QElement($tag, $args = '', $content = '')
117 return Element($tag, $args, htmlspecialchars($content));
121 return Element($tag, htmlspecialchars($content));
125 function LinkURL($url, $linktext='') {
126 // FIXME: Is this needed (or sufficient?)
127 if(ereg("[<>\"]", $url)) {
128 return "<b><u>BAD URL -- remove all of <, >, "</u></b>";
132 if (empty($linktext))
133 $linktext = QElement('span', array('class' => 'rawurl'), $url);
135 $linktext = htmlspecialchars($linktext);
138 array('href' => $url, 'class' => 'linkurl'),
142 function LinkExistingWikiWord($wikiword, $linktext='') {
143 if (empty($linktext))
144 $linktext = QElement('span', array('class' => 'wikiword'), $wikiword);
146 $linktext = htmlspecialchars($linktext);
148 return Element('a', array('href' => WikiURL($wikiword),
149 'class' => 'wikilink'),
153 function LinkUnknownWikiWord($wikiword, $linktext='') {
154 if (empty($linktext))
155 $linktext = QElement('span', array('class' => 'wikiword'), $wikiword);
157 $linktext = htmlspecialchars($linktext);
159 return Element('span', array('class' => 'wikiunknown'),
160 QElement('a', array('href' => WikiURL($wikiword, array('action' => 'edit')),'class' => 'wikiunknown'),'?')
161 . Element('u', $linktext)
166 function LinkImage($url, $alt='[External Image]') {
167 // FIXME: Is this needed (or sufficient?)
168 // As long as the src in htmlspecialchars()ed I think it's safe.
169 if(ereg('[<>"]', $url)) {
170 return "<b><u>BAD URL -- remove all of <, >, "</u></b>";
172 return Element('img', array('src' => $url, 'alt' => $alt));
176 // converts spaces to tabs
177 function CookSpaces($pagearray) {
178 return preg_replace("/ {3,8}/", "\t", $pagearray);
183 var $items = array();
186 function push($item) {
187 $this->items[$this->size] = $item;
193 if ($this->size == 0) {
194 return false; // stack is empty
197 return $this->items[$this->size];
206 return $this->items[$this->size - 1];
212 // end class definition
215 function MakeWikiForm ($pagename, $args, $class, $button_text = '')
217 $formargs['action'] = USE_PATH_INFO ? WikiURL($pagename) : SCRIPT_NAME;
218 $formargs['method'] = 'post';
219 $formargs['class'] = $class;
224 while (list($key, $val) = each($args))
226 $a = array('name' => $key, 'value' => $val, 'type' => 'hidden');
228 if (preg_match('/^ (\d*) \( (.*) \) ((upload)?) $/xi', $val, $m))
232 $a['size'] = $m[1] ? $m[1] : 30;
237 $formargs['enctype'] = 'multipart/form-data';
238 $contents .= Element('input',
239 array('name' => 'MAX_FILE_SIZE',
240 'value' => MAX_UPLOAD_SIZE,
241 'type' => 'hidden'));
245 $contents .= Element('input', $a);
248 $row = Element('td', $contents);
250 if (!empty($button_text)) {
251 $row .= Element('td', Element('input', array('type' => 'submit',
252 'value' => $button_text)));
255 return Element('form', $formargs,
256 Element('table', array('cellspacing' => 0, 'cellpadding' => 2, 'border' => 0),
257 Element('tr', $row)));
260 function SplitQueryArgs ($query_args = '')
262 $split_args = split('&', $query_args);
264 while (list($key, $val) = each($split_args))
265 if (preg_match('/^ ([^=]+) =? (.*) /x', $val, $m))
266 $args[$m[1]] = $m[2];
270 function LinkPhpwikiURL($url, $text = '') {
275 if (!preg_match('/^ phpwiki: ([^?]*) [?]? (.*) $/x', $url, $m))
276 return "<b><u>BAD phpwiki: URL</u></b>";
279 $page = urldecode($m[1]);
282 if (!$page && preg_match('/^(diff|edit|links|info|diff)=([^&]+)$/', $qargs, $m))
284 // Convert old style links (to not break diff links in RecentChanges).
285 $page = urldecode($m[2]);
286 $args = array("action" => $m[1]);
290 $args = SplitQueryArgs($qargs);
293 if (isset($args['action']) && $args['action'] == 'browse')
294 unset($args['action']);
296 if (empty($args['action']))
298 else if (IsSafeAction($args['action']))
299 $class = 'wikiaction';
302 // Don't allow administrative links on unlocked pages.
303 // FIXME: Ugh: don't like this...
305 if (($pagehash['flags'] & FLAG_PAGE_LOCKED) == 0)
306 return QElement('u', array('class' => 'wikiunsafe'),
307 gettext('Lock page to enable link'));
309 $class = 'wikiadmin';
312 // FIXME: ug, don't like this
313 if (preg_match('/=\d*\(/', $qargs))
314 return MakeWikiForm($page, $args, $class, $text);
318 $text = htmlspecialchars($text);
320 $text = QElement('span', array('class' => 'rawurl'), $url);
322 return Element('a', array('href' => WikiURL($page, $args),
328 function ParseAndLink($bracketlink) {
329 global $dbi, $AllowedProtocols, $InlineImages;
330 global $InterWikiLinkRegexp;
332 // $bracketlink will start and end with brackets; in between
333 // will be either a page name, a URL or both separated by a pipe.
335 // strip brackets and leading space
336 preg_match("/(\[\s*)(.+?)(\s*\])/", $bracketlink, $match);
337 // match the contents
338 preg_match("/([^|]+)(\|)?([^|]+)?/", $match[2], $matches);
340 if (isset($matches[3])) {
341 // named link of the form "[some link name | http://blippy.com/]"
342 $URL = trim($matches[3]);
343 $linkname = trim($matches[1]);
346 // unnamed link of the form "[http://blippy.com/] or [wiki page]"
347 $URL = trim($matches[1]);
349 $linktype = 'simple';
352 if (IsWikiPage($dbi, $URL)) {
353 $link['type'] = "wiki-$linktype";
354 $link['link'] = LinkExistingWikiWord($URL, $linkname);
355 } elseif (preg_match("#^($AllowedProtocols):#", $URL)) {
356 // if it's an image, embed it; otherwise, it's a regular link
357 if (preg_match("/($InlineImages)$/i", $URL)) {
358 $link['type'] = "image-$linktype";
359 $link['link'] = LinkImage($URL, $linkname);
361 $link['type'] = "url-$linktype";
362 $link['link'] = LinkURL($URL, $linkname);
364 } elseif (preg_match("#^phpwiki:(.*)#", $URL, $match)) {
365 $link['type'] = "url-wiki-$linktype";
366 $link['link'] = LinkPhpwikiURL($URL, $linkname);
367 } elseif (preg_match("#^\d+$#", $URL)) {
368 $link['type'] = "footnote-$linktype";
369 $link['link'] = $URL;
370 } elseif (function_exists('LinkInterWikiLink') &&
371 preg_match("#^$InterWikiLinkRegexp:#", $URL)) {
372 $link['type'] = "interwiki-$linktype";
373 $link['link'] = LinkInterWikiLink($URL, $linkname);
375 $link['type'] = "wiki-unknown-$linktype";
376 $link['link'] = LinkUnknownWikiWord($URL, $linkname);
383 function ExtractWikiPageLinks($content)
385 global $WikiNameRegexp;
387 $wikilinks = array();
388 $numlines = count($content);
389 for($l = 0; $l < $numlines; $l++)
391 // remove escaped '['
392 $line = str_replace('[[', ' ', $content[$l]);
394 // bracket links (only type wiki-* is of interest)
395 $numBracketLinks = preg_match_all("/\[\s*([^\]|]+\|)?\s*(.+?)\s*\]/", $line, $brktlinks);
396 for ($i = 0; $i < $numBracketLinks; $i++) {
397 $link = ParseAndLink($brktlinks[0][$i]);
398 if (preg_match("#^wiki#", $link['type']))
399 $wikilinks[$brktlinks[2][$i]] = 1;
401 $brktlink = preg_quote($brktlinks[0][$i]);
402 $line = preg_replace("|$brktlink|", '', $line);
405 // BumpyText old-style wiki links
406 if (preg_match_all("/!?$WikiNameRegexp/", $line, $link)) {
407 for ($i = 0; isset($link[0][$i]); $i++) {
408 if($link[0][$i][0] <> '!')
409 $wikilinks[$link[0][$i]] = 1;
416 function LinkRelatedPages($dbi, $pagename)
418 // currently not supported everywhere
419 if(!function_exists('GetWikiPageLinks'))
422 $links = GetWikiPageLinks($dbi, $pagename);
425 $txt .= sprintf (gettext ("%d best incoming links:"), NUM_RELATED_PAGES);
427 for($i = 0; $i < NUM_RELATED_PAGES; $i++) {
428 if(isset($links['in'][$i])) {
429 list($name, $score) = $links['in'][$i];
430 $txt .= LinkExistingWikiWord($name) . " ($score), ";
435 $txt .= sprintf (gettext ("%d best outgoing links:"), NUM_RELATED_PAGES);
437 for($i = 0; $i < NUM_RELATED_PAGES; $i++) {
438 if(isset($links['out'][$i])) {
439 list($name, $score) = $links['out'][$i];
440 if(IsWikiPage($dbi, $name))
441 $txt .= LinkExistingWikiWord($name) . " ($score), ";
446 $txt .= sprintf (gettext ("%d most popular nearby:"), NUM_RELATED_PAGES);
448 for($i = 0; $i < NUM_RELATED_PAGES; $i++) {
449 if(isset($links['popular'][$i])) {
450 list($name, $score) = $links['popular'][$i];
451 $txt .= LinkExistingWikiWord($name) . " ($score), ";
459 # GeneratePage() -- takes $content and puts it in the template $template
460 # this function contains all the template logic
462 # $template ... name of the template (see config.php for list of names)
463 # $content ... html content to put into the page
464 # $name ... page title
465 # $hash ... if called while creating a wiki page, $hash points to
466 # the $pagehash array of that wiki page.
468 function GeneratePage($template, $content, $name, $hash)
471 global $datetimeformat, $dbi, $logo, $FieldSeparator;
472 global $user, $pagename;
473 global $WikiPageStore;
475 if (!is_array($hash))
478 function _dotoken ($id, $val, &$page) {
479 global $FieldSeparator;
480 $page = str_replace("$FieldSeparator#$id$FieldSeparator#",
484 function _iftoken ($id, $condition, &$page) {
485 global $FieldSeparator;
487 // line based IF directive
488 $lineyes = "$FieldSeparator#IF $id$FieldSeparator#";
489 $lineno = "$FieldSeparator#IF !$id$FieldSeparator#";
490 // block based IF directive
491 $blockyes = "$FieldSeparator#IF:$id$FieldSeparator#";
492 $blockyesend = "$FieldSeparator#ENDIF:$id$FieldSeparator#";
493 $blockno = "$FieldSeparator#IF:!$id$FieldSeparator#";
494 $blocknoend = "$FieldSeparator#ENDIF:!$id$FieldSeparator#";
497 $page = str_replace($lineyes, '', $page);
498 $page = str_replace($blockyes, '', $page);
499 $page = str_replace($blockyesend, '', $page);
500 $page = preg_replace("/$blockno(.*?)$blocknoend/s", '', $page);
501 $page = ereg_replace("${lineno}[^\n]*\n", '', $page);
503 $page = str_replace($lineno, '', $page);
504 $page = str_replace($blockno, '', $page);
505 $page = str_replace($blocknoend, '', $page);
506 $page = preg_replace("/$blockyes(.*?)$blockyesend/s", '', $page);
507 $page = ereg_replace("${lineyes}[^\n]*\n", '', $page);
511 $page = join('', file(FindLocalizedFile($templates[$template])));
512 $page = str_replace('###', "$FieldSeparator#", $page);
514 // valid for all pagetypes
515 _iftoken('COPY', isset($hash['copy']), $page);
516 _iftoken('LOCK', (isset($hash['flags']) &&
517 ($hash['flags'] & FLAG_PAGE_LOCKED)), $page);
518 _iftoken('ADMIN', $user->is_admin(), $page);
519 _iftoken('ANONYMOUS', !$user->is_authenticated(), $page);
520 _iftoken('CURRENT', isset($hash['version']) && $hash['version'] == GetMaxVersionNumber($dbi, $hash['pagename'], $WikiPageStore), $page);
522 if (empty($hash['minor_edit_checkbox']))
523 $hash['minor_edit_checkbox'] = '';
524 _iftoken('MINOR_EDIT_CHECKBOX', $hash['minor_edit_checkbox'], $page);
526 _dotoken('MINOR_EDIT_CHECKBOX', $hash['minor_edit_checkbox'], $page);
528 _dotoken('USERID', htmlspecialchars($user->id()), $page);
529 _dotoken('PAGE', htmlspecialchars($name), $page);
530 _dotoken('SPLIT_PAGE',
532 preg_replace('/([[:lower:]])([[:upper:]])/', '\\1 \\2', $name)),
534 _dotoken('LOGO', htmlspecialchars(DataURL($logo)), $page);
535 _dotoken('CSS_URL', htmlspecialchars(DataURL(CSS_URL)), $page);
537 _dotoken('RCS_IDS', $GLOBALS['RCS_IDS'], $page);
539 $prefs = $user->getPreferences();
540 _dotoken('EDIT_AREA_WIDTH', $prefs['edit_area.width'], $page);
541 _dotoken('EDIT_AREA_HEIGHT', $prefs['edit_area.height'], $page);
543 // FIXME: Clean up this stuff
544 _dotoken('BROWSE', WikiURL(''), $page);
548 SERVER_URL . VIRTUAL_PATH . "/" . WikiURL($pagename), $page);
550 _dotoken('BASE_URL', SERVER_URL . SCRIPT_NAME, $page);
552 if ($GLOBALS['action'] != 'browse')
553 _dotoken('ROBOTS_META',
554 Element('meta', array('name' => 'robots',
555 'content' => 'noindex, nofollow')),
558 _dotoken('ROBOTS_META', '', $page);
561 // invalid for messages (search results, error messages)
562 if ($template != 'MESSAGE') {
563 $browse_page = WikiURL($name);
564 _dotoken('BROWSE_PAGE', $browse_page, $page);
566 $arg_sep = strstr($browse_page, '?') ? '&' : '?';
567 _dotoken('ACTION', $browse_page . $arg_sep . "action=", $page);
569 _dotoken('PAGEURL', rawurlencode($name), $page);
570 if (!empty($hash['lastmodified']))
571 _dotoken('LASTMODIFIED',
572 strftime($datetimeformat, $hash['lastmodified']), $page);
573 if (!empty($hash['author']))
574 _dotoken('LASTAUTHOR', $hash['author'], $page);
575 if (!empty($hash['version']))
576 _dotoken('VERSION', $hash['version'], $page);
577 if (!empty($hash['pagename']))
578 _dotoken('CURRENT_VERSION', GetMaxVersionNumber($dbi, $hash['pagename'], $WikiPageStore), $page);
579 if (strstr($page, "$FieldSeparator#HITS$FieldSeparator#")) {
580 _dotoken('HITS', GetHitCount($dbi, $name), $page);
582 if (strstr($page, "$FieldSeparator#RELATEDPAGES$FieldSeparator#")) {
583 _dotoken('RELATEDPAGES', LinkRelatedPages($dbi, $name), $page);
587 _dotoken('CONTENT', $content, $page);
591 function UpdateRecentChanges($dbi, $pagename, $isnewpage)
595 global $WikiPageStore;
597 $recentchanges = RetrievePage($dbi, gettext ("RecentChanges"), $WikiPageStore, 0);
599 // this shouldn't be necessary, since PhpWiki loads
600 // default pages if this is a new baby Wiki
602 $today = strftime($dateformat, $now);
604 if (is_array($recentchanges)) {
605 $isNewDay = strftime($dateformat, $recentchanges['lastmodified']) != $today;
608 $recentchanges = array('version' => 1,
610 'flags' => FLAG_PAGE_LOCKED,
611 'author' => $GLOBALS['user']->id());
612 $recentchanges['content']
613 = array(gettext("The most recently changed pages are listed below."),
615 "____$today " . gettext("(first day for this Wiki)"),
617 gettext("Quick title search:"),
618 '[phpwiki:?action=search&searchterm=()]',
622 $recentchanges['lastmodified'] = $now;
624 $numlines = sizeof($recentchanges['content']);
628 // scroll through the page to the first date and break
629 // dates are marked with "____" at the beginning of the line
630 for ($i = 0; $i < $numlines; $i++) {
631 if (preg_match("/^____/",
632 $recentchanges['content'][$i])) {
635 $newpage[$k++] = $recentchanges['content'][$i];
639 // if it's a new date, insert it
640 $newpage[$k++] = $isNewDay ? "____$today\r"
641 : $recentchanges['content'][$i++];
643 $userid = $user->id();
645 // add the updated page's name to the array
647 $newpage[$k++] = "* [$pagename] (new) ..... $userid\r";
649 $diffurl = "phpwiki:" . rawurlencode($pagename) . "?action=diff";
650 $newpage[$k++] = "* [$pagename] ([diff|$diffurl]) ..... $userid\r";
653 $newpage[$k++] = "\r";
655 // copy the rest of the page into the new array
656 // and skip previous entry for $pagename
657 $pagename = preg_quote($pagename);
658 for (; $i < $numlines; $i++) {
659 if (!preg_match("|\[$pagename\]|", $recentchanges['content'][$i])) {
660 $newpage[$k++] = $recentchanges['content'][$i];
664 $recentchanges['content'] = $newpage;
666 ReplaceCurrentPage(gettext ("RecentChanges"), $recentchanges);
672 // c-file-style: "ellemtel"