1 <?php rcs_id('$Id: stdlib.php,v 1.21.2.2 2001-03-02 03:48:47 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)
183 if ($tagtype == ZERO_LEVEL) {
184 // empty the stack until $level == 0;
185 if ($tag == $stack->top()) {
186 return; // same tag? -> nothing to do
188 while ($stack->cnt() > 0) {
189 $closetag = $stack->pop();
190 $retvar .= "</$closetag>\n";
194 $retvar .= "<$tag>\n";
199 } elseif ($tagtype == NESTED_LEVEL) {
200 if ($level < $stack->cnt()) {
201 // $tag has fewer nestings (old: tabs) than stack,
202 // reduce stack to that tab count
203 while ($stack->cnt() > $level) {
204 $closetag = $stack->pop();
205 if ($closetag == false) {
206 //echo "bounds error in tag stack";
209 $retvar .= "</$closetag>\n";
212 // if list type isn't the same,
213 // back up one more and push new tag
214 if ($tag != $stack->top()) {
215 $closetag = $stack->pop();
216 $retvar .= "</$closetag><$tag>\n";
220 } elseif ($level > $stack->cnt()) {
221 // Test for and close top level elements which are not allowed to contain
222 // other block-level elements.
223 if ($stack->cnt() == 1 and
224 preg_match('/^(p|pre|h\d)$/i', $stack->top()))
226 $closetag = $stack->pop();
227 $retvar .= "</$closetag>";
230 // we add the diff to the stack
231 // stack might be zero
232 while ($stack->cnt() < $level) {
233 $retvar .= "<$tag>\n";
235 if ($stack->cnt() > 10) {
236 // arbitrarily limit tag nesting
237 ExitWiki(gettext ("Stack bounds exceeded in SetHTMLOutputMode"));
241 } else { // $level == $stack->cnt()
242 if ($tag == $stack->top()) {
243 return; // same tag? -> nothing to do
245 // different tag - close old one, add new one
246 $closetag = $stack->pop();
247 $retvar .= "</$closetag>\n";
248 $retvar .= "<$tag>\n";
254 } else { // unknown $tagtype
255 ExitWiki ("Passed bad tag type value in SetHTMLOutputMode");
260 // end SetHTMLOutputMode
264 function ParseAndLink($bracketlink) {
265 global $dbi, $ScriptUrl, $AllowedProtocols, $InlineImages;
267 // $bracketlink will start and end with brackets; in between
268 // will be either a page name, a URL or both separated by a pipe.
270 // strip brackets and leading space
271 preg_match("/(\[\s*)(.+?)(\s*\])/", $bracketlink, $match);
272 // match the contents
273 preg_match("/([^|]+)(\|)?([^|]+)?/", $match[2], $matches);
275 if (isset($matches[3])) {
276 // named link of the form "[some link name | http://blippy.com/]"
277 $URL = trim($matches[3]);
278 $linkname = htmlspecialchars(trim($matches[1]));
281 // unnamed link of the form "[http://blippy.com/] or [wiki page]"
282 $URL = trim($matches[1]);
284 $linktype = 'simple';
287 if (IsWikiPage($dbi, $URL)) {
288 $link['type'] = "wiki-$linktype";
289 $link['link'] = LinkExistingWikiWord($URL, $linkname);
290 } elseif (preg_match("#^($AllowedProtocols):#", $URL)) {
291 // if it's an image, embed it; otherwise, it's a regular link
292 if (preg_match("/($InlineImages)$/i", $URL)) {
293 $link['type'] = "image-$linktype";
294 $link['link'] = LinkImage($URL, $linkname);
296 $link['type'] = "url-$linktype";
297 $link['link'] = LinkURL($URL, $linkname);
299 } elseif (preg_match("#^phpwiki:(.*)#", $URL, $match)) {
300 $link['type'] = "url-wiki-$linktype";
302 $linkname = htmlspecialchars($URL);
303 $link['link'] = "<a href=\"$ScriptUrl$match[1]\">$linkname</a>";
304 } elseif (preg_match("#^\d+$#", $URL)) {
305 $link['type'] = "reference-$linktype";
306 $link['link'] = $URL;
308 $link['type'] = "wiki-unknown-$linktype";
309 $link['link'] = LinkUnknownWikiWord($URL, $linkname);
316 function ExtractWikiPageLinks($content)
318 global $WikiNameRegexp;
320 $wikilinks = array();
321 $numlines = count($content);
322 for($l = 0; $l < $numlines; $l++)
324 // remove escaped '['
325 $line = str_replace('[[', ' ', $content[$l]);
327 // bracket links (only type wiki-* is of interest)
328 $numBracketLinks = preg_match_all("/\[\s*([^\]|]+\|)?\s*(.+?)\s*\]/", $line, $brktlinks);
329 for ($i = 0; $i < $numBracketLinks; $i++) {
330 $link = ParseAndLink($brktlinks[0][$i]);
331 if (preg_match("#^wiki#", $link['type']))
332 $wikilinks[$brktlinks[2][$i]] = 1;
334 $brktlink = preg_quote($brktlinks[0][$i]);
335 $line = preg_replace("|$brktlink|", '', $line);
338 // BumpyText old-style wiki links
339 if (preg_match_all("/!?$WikiNameRegexp/", $line, $link)) {
340 for ($i = 0; isset($link[0][$i]); $i++) {
341 if($link[0][$i][0] <> '!')
342 $wikilinks[$link[0][$i]] = 1;
350 function LinkRelatedPages($dbi, $pagename)
352 // currently not supported everywhere
353 if(!function_exists('GetWikiPageLinks'))
356 $links = GetWikiPageLinks($dbi, $pagename);
359 $txt .= sprintf (gettext ("%d best incoming links:"), NUM_RELATED_PAGES);
361 for($i = 0; $i < NUM_RELATED_PAGES; $i++) {
362 if(isset($links['in'][$i])) {
363 list($name, $score) = $links['in'][$i];
364 $txt .= LinkExistingWikiWord($name) . " ($score), ";
369 $txt .= sprintf (gettext ("%d best outgoing links:"), NUM_RELATED_PAGES);
371 for($i = 0; $i < NUM_RELATED_PAGES; $i++) {
372 if(isset($links['out'][$i])) {
373 list($name, $score) = $links['out'][$i];
374 if(IsWikiPage($dbi, $name))
375 $txt .= LinkExistingWikiWord($name) . " ($score), ";
380 $txt .= sprintf (gettext ("%d most popular nearby:"), NUM_RELATED_PAGES);
382 for($i = 0; $i < NUM_RELATED_PAGES; $i++) {
383 if(isset($links['popular'][$i])) {
384 list($name, $score) = $links['popular'][$i];
385 $txt .= LinkExistingWikiWord($name) . " ($score), ";
393 # GeneratePage() -- takes $content and puts it in the template $template
394 # this function contains all the template logic
396 # $template ... name of the template (see config.php for list of names)
397 # $content ... html content to put into the page
398 # $name ... page title
399 # $hash ... if called while creating a wiki page, $hash points to
400 # the $pagehash array of that wiki page.
402 function GeneratePage($template, $content, $name, $hash)
404 global $ScriptUrl, $AllowedProtocols, $templates;
405 global $datetimeformat, $dbi, $logo, $FieldSeparator;
407 if (!is_array($hash))
410 function _dotoken ($id, $val, &$page) {
411 global $FieldSeparator;
412 $page = str_replace("$FieldSeparator#$id$FieldSeparator#",
416 function _iftoken ($id, $condition, &$page) {
417 global $FieldSeparator;
419 // line based IF directive
420 $lineyes = "$FieldSeparator#IF $id$FieldSeparator#";
421 $lineno = "$FieldSeparator#IF !$id$FieldSeparator#";
422 // block based IF directive
423 $blockyes = "$FieldSeparator#IF:$id$FieldSeparator#";
424 $blockyesend = "$FieldSeparator#ENDIF:$id$FieldSeparator#";
425 $blockno = "$FieldSeparator#IF:!$id$FieldSeparator#";
426 $blocknoend = "$FieldSeparator#ENDIF:!$id$FieldSeparator#";
429 $page = str_replace($lineyes, '', $page);
430 $page = str_replace($blockyes, '', $page);
431 $page = str_replace($blockyesend, '', $page);
432 $page = preg_replace("/$blockno(.*?)$blocknoend/s", '', $page);
433 $page = ereg_replace("${lineno}[^\n]*\n", '', $page);
435 $page = str_replace($lineno, '', $page);
436 $page = str_replace($blockno, '', $page);
437 $page = str_replace($blocknoend, '', $page);
438 $page = preg_replace("/$blockyes(.*?)$blockyesend/s", '', $page);
439 $page = ereg_replace("${lineyes}[^\n]*\n", '', $page);
443 $page = join('', file($templates[$template]));
444 $page = str_replace('###', "$FieldSeparator#", $page);
446 // valid for all pagetypes
447 _iftoken('COPY', isset($hash['copy']), $page);
448 _iftoken('LOCK', (isset($hash['flags']) &&
449 ($hash['flags'] & FLAG_PAGE_LOCKED)), $page);
450 _iftoken('ADMIN', defined('WIKI_ADMIN'), $page);
452 _dotoken('SCRIPTURL', $ScriptUrl, $page);
453 _dotoken('PAGE', htmlspecialchars($name), $page);
454 _dotoken('ALLOWEDPROTOCOLS', $AllowedProtocols, $page);
455 _dotoken('LOGO', $logo, $page);
457 // invalid for messages (search results, error messages)
458 if ($template != 'MESSAGE') {
459 _dotoken('PAGEURL', rawurlencode($name), $page);
460 _dotoken('LASTMODIFIED',
461 date($datetimeformat, $hash['lastmodified']), $page);
462 _dotoken('LASTAUTHOR', $hash['author'], $page);
463 _dotoken('VERSION', $hash['version'], $page);
464 if (strstr($page, "$FieldSeparator#HITS$FieldSeparator#")) {
465 _dotoken('HITS', GetHitCount($dbi, $name), $page);
467 if (strstr($page, "$FieldSeparator#RELATEDPAGES$FieldSeparator#")) {
468 _dotoken('RELATEDPAGES', LinkRelatedPages($dbi, $name), $page);
472 // valid only for EditLinks
473 if ($template == 'EDITLINKS') {
474 for ($i = 1; $i <= NUM_LINKS; $i++) {
475 $ref = isset($hash['refs'][$i]) ? $hash['refs'][$i] : '';
476 _dotoken("R$i", $ref, $page);
480 _dotoken('CONTENT', $content, $page);