]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/stdlib.php
Some fixes to clean-up generated HTML.
[SourceForge/phpwiki.git] / lib / stdlib.php
1 <?php rcs_id('$Id: stdlib.php,v 1.21.2.2 2001-03-02 03:48:47 dairiki Exp $');
2
3    /*
4       Standard functions for Wiki functionality
5          ExitWiki($errormsg)
6          LinkExistingWikiWord($wikiword, $linktext) 
7          LinkUnknownWikiWord($wikiword, $linktext) 
8          LinkURL($url, $linktext)
9          LinkImage($url, $alt)
10          RenderQuickSearch($value)
11          RenderFullSearch($value)
12          RenderMostPopular()
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)
21    */
22
23
24    function ExitWiki($errormsg)
25    {
26       static $exitwiki = 0;
27       global $dbi;
28
29       if($exitwiki)             // just in case CloseDataBase calls us
30          exit();
31       $exitwiki = 1;
32
33       CloseDataBase($dbi);
34
35       if($errormsg <> '') {
36          print "<P><hr noshade><h2>" . gettext("WikiFatalError") . "</h2>\n";
37          print $errormsg;
38          print "\n</BODY></HTML>";
39       }
40       exit;
41    }
42
43
44    function LinkExistingWikiWord($wikiword, $linktext='') {
45       global $ScriptUrl;
46       $enc_word = rawurlencode($wikiword);
47       if(empty($linktext))
48          $linktext = htmlspecialchars($wikiword);
49       return "<a href=\"$ScriptUrl?$enc_word\">$linktext</a>";
50    }
51
52    function LinkUnknownWikiWord($wikiword, $linktext='') {
53       global $ScriptUrl;
54       $enc_word = rawurlencode($wikiword);
55       if(empty($linktext))
56          $linktext = htmlspecialchars($wikiword);
57       return "<u>$linktext</u><a href=\"$ScriptUrl?edit=$enc_word\">?</a>";
58    }
59
60    function LinkURL($url, $linktext='') {
61       global $ScriptUrl;
62       if(ereg("[<>\"]", $url)) {
63          return "<b><u>BAD URL -- remove all of &lt;, &gt;, &quot;</u></b>";
64       }
65       if(empty($linktext))
66          $linktext = htmlspecialchars($url);
67       return "<a href=\"$url\">$linktext</a>";
68    }
69
70    function LinkImage($url, $alt='[External Image]') {
71       global $ScriptUrl;
72       if(ereg('[<>"]', $url)) {
73          return "<b><u>BAD URL -- remove all of &lt;, &gt;, &quot;</u></b>";
74       }
75       return "<img src=\"$url\" ALT=\"$alt\">";
76    }
77
78    
79    function RenderQuickSearch($value = '') {
80       global $ScriptUrl;
81       return "<form action=\"$ScriptUrl\">\n" .
82              "<input type=text size=30 name=search value=\"$value\">\n" .
83              "<input type=submit value=\"". gettext("Search") .
84              "\"></form>\n";
85    }
86
87    function RenderFullSearch($value = '') {
88       global $ScriptUrl;
89       return "<form action=\"$ScriptUrl\">\n" .
90              "<input type=text size=30 name=full value=\"$value\">\n" .
91              "<input type=submit value=\"". gettext("Search") .
92              "\"></form>\n";
93    }
94
95    function RenderMostPopular() {
96       global $ScriptUrl, $dbi;
97       
98       $query = InitMostPopular($dbi, MOST_POPULAR_LIST_LENGTH);
99       $result = "<DL>\n";
100       while ($qhash = MostPopularNextMatch($dbi, $query)) {
101          $result .= "<DD>$qhash[hits] ... " . LinkExistingWikiWord($qhash['pagename']) . "\n";
102       }
103       $result .= "</DL>\n";
104       
105       return $result;
106    }
107
108
109    function ParseAdminTokens($line) {
110       global $ScriptUrl;
111       
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") . "\">"
117                 ."</FORM>";
118          $line = str_replace($matches[0], $form, $line);
119       }
120       return $line;
121    }
122
123    // converts spaces to tabs
124    function CookSpaces($pagearray) {
125       return preg_replace("/ {3,8}/", "\t", $pagearray);
126    }
127
128
129    class Stack {
130       var $items = array();
131       var $size = 0;
132
133       function push($item) {
134          $this->items[$this->size] = $item;
135          $this->size++;
136          return true;
137       }  
138    
139       function pop() {
140          if ($this->size == 0) {
141             return false; // stack is empty
142          }  
143          $this->size--;
144          return $this->items[$this->size];
145       }  
146    
147       function cnt() {
148          return $this->size;
149       }  
150
151       function top() {
152          if($this->size)
153             return $this->items[$this->size - 1];
154          else
155             return '';
156       }  
157
158    }  
159    // end class definition
160
161
162    // I couldn't move this to lib/config.php because it wasn't declared yet.
163    $stack = new Stack;
164
165    /* 
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.
170
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
176    */
177
178    function SetHTMLOutputMode($tag, $tagtype, $level)
179    {
180       global $stack;
181       $retvar = '';
182
183       if ($tagtype == ZERO_LEVEL) {
184          // empty the stack until $level == 0;
185          if ($tag == $stack->top()) {
186             return; // same tag? -> nothing to do
187          }
188          while ($stack->cnt() > 0) {
189             $closetag = $stack->pop();
190             $retvar .= "</$closetag>\n";
191          }
192    
193          if ($tag) {
194             $retvar .= "<$tag>\n";
195             $stack->push($tag);
196          }
197
198
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";
207                   break;
208                }
209                $retvar .= "</$closetag>\n";
210             }
211
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";
217                $stack->push($tag);
218             }
219    
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()))
225             {
226                $closetag = $stack->pop();
227                $retvar .= "</$closetag>";
228             }
229
230             // we add the diff to the stack
231             // stack might be zero
232             while ($stack->cnt() < $level) {
233                $retvar .= "<$tag>\n";
234                $stack->push($tag);
235                if ($stack->cnt() > 10) {
236                   // arbitrarily limit tag nesting
237                   ExitWiki(gettext ("Stack bounds exceeded in SetHTMLOutputMode"));
238                }
239             }
240    
241          } else { // $level == $stack->cnt()
242             if ($tag == $stack->top()) {
243                return; // same tag? -> nothing to do
244             } else {
245                // different tag - close old one, add new one
246                $closetag = $stack->pop();
247                $retvar .= "</$closetag>\n";
248                $retvar .= "<$tag>\n";
249                $stack->push($tag);
250             }
251          }
252
253    
254       } else { // unknown $tagtype
255          ExitWiki ("Passed bad tag type value in SetHTMLOutputMode");
256       }
257
258       return $retvar;
259    }
260    // end SetHTMLOutputMode
261
262
263
264    function ParseAndLink($bracketlink) {
265       global $dbi, $ScriptUrl, $AllowedProtocols, $InlineImages;
266
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.
269
270       // strip brackets and leading space
271       preg_match("/(\[\s*)(.+?)(\s*\])/", $bracketlink, $match);
272       // match the contents 
273       preg_match("/([^|]+)(\|)?([^|]+)?/", $match[2], $matches);
274
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]));
279          $linktype = 'named';
280       } else {
281          // unnamed link of the form "[http://blippy.com/] or [wiki page]"
282          $URL = trim($matches[1]);
283          $linkname = '';
284          $linktype = 'simple';
285       }
286
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);
295          } else {
296             $link['type'] = "url-$linktype";
297             $link['link'] = LinkURL($URL, $linkname);
298          }
299       } elseif (preg_match("#^phpwiki:(.*)#", $URL, $match)) {
300          $link['type'] = "url-wiki-$linktype";
301          if(empty($linkname))
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;
307       } else {
308          $link['type'] = "wiki-unknown-$linktype";
309          $link['link'] = LinkUnknownWikiWord($URL, $linkname);
310       }
311
312       return $link;
313    }
314
315
316    function ExtractWikiPageLinks($content)
317    {
318       global $WikiNameRegexp;
319
320       $wikilinks = array();
321       $numlines = count($content);
322       for($l = 0; $l < $numlines; $l++)
323       {
324          // remove escaped '['
325          $line = str_replace('[[', ' ', $content[$l]);
326
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;
333
334             $brktlink = preg_quote($brktlinks[0][$i]);
335             $line = preg_replace("|$brktlink|", '', $line);
336          }
337
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;
343             }
344          }
345       }
346       return $wikilinks;
347    }      
348
349
350    function LinkRelatedPages($dbi, $pagename)
351    {
352       // currently not supported everywhere
353       if(!function_exists('GetWikiPageLinks'))
354          return '';
355
356       $links = GetWikiPageLinks($dbi, $pagename);
357
358       $txt = "<b>";
359       $txt .= sprintf (gettext ("%d best incoming links:"), NUM_RELATED_PAGES);
360       $txt .= "</b>\n";
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), ";
365          }
366       }
367
368       $txt .= "\n<br><b>";
369       $txt .= sprintf (gettext ("%d best outgoing links:"), NUM_RELATED_PAGES);
370       $txt .= "</b>\n";
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), ";
376          }
377       }
378
379       $txt .= "\n<br><b>";
380       $txt .= sprintf (gettext ("%d most popular nearby:"), NUM_RELATED_PAGES);
381       $txt .= "</b>\n";
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), ";
386          }
387       }
388       
389       return $txt;
390    }
391
392    
393    # GeneratePage() -- takes $content and puts it in the template $template
394    # this function contains all the template logic
395    #
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.
401
402    function GeneratePage($template, $content, $name, $hash)
403    {
404       global $ScriptUrl, $AllowedProtocols, $templates;
405       global $datetimeformat, $dbi, $logo, $FieldSeparator;
406
407       if (!is_array($hash))
408          unset($hash);
409
410       function _dotoken ($id, $val, &$page) {
411          global $FieldSeparator;
412          $page = str_replace("$FieldSeparator#$id$FieldSeparator#",
413                                 $val, $page);
414       }
415
416       function _iftoken ($id, $condition, &$page) {
417          global $FieldSeparator;
418
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#";
427
428          if ($condition) {
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);
434          } else {
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);
440          }
441       }
442
443       $page = join('', file($templates[$template]));
444       $page = str_replace('###', "$FieldSeparator#", $page);
445
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);
451
452       _dotoken('SCRIPTURL', $ScriptUrl, $page);
453       _dotoken('PAGE', htmlspecialchars($name), $page);
454       _dotoken('ALLOWEDPROTOCOLS', $AllowedProtocols, $page);
455       _dotoken('LOGO', $logo, $page);
456       
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);
466          }
467          if (strstr($page, "$FieldSeparator#RELATEDPAGES$FieldSeparator#")) {
468             _dotoken('RELATEDPAGES', LinkRelatedPages($dbi, $name), $page);
469          }
470       }
471
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);
477          }
478       }
479
480       _dotoken('CONTENT', $content, $page);
481       print $page;
482    }
483 ?>