1 <?php rcs_id('$Id: stdlib.php,v 1.21.2.3 2001-03-02 21:26:03 dairiki Exp $');
4 Standard functions for Wiki functionality
6 LinkExistingWikiWord($wikiword, $linktext)
7 LinkUnknownWikiWord($wikiword, $linktext)
8 LinkURL($url, $linktext)
10 RenderQuickSearch($value)
11 RenderFullSearch($value)
13 CookSpaces($pagearray)
14 class Stack (push(), pop(), cnt(), top())
15 SetHTMLOutputMode($newmode, $depth)
16 UpdateRecentChanges($dbi, $pagename, $isnewpage)
17 ParseAndLink($bracketlink)
18 ExtractWikiPageLinks($content)
19 LinkRelatedPages($dbi, $pagename)
20 GeneratePage($template, $content, $name, $hash)
24 function ExitWiki($errormsg)
29 if($exitwiki) // just in case CloseDataBase calls us
36 print "<P><hr noshade><h2>" . gettext("WikiFatalError") . "</h2>\n";
38 print "\n</BODY></HTML>";
44 function LinkExistingWikiWord($wikiword, $linktext='') {
46 $enc_word = rawurlencode($wikiword);
48 $linktext = htmlspecialchars($wikiword);
49 return "<a href=\"$ScriptUrl?$enc_word\">$linktext</a>";
52 function LinkUnknownWikiWord($wikiword, $linktext='') {
54 $enc_word = rawurlencode($wikiword);
56 $linktext = htmlspecialchars($wikiword);
57 return "<u>$linktext</u><a href=\"$ScriptUrl?edit=$enc_word\">?</a>";
60 function LinkURL($url, $linktext='') {
62 if(ereg("[<>\"]", $url)) {
63 return "<b><u>BAD URL -- remove all of <, >, "</u></b>";
66 $linktext = htmlspecialchars($url);
67 return "<a href=\"$url\">$linktext</a>";
70 function LinkImage($url, $alt='[External Image]') {
72 if(ereg('[<>"]', $url)) {
73 return "<b><u>BAD URL -- remove all of <, >, "</u></b>";
75 return "<img src=\"$url\" ALT=\"$alt\">";
79 function RenderQuickSearch($value = '') {
81 return "<form action=\"$ScriptUrl\">\n" .
82 "<input type=text size=30 name=search value=\"$value\">\n" .
83 "<input type=submit value=\"". gettext("Search") .
87 function RenderFullSearch($value = '') {
89 return "<form action=\"$ScriptUrl\">\n" .
90 "<input type=text size=30 name=full value=\"$value\">\n" .
91 "<input type=submit value=\"". gettext("Search") .
95 function RenderMostPopular() {
96 global $ScriptUrl, $dbi;
98 $query = InitMostPopular($dbi, MOST_POPULAR_LIST_LENGTH);
100 while ($qhash = MostPopularNextMatch($dbi, $query)) {
101 $result .= "<DD>$qhash[hits] ... " . LinkExistingWikiWord($qhash['pagename']) . "\n";
103 $result .= "</DL>\n";
109 function ParseAdminTokens($line) {
112 while (preg_match("/%%ADMIN-INPUT-(.*?)-(\w+)%%/", $line, $matches)) {
113 $head = str_replace('_', ' ', $matches[2]);
114 $form = "<FORM ACTION=\"$ScriptUrl\" METHOD=POST>"
115 ."$head: <INPUT NAME=$matches[1] SIZE=20> "
116 ."<INPUT TYPE=SUBMIT VALUE=\"" . gettext("Go") . "\">"
118 $line = str_replace($matches[0], $form, $line);
123 // converts spaces to tabs
124 function CookSpaces($pagearray) {
125 return preg_replace("/ {3,8}/", "\t", $pagearray);
130 var $items = array();
133 function push($item) {
134 $this->items[$this->size] = $item;
140 if ($this->size == 0) {
141 return false; // stack is empty
144 return $this->items[$this->size];
153 return $this->items[$this->size - 1];
159 // end class definition
162 // I couldn't move this to lib/config.php because it wasn't declared yet.
166 Wiki HTML output can, at any given time, be in only one mode.
167 It will be something like Unordered List, Preformatted Text,
168 plain text etc. When we change modes we have to issue close tags
169 for one mode and start tags for another.
171 $tag ... HTML tag to insert
172 $tagtype ... ZERO_LEVEL - close all open tags before inserting $tag
173 NESTED_LEVEL - close tags until depths match
174 $level ... nesting level (depth) of $tag
175 nesting is arbitrary limited to 10 levels
178 function SetHTMLOutputMode($tag, $tagtype, $level)
184 // arbitrarily limit tag nesting
185 ExitWiki(gettext ("Nesting depth exceeded in SetHTMLOutputMode"));
188 if ($tagtype == ZERO_LEVEL) {
189 // empty the stack until $level == 0;
190 if ($tag == $stack->top()) {
191 return; // same tag? -> nothing to do
193 while ($stack->cnt() > 0) {
194 $closetag = $stack->pop();
195 $retvar .= "</$closetag>\n";
199 $retvar .= "<$tag>\n";
204 } elseif ($tagtype == NESTED_LEVEL) {
205 if ($level < $stack->cnt()) {
206 // $tag has fewer nestings (old: tabs) than stack,
207 // reduce stack to that tab count
208 while ($stack->cnt() > $level) {
209 $closetag = $stack->pop();
210 if ($closetag == false) {
211 //echo "bounds error in tag stack";
214 $retvar .= "</$closetag>\n";
217 // if list type isn't the same,
218 // back up one more and push new tag
219 if ($tag != $stack->top()) {
220 $closetag = $stack->pop();
221 $retvar .= "</$closetag><$tag>\n";
225 } elseif ($level > $stack->cnt()) {
226 // Test for and close top level elements which are not allowed to contain
227 // other block-level elements.
228 if ($stack->cnt() == 1 and
229 preg_match('/^(p|pre|h\d)$/i', $stack->top()))
231 $closetag = $stack->pop();
232 $retvar .= "</$closetag>";
235 // we add the diff to the stack
236 // stack might be zero
237 if ($stack->cnt() < $level) {
238 while ($stack->cnt() < $level - 1) {
239 // This is a bit of a hack:
241 // We're not nested deep enough, and have to make up
242 // some kind of block element to nest within.
244 // Currently, this can only happen for nested list
245 // element (either <ul> <ol> or <dl>). What we used
246 // to do here is to open extra lists of whatever
247 // type was requested. This would result in invalid
248 // HTML, since and list is not allowed to contain
249 // another list without first containing a list
250 // item. ("<ul><ul><li>Item</ul></ul>" is invalid.)
252 // So now, when we need extra list elements, we use
253 // a <dl>, and open it with an empty <dd>.
255 $retvar .= "<dl><dd>";
259 $retvar .= "<$tag>\n";
263 } else { // $level == $stack->cnt()
264 if ($tag == $stack->top()) {
265 return; // same tag? -> nothing to do
267 // different tag - close old one, add new one
268 $closetag = $stack->pop();
269 $retvar .= "</$closetag>\n";
270 $retvar .= "<$tag>\n";
276 } else { // unknown $tagtype
277 ExitWiki ("Passed bad tag type value in SetHTMLOutputMode");
282 // end SetHTMLOutputMode
286 function ParseAndLink($bracketlink) {
287 global $dbi, $ScriptUrl, $AllowedProtocols, $InlineImages;
289 // $bracketlink will start and end with brackets; in between
290 // will be either a page name, a URL or both separated by a pipe.
292 // strip brackets and leading space
293 preg_match("/(\[\s*)(.+?)(\s*\])/", $bracketlink, $match);
294 // match the contents
295 preg_match("/([^|]+)(\|)?([^|]+)?/", $match[2], $matches);
297 if (isset($matches[3])) {
298 // named link of the form "[some link name | http://blippy.com/]"
299 $URL = trim($matches[3]);
300 $linkname = htmlspecialchars(trim($matches[1]));
303 // unnamed link of the form "[http://blippy.com/] or [wiki page]"
304 $URL = trim($matches[1]);
306 $linktype = 'simple';
309 if (IsWikiPage($dbi, $URL)) {
310 $link['type'] = "wiki-$linktype";
311 $link['link'] = LinkExistingWikiWord($URL, $linkname);
312 } elseif (preg_match("#^($AllowedProtocols):#", $URL)) {
313 // if it's an image, embed it; otherwise, it's a regular link
314 if (preg_match("/($InlineImages)$/i", $URL)) {
315 $link['type'] = "image-$linktype";
316 $link['link'] = LinkImage($URL, $linkname);
318 $link['type'] = "url-$linktype";
319 $link['link'] = LinkURL($URL, $linkname);
321 } elseif (preg_match("#^phpwiki:(.*)#", $URL, $match)) {
322 $link['type'] = "url-wiki-$linktype";
324 $linkname = htmlspecialchars($URL);
325 $link['link'] = "<a href=\"$ScriptUrl$match[1]\">$linkname</a>";
326 } elseif (preg_match("#^\d+$#", $URL)) {
327 $link['type'] = "reference-$linktype";
328 $link['link'] = $URL;
330 $link['type'] = "wiki-unknown-$linktype";
331 $link['link'] = LinkUnknownWikiWord($URL, $linkname);
338 function ExtractWikiPageLinks($content)
340 global $WikiNameRegexp;
342 $wikilinks = array();
343 $numlines = count($content);
344 for($l = 0; $l < $numlines; $l++)
346 // remove escaped '['
347 $line = str_replace('[[', ' ', $content[$l]);
349 // bracket links (only type wiki-* is of interest)
350 $numBracketLinks = preg_match_all("/\[\s*([^\]|]+\|)?\s*(.+?)\s*\]/", $line, $brktlinks);
351 for ($i = 0; $i < $numBracketLinks; $i++) {
352 $link = ParseAndLink($brktlinks[0][$i]);
353 if (preg_match("#^wiki#", $link['type']))
354 $wikilinks[$brktlinks[2][$i]] = 1;
356 $brktlink = preg_quote($brktlinks[0][$i]);
357 $line = preg_replace("|$brktlink|", '', $line);
360 // BumpyText old-style wiki links
361 if (preg_match_all("/!?$WikiNameRegexp/", $line, $link)) {
362 for ($i = 0; isset($link[0][$i]); $i++) {
363 if($link[0][$i][0] <> '!')
364 $wikilinks[$link[0][$i]] = 1;
372 function LinkRelatedPages($dbi, $pagename)
374 // currently not supported everywhere
375 if(!function_exists('GetWikiPageLinks'))
378 $links = GetWikiPageLinks($dbi, $pagename);
381 $txt .= sprintf (gettext ("%d best incoming links:"), NUM_RELATED_PAGES);
383 for($i = 0; $i < NUM_RELATED_PAGES; $i++) {
384 if(isset($links['in'][$i])) {
385 list($name, $score) = $links['in'][$i];
386 $txt .= LinkExistingWikiWord($name) . " ($score), ";
391 $txt .= sprintf (gettext ("%d best outgoing links:"), NUM_RELATED_PAGES);
393 for($i = 0; $i < NUM_RELATED_PAGES; $i++) {
394 if(isset($links['out'][$i])) {
395 list($name, $score) = $links['out'][$i];
396 if(IsWikiPage($dbi, $name))
397 $txt .= LinkExistingWikiWord($name) . " ($score), ";
402 $txt .= sprintf (gettext ("%d most popular nearby:"), NUM_RELATED_PAGES);
404 for($i = 0; $i < NUM_RELATED_PAGES; $i++) {
405 if(isset($links['popular'][$i])) {
406 list($name, $score) = $links['popular'][$i];
407 $txt .= LinkExistingWikiWord($name) . " ($score), ";
415 # GeneratePage() -- takes $content and puts it in the template $template
416 # this function contains all the template logic
418 # $template ... name of the template (see config.php for list of names)
419 # $content ... html content to put into the page
420 # $name ... page title
421 # $hash ... if called while creating a wiki page, $hash points to
422 # the $pagehash array of that wiki page.
424 function GeneratePage($template, $content, $name, $hash)
426 global $ScriptUrl, $AllowedProtocols, $templates;
427 global $datetimeformat, $dbi, $logo, $FieldSeparator;
429 if (!is_array($hash))
432 function _dotoken ($id, $val, &$page) {
433 global $FieldSeparator;
434 $page = str_replace("$FieldSeparator#$id$FieldSeparator#",
438 function _iftoken ($id, $condition, &$page) {
439 global $FieldSeparator;
441 // line based IF directive
442 $lineyes = "$FieldSeparator#IF $id$FieldSeparator#";
443 $lineno = "$FieldSeparator#IF !$id$FieldSeparator#";
444 // block based IF directive
445 $blockyes = "$FieldSeparator#IF:$id$FieldSeparator#";
446 $blockyesend = "$FieldSeparator#ENDIF:$id$FieldSeparator#";
447 $blockno = "$FieldSeparator#IF:!$id$FieldSeparator#";
448 $blocknoend = "$FieldSeparator#ENDIF:!$id$FieldSeparator#";
451 $page = str_replace($lineyes, '', $page);
452 $page = str_replace($blockyes, '', $page);
453 $page = str_replace($blockyesend, '', $page);
454 $page = preg_replace("/$blockno(.*?)$blocknoend/s", '', $page);
455 $page = ereg_replace("${lineno}[^\n]*\n", '', $page);
457 $page = str_replace($lineno, '', $page);
458 $page = str_replace($blockno, '', $page);
459 $page = str_replace($blocknoend, '', $page);
460 $page = preg_replace("/$blockyes(.*?)$blockyesend/s", '', $page);
461 $page = ereg_replace("${lineyes}[^\n]*\n", '', $page);
465 $page = join('', file($templates[$template]));
466 $page = str_replace('###', "$FieldSeparator#", $page);
468 // valid for all pagetypes
469 _iftoken('COPY', isset($hash['copy']), $page);
470 _iftoken('LOCK', (isset($hash['flags']) &&
471 ($hash['flags'] & FLAG_PAGE_LOCKED)), $page);
472 _iftoken('ADMIN', defined('WIKI_ADMIN'), $page);
474 _dotoken('SCRIPTURL', $ScriptUrl, $page);
475 _dotoken('PAGE', htmlspecialchars($name), $page);
476 _dotoken('ALLOWEDPROTOCOLS', $AllowedProtocols, $page);
477 _dotoken('LOGO', $logo, $page);
479 // invalid for messages (search results, error messages)
480 if ($template != 'MESSAGE') {
481 _dotoken('PAGEURL', rawurlencode($name), $page);
482 _dotoken('LASTMODIFIED',
483 date($datetimeformat, $hash['lastmodified']), $page);
484 _dotoken('LASTAUTHOR', $hash['author'], $page);
485 _dotoken('VERSION', $hash['version'], $page);
486 if (strstr($page, "$FieldSeparator#HITS$FieldSeparator#")) {
487 _dotoken('HITS', GetHitCount($dbi, $name), $page);
489 if (strstr($page, "$FieldSeparator#RELATEDPAGES$FieldSeparator#")) {
490 _dotoken('RELATEDPAGES', LinkRelatedPages($dbi, $name), $page);
494 // valid only for EditLinks
495 if ($template == 'EDITLINKS') {
496 for ($i = 1; $i <= NUM_LINKS; $i++) {
497 $ref = isset($hash['refs'][$i]) ? $hash['refs'][$i] : '';
498 _dotoken("R$i", $ref, $page);
502 _dotoken('CONTENT', $content, $page);