]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/stdlib.php
ExtractWikiPageLinks now recognizes references of the form [\d+]
[SourceForge/phpwiki.git] / lib / stdlib.php
1 <?php rcs_id('$Id: stdlib.php,v 1.21 2001-01-15 12:32:57 ahollosi Exp $');
2
3    /*
4       Standard functions for Wiki functionality
5          ExitWiki($errormsg)
6          LinkExistingWikiWord($wikiword) 
7          LinkUnknownWikiWord($wikiword) 
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             // we add the diff to the stack
222             // stack might be zero
223             while ($stack->cnt() < $level) {
224                $retvar .= "<$tag>\n";
225                $stack->push($tag);
226                if ($stack->cnt() > 10) {
227                   // arbitrarily limit tag nesting
228                   ExitWiki(gettext ("Stack bounds exceeded in SetHTMLOutputMode"));
229                }
230             }
231    
232          } else { // $level == $stack->cnt()
233             if ($tag == $stack->top()) {
234                return; // same tag? -> nothing to do
235             } else {
236                // different tag - close old one, add new one
237                $closetag = $stack->pop();
238                $retvar .= "</$closetag>\n";
239                $retvar .= "<$tag>\n";
240                $stack->push($tag);
241             }
242          }
243
244    
245       } else { // unknown $tagtype
246          ExitWiki ("Passed bad tag type value in SetHTMLOutputMode");
247       }
248
249       return $retvar;
250    }
251    // end SetHTMLOutputMode
252
253
254
255    function ParseAndLink($bracketlink) {
256       global $dbi, $ScriptUrl, $AllowedProtocols, $InlineImages;
257
258       // $bracketlink will start and end with brackets; in between
259       // will be either a page name, a URL or both separated by a pipe.
260
261       // strip brackets and leading space
262       preg_match("/(\[\s*)(.+?)(\s*\])/", $bracketlink, $match);
263       // match the contents 
264       preg_match("/([^|]+)(\|)?([^|]+)?/", $match[2], $matches);
265
266       if (isset($matches[3])) {
267          // named link of the form  "[some link name | http://blippy.com/]"
268          $URL = trim($matches[3]);
269          $linkname = htmlspecialchars(trim($matches[1]));
270          $linktype = 'named';
271       } else {
272          // unnamed link of the form "[http://blippy.com/] or [wiki page]"
273          $URL = trim($matches[1]);
274          $linkname = '';
275          $linktype = 'simple';
276       }
277
278       if (IsWikiPage($dbi, $URL)) {
279          $link['type'] = "wiki-$linktype";
280          $link['link'] = LinkExistingWikiWord($URL, $linkname);
281       } elseif (preg_match("#^($AllowedProtocols):#", $URL)) {
282         // if it's an image, embed it; otherwise, it's a regular link
283          if (preg_match("/($InlineImages)$/i", $URL)) {
284             $link['type'] = "image-$linktype";
285             $link['link'] = LinkImage($URL, $linkname);
286          } else {
287             $link['type'] = "url-$linktype";
288             $link['link'] = LinkURL($URL, $linkname);
289          }
290       } elseif (preg_match("#^phpwiki:(.*)#", $URL, $match)) {
291          $link['type'] = "url-wiki-$linktype";
292          if(empty($linkname))
293             $linkname = htmlspecialchars($URL);
294          $link['link'] = "<a href=\"$ScriptUrl$match[1]\">$linkname</a>";
295       } elseif (preg_match("#^\d+$#", $URL)) {
296          $link['type'] = "reference-$linktype";
297          $link['link'] = $URL;
298       } else {
299          $link['type'] = "wiki-unknown-$linktype";
300          $link['link'] = LinkUnknownWikiWord($URL, $linkname);
301       }
302
303       return $link;
304    }
305
306
307    function ExtractWikiPageLinks($content)
308    {
309       global $WikiNameRegexp;
310
311       $wikilinks = array();
312       $numlines = count($content);
313       for($l = 0; $l < $numlines; $l++)
314       {
315          // remove escaped '['
316          $line = str_replace('[[', ' ', $content[$l]);
317
318          // bracket links (only type wiki-* is of interest)
319          $numBracketLinks = preg_match_all("/\[\s*([^\]|]+\|)?\s*(.+?)\s*\]/", $line, $brktlinks);
320          for ($i = 0; $i < $numBracketLinks; $i++) {
321             $link = ParseAndLink($brktlinks[0][$i]);
322             if (preg_match("#^wiki#", $link['type']))
323                $wikilinks[$brktlinks[2][$i]] = 1;
324
325             $brktlink = preg_quote($brktlinks[0][$i]);
326             $line = preg_replace("|$brktlink|", '', $line);
327          }
328
329          // BumpyText old-style wiki links
330          if (preg_match_all("/!?$WikiNameRegexp/", $line, $link)) {
331             for ($i = 0; isset($link[0][$i]); $i++) {
332                if($link[0][$i][0] <> '!')
333                   $wikilinks[$link[0][$i]] = 1;
334             }
335          }
336       }
337       return $wikilinks;
338    }      
339
340
341    function LinkRelatedPages($dbi, $pagename)
342    {
343       // currently not supported everywhere
344       if(!function_exists('GetWikiPageLinks'))
345          return '';
346
347       $links = GetWikiPageLinks($dbi, $pagename);
348
349       $txt = "<b>";
350       $txt .= sprintf (gettext ("%d best incoming links:"), NUM_RELATED_PAGES);
351       $txt .= "</b>\n";
352       for($i = 0; $i < NUM_RELATED_PAGES; $i++) {
353          if(isset($links['in'][$i])) {
354             list($name, $score) = $links['in'][$i];
355             $txt .= LinkExistingWikiWord($name) . " ($score), ";
356          }
357       }
358
359       $txt .= "\n<br><b>";
360       $txt .= sprintf (gettext ("%d best outgoing links:"), NUM_RELATED_PAGES);
361       $txt .= "</b>\n";
362       for($i = 0; $i < NUM_RELATED_PAGES; $i++) {
363          if(isset($links['out'][$i])) {
364             list($name, $score) = $links['out'][$i];
365             if(IsWikiPage($dbi, $name))
366                $txt .= LinkExistingWikiWord($name) . " ($score), ";
367          }
368       }
369
370       $txt .= "\n<br><b>";
371       $txt .= sprintf (gettext ("%d most popular nearby:"), NUM_RELATED_PAGES);
372       $txt .= "</b>\n";
373       for($i = 0; $i < NUM_RELATED_PAGES; $i++) {
374          if(isset($links['popular'][$i])) {
375             list($name, $score) = $links['popular'][$i];
376             $txt .= LinkExistingWikiWord($name) . " ($score), ";
377          }
378       }
379       
380       return $txt;
381    }
382
383    
384    # GeneratePage() -- takes $content and puts it in the template $template
385    # this function contains all the template logic
386    #
387    # $template ... name of the template (see config.php for list of names)
388    # $content ... html content to put into the page
389    # $name ... page title
390    # $hash ... if called while creating a wiki page, $hash points to
391    #           the $pagehash array of that wiki page.
392
393    function GeneratePage($template, $content, $name, $hash)
394    {
395       global $ScriptUrl, $AllowedProtocols, $templates;
396       global $datetimeformat, $dbi, $logo, $FieldSeparator;
397
398       if (!is_array($hash))
399          unset($hash);
400
401       function _dotoken ($id, $val, &$page) {
402          global $FieldSeparator;
403          $page = str_replace("$FieldSeparator#$id$FieldSeparator#",
404                                 $val, $page);
405       }
406
407       function _iftoken ($id, $condition, &$page) {
408          global $FieldSeparator;
409
410          // line based IF directive
411          $lineyes = "$FieldSeparator#IF $id$FieldSeparator#";
412          $lineno = "$FieldSeparator#IF !$id$FieldSeparator#";
413          // block based IF directive
414          $blockyes = "$FieldSeparator#IF:$id$FieldSeparator#";
415          $blockyesend = "$FieldSeparator#ENDIF:$id$FieldSeparator#";
416          $blockno = "$FieldSeparator#IF:!$id$FieldSeparator#";
417          $blocknoend = "$FieldSeparator#ENDIF:!$id$FieldSeparator#";
418
419          if ($condition) {
420             $page = str_replace($lineyes, '', $page);
421             $page = str_replace($blockyes, '', $page);
422             $page = str_replace($blockyesend, '', $page);
423             $page = preg_replace("/$blockno(.*?)$blocknoend/s", '', $page);
424             $page = ereg_replace("${lineno}[^\n]*\n", '', $page);
425          } else {
426             $page = str_replace($lineno, '', $page);
427             $page = str_replace($blockno, '', $page);
428             $page = str_replace($blocknoend, '', $page);
429             $page = preg_replace("/$blockyes(.*?)$blockyesend/s", '', $page);
430             $page = ereg_replace("${lineyes}[^\n]*\n", '', $page);
431          }
432       }
433
434       $page = join('', file($templates[$template]));
435       $page = str_replace('###', "$FieldSeparator#", $page);
436
437       // valid for all pagetypes
438       _iftoken('COPY', isset($hash['copy']), $page);
439       _iftoken('LOCK',  (isset($hash['flags']) &&
440                         ($hash['flags'] & FLAG_PAGE_LOCKED)), $page);
441       _iftoken('ADMIN', defined('WIKI_ADMIN'), $page);
442
443       _dotoken('SCRIPTURL', $ScriptUrl, $page);
444       _dotoken('PAGE', htmlspecialchars($name), $page);
445       _dotoken('ALLOWEDPROTOCOLS', $AllowedProtocols, $page);
446       _dotoken('LOGO', $logo, $page);
447       
448       // invalid for messages (search results, error messages)
449       if ($template != 'MESSAGE') {
450          _dotoken('PAGEURL', rawurlencode($name), $page);
451          _dotoken('LASTMODIFIED',
452                         date($datetimeformat, $hash['lastmodified']), $page);
453          _dotoken('LASTAUTHOR', $hash['author'], $page);
454          _dotoken('VERSION', $hash['version'], $page);
455          if (strstr($page, "$FieldSeparator#HITS$FieldSeparator#")) {
456             _dotoken('HITS', GetHitCount($dbi, $name), $page);
457          }
458          if (strstr($page, "$FieldSeparator#RELATEDPAGES$FieldSeparator#")) {
459             _dotoken('RELATEDPAGES', LinkRelatedPages($dbi, $name), $page);
460          }
461       }
462
463       // valid only for EditLinks
464       if ($template == 'EDITLINKS') {
465          for ($i = 1; $i <= NUM_LINKS; $i++) {
466             $ref = isset($hash['refs'][$i]) ? $hash['refs'][$i] : '';
467             _dotoken("R$i", $ref, $page);
468          }
469       }
470
471       _dotoken('CONTENT', $content, $page);
472       print $page;
473    }
474 ?>