]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/stdlib.php
cleaned up ParseAndLink() function, consequences:
[SourceForge/phpwiki.git] / lib / stdlib.php
1 <?php
2    rcs_id('$Id: stdlib.php,v 1.16 2000-12-30 21:42:50 ahollosi Exp $');
3    /*
4       Standard functions for Wiki functionality
5          LinkRelatedPages($dbi, $pagename)
6          GeneratePage($template, $content, $name, $hash)
7          LinkExistingWikiWord($wikiword) 
8          LinkUnknownWikiWord($wikiword) 
9          LinkURL($url)
10          LinkImage($url)
11          RenderQuickSearch() 
12          RenderFullSearch() 
13          RenderMostPopular()
14          CookSpaces($pagearray) 
15          class Stack
16          SetHTMLOutputMode($newmode, $depth)
17          UpdateRecentChanges($dbi, $pagename, $isnewpage) 
18          ParseAndLink($bracketlink)
19          ExtractWikiPageLinks($content)
20          ExitWiki($errormsg)
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 LinkRelatedPages($dbi, $pagename)
45    {
46       // currently not supported everywhere
47       if(!function_exists('GetWikiPageLinks'))
48          return '';
49
50       $links = GetWikiPageLinks($dbi, $pagename);
51
52       $txt = "<b>";
53       $txt .= sprintf (gettext ("%d best incoming links:"), NUM_RELATED_PAGES);
54       $txt .= "</b>\n";
55       for($i = 0; $i < NUM_RELATED_PAGES; $i++) {
56          if(isset($links['in'][$i])) {
57             list($name, $score) = $links['in'][$i];
58             $txt .= LinkExistingWikiWord($name) . " ($score), ";
59          }
60       }
61
62       $txt .= "\n<br><b>";
63       $txt .= sprintf (gettext ("%d best outgoing links:"), NUM_RELATED_PAGES);
64       $txt .= "</b>\n";
65       for($i = 0; $i < NUM_RELATED_PAGES; $i++) {
66          if(isset($links['out'][$i])) {
67             list($name, $score) = $links['out'][$i];
68             if(IsWikiPage($dbi, $name))
69                $txt .= LinkExistingWikiWord($name) . " ($score), ";
70          }
71       }
72
73       $txt .= "\n<br><b>";
74       $txt .= sprintf (gettext ("%d most popular nearby:"), NUM_RELATED_PAGES);
75       $txt .= "</b>\n";
76       for($i = 0; $i < NUM_RELATED_PAGES; $i++) {
77          if(isset($links['popular'][$i])) {
78             list($name, $score) = $links['popular'][$i];
79             $txt .= LinkExistingWikiWord($name) . " ($score), ";
80          }
81       }
82       
83       return $txt;
84    }
85
86    
87    function GeneratePage($template, $content, $name, $hash)
88    {
89       global $ScriptUrl, $AllowedProtocols, $templates;
90       global $datetimeformat, $dbi, $logo, $FieldSeparator;
91
92       if (!is_array($hash))
93          unset($hash);
94
95       function _dotoken ($id, $val, &$page) {
96          global $FieldSeparator;
97          $page = str_replace("$FieldSeparator#$id$FieldSeparator#",
98                                 $val, $page);
99       }
100
101       function _iftoken ($id, $condition, &$page) {
102          global $FieldSeparator;
103
104          // line based IF directive
105          $lineyes = "$FieldSeparator#IF $id$FieldSeparator#";
106          $lineno = "$FieldSeparator#IF !$id$FieldSeparator#";
107          // block based IF directive
108          $blockyes = "$FieldSeparator#IF:$id$FieldSeparator#";
109          $blockyesend = "$FieldSeparator#ENDIF:$id$FieldSeparator#";
110          $blockno = "$FieldSeparator#IF:!$id$FieldSeparator#";
111          $blocknoend = "$FieldSeparator#ENDIF:!$id$FieldSeparator#";
112
113          if ($condition) {
114             $page = str_replace($lineyes, '', $page);
115             $page = str_replace($blockyes, '', $page);
116             $page = str_replace($blockyesend, '', $page);
117             $page = preg_replace("/$blockno(.*?)$blocknoend/s", '', $page);
118             $page = ereg_replace("${lineno}[^\n]*\n", '', $page);
119          } else {
120             $page = str_replace($lineno, '', $page);
121             $page = str_replace($blockno, '', $page);
122             $page = str_replace($blocknoend, '', $page);
123             $page = preg_replace("/$blockyes(.*?)$blockyesend/s", '', $page);
124             $page = ereg_replace("${lineyes}[^\n]*\n", '', $page);
125          }
126       }
127
128       $page = join('', file($templates[$template]));
129       $page = str_replace('###', "$FieldSeparator#", $page);
130
131       // valid for all pagetypes
132       _iftoken('COPY', isset($hash['copy']), $page);
133       _iftoken('LOCK',  (isset($hash['flags']) &&
134                         ($hash['flags'] & FLAG_PAGE_LOCKED)), $page);
135       _iftoken('ADMIN', defined('WIKI_ADMIN'), $page);
136
137       _dotoken('SCRIPTURL', $ScriptUrl, $page);
138       _dotoken('PAGE', htmlspecialchars($name), $page);
139       _dotoken('ALLOWEDPROTOCOLS', $AllowedProtocols, $page);
140       _dotoken('LOGO', $logo, $page);
141       
142       // invalid for messages (search results, error messages)
143       if ($template != 'MESSAGE') {
144          _dotoken('PAGEURL', rawurlencode($name), $page);
145          _dotoken('LASTMODIFIED',
146                         date($datetimeformat, $hash['lastmodified']), $page);
147          _dotoken('LASTAUTHOR', $hash['author'], $page);
148          _dotoken('VERSION', $hash['version'], $page);
149          if (strstr($page, "$FieldSeparator#HITS$FieldSeparator#")) {
150             _dotoken('HITS', GetHitCount($dbi, $name), $page);
151          }
152          if (strstr($page, "$FieldSeparator#RELATEDPAGES$FieldSeparator#")) {
153             _dotoken('RELATEDPAGES', LinkRelatedPages($dbi, $name), $page);
154          }
155       }
156
157       // valid only for EditLinks
158       if ($template == 'EDITLINKS') {
159          for ($i = 1; $i <= NUM_LINKS; $i++)
160             _dotoken("R$i", $hash['refs'][$i], $page);
161       }
162
163       _dotoken('CONTENT', $content, $page);
164       print $page;
165    }
166
167
168    function LinkExistingWikiWord($wikiword, $linktext='') {
169       global $ScriptUrl;
170       $enc_word = rawurlencode($wikiword);
171       if(empty($linktext))
172          $linktext = htmlspecialchars($wikiword);
173       return "<a href=\"$ScriptUrl?$enc_word\">$linktext</a>";
174    }
175
176    function LinkUnknownWikiWord($wikiword, $linktext='') {
177       global $ScriptUrl;
178       $enc_word = rawurlencode($wikiword);
179       if(empty($linktext))
180          $linktext = htmlspecialchars($wikiword);
181       return "<u>$linktext</u><a href=\"$ScriptUrl?edit=$enc_word\">?</a>";
182    }
183
184    function LinkURL($url, $linktext='') {
185       global $ScriptUrl;
186       if(ereg("[<>\"]", $url)) {
187          return "<b><u>BAD URL -- remove all of &lt;, &gt;, &quot;</u></b>";
188       }
189
190       if(empty($linktext))
191          $linktext = htmlspecialchars($url);
192       return "<a href=\"$url\">$linktext</a>";
193    }
194
195
196    function LinkImage($url, $alt="[External Image]") {
197       global $ScriptUrl;
198       if(ereg("[<>\"]", $url)) {
199          return "<b><u>BAD URL -- remove all of &lt;, &gt;, &quot;</u></b>";
200       }
201       return "<img src=\"$url\" ALT=\"$alt\">";
202    }
203
204    
205    function RenderQuickSearch($value = "") {
206       global $ScriptUrl;
207       return "<form action=\"$ScriptUrl\">\n" .
208              "<input type=text size=30 name=search value=\"$value\">\n" .
209              "<input type=submit value=\"". gettext("Search") .
210              "\"></form>\n";
211    }
212
213    function RenderFullSearch($value = "") {
214       global $ScriptUrl;
215       return "<form action=\"$ScriptUrl\">\n" .
216              "<input type=text size=30 name=full value=\"$value\">\n" .
217              "<input type=submit value=\"". gettext("Search") .
218              "\"></form>\n";
219    }
220
221    function RenderMostPopular() {
222       global $ScriptUrl, $dbi;
223       
224       $query = InitMostPopular($dbi, MOST_POPULAR_LIST_LENGTH);
225       $result = "<DL>\n";
226       while ($qhash = MostPopularNextMatch($dbi, $query)) {
227          $result .= "<DD>$qhash[hits] ... " . LinkExistingWikiWord($qhash['pagename']) . "\n";
228       }
229       $result .= "</DL>\n";
230       
231       return $result;
232    }
233
234    function ParseAdminTokens($line) {
235       global $ScriptUrl;
236       
237       while (preg_match("/%%ADMIN-INPUT-(.*?)-(\w+)%%/", $line, $matches)) {
238          $head = str_replace("_", " ", $matches[2]);
239          $form = "<FORM ACTION=\"$ScriptUrl\" METHOD=POST>"
240                 ."$head: <INPUT NAME=$matches[1] SIZE=20> "
241                 ."<INPUT TYPE=SUBMIT VALUE=\"" . gettext("Go") . "\">"
242                 ."</FORM>";
243          $line = str_replace($matches[0], $form, $line);
244       }
245       return $line;
246    }
247
248    // converts spaces to tabs
249    function CookSpaces($pagearray) {
250       return preg_replace("/ {3,8}/", "\t", $pagearray);
251    }
252
253
254    class Stack {
255       var $items = array();
256       var $size = 0;
257
258       function push($item) {
259          $this->items[$this->size] = $item;
260          $this->size++;
261          return true;
262       }  
263    
264       function pop() {
265          if ($this->size == 0) {
266             return false; // stack is empty
267          }  
268          $this->size--;
269          return $this->items[$this->size];
270       }  
271    
272       function cnt() {
273          return $this->size;
274       }  
275
276       function top() {
277          if($this->size)
278             return $this->items[$this->size - 1];
279          else
280             return '';
281       }  
282
283    }  
284    // end class definition
285
286
287    // I couldn't move this to lib/config.php because it 
288    // wasn't declared yet.
289    $stack = new Stack;
290
291    /* 
292       Wiki HTML output can, at any given time, be in only one mode.
293       It will be something like Unordered List, Preformatted Text,
294       plain text etc. When we change modes we have to issue close tags
295       for one mode and start tags for another.
296    */
297
298    function SetHTMLOutputMode($tag, $tagdepth, $tabcount) {
299       global $stack;
300       $retvar = "";
301    
302       if ($tagdepth == SINGLE_DEPTH) {
303          if ($tabcount < $stack->cnt()) {
304             // there are fewer tabs than stack,
305             // reduce stack to that tab count
306             while ($stack->cnt() > $tabcount) {
307                $closetag = $stack->pop();
308                if ($closetag == false) {
309                   //echo "bounds error in tag stack";
310                   break;
311                }
312                $retvar .= "</$closetag>\n";
313             }
314
315             // if list type isn't the same,
316             // back up one more and push new tag
317             if ($tag != $stack->top()) {
318                $closetag = $stack->pop();
319                $retvar .= "</$closetag><$tag>\n";
320                $stack->push($tag);
321             }
322    
323          } elseif ($tabcount > $stack->cnt()) {
324             // we add the diff to the stack
325             // stack might be zero
326             while ($stack->cnt() < $tabcount) {
327                #echo "<$tag>\n";
328                $retvar .= "<$tag>\n";
329                $stack->push($tag);
330                if ($stack->cnt() > 10) {
331                   // arbitrarily limit tag nesting
332                   ExitWiki(gettext ("Stack bounds exceeded in SetHTMLOutputMode"));
333                }
334             }
335    
336          } else {
337             if ($tag == $stack->top()) {
338                return;
339             } else {
340                $closetag = $stack->pop();
341                #echo "</$closetag>\n";
342                #echo "<$tag>\n";
343                $retvar .= "</$closetag>\n";
344                $retvar .= "<$tag>\n";
345                $stack->push($tag);
346             }
347          }
348    
349       } elseif ($tagdepth == ZERO_DEPTH) {
350          // empty the stack for $depth == 0;
351          // what if the stack is empty?
352          if ($tag == $stack->top()) {
353             return;
354          }
355          while ($stack->cnt() > 0) {
356             $closetag = $stack->pop();
357             #echo "</$closetag>\n";
358             $retvar .= "</$closetag>\n";
359          }
360    
361          if ($tag) {
362             #echo "<$tag>\n";
363             $retvar .= "<$tag>\n";
364             $stack->push($tag);
365          }
366    
367       } else {
368          // error
369          ExitWiki ("Passed bad tag depth value in SetHTMLOutputMode");
370       }
371
372       return $retvar;
373
374    }
375    // end SetHTMLOutputMode
376
377
378
379    // The Recent Changes file is solely handled here
380    function UpdateRecentChanges($dbi, $pagename, $isnewpage) {
381
382       global $remoteuser; // this is set in the config
383       global $dateformat;
384       global $WikiPageStore;
385
386       $recentchanges = RetrievePage($dbi, gettext ("RecentChanges"), 
387         $WikiPageStore);
388
389       // this shouldn't be necessary, since PhpWiki loads 
390       // default pages if this is a new baby Wiki
391       if ($recentchanges == -1) {
392          $recentchanges = array(); 
393       }
394
395       $now = time();
396       $today = date($dateformat, $now);
397
398       if (date($dateformat, $recentchanges["lastmodified"]) != $today) {
399          $isNewDay = TRUE;
400          $recentchanges["lastmodified"] = $now;
401       } else {
402          $isNewDay = FALSE;
403       }
404
405       $numlines = sizeof($recentchanges["content"]);
406       $newpage = array();
407       $k = 0;
408
409       // scroll through the page to the first date and break
410       // dates are marked with "____" at the beginning of the line
411       for ($i = 0; $i < $numlines; $i++) {
412          if (preg_match("/^____/",
413                         $recentchanges["content"][$i])) {
414             break;
415          } else {
416             $newpage[$k++] = $recentchanges["content"][$i];
417          }
418       }
419
420       // if it's a new date, insert it, else add the updated page's
421       // name to the array
422
423       $newpage[$k++] = $isNewDay ? "____$today\r"
424                                  : $recentchanges["content"][$i++];
425       if($isnewpage) {
426          $newpage[$k++] = "* [$pagename] (new) ..... $remoteuser\r";
427       } else {
428          $diffurl = "phpwiki:?diff=" . rawurlencode($pagename);
429          $newpage[$k++] = "* [$pagename] ([diff|$diffurl]) ..... $remoteuser\r";
430       }
431       if ($isNewDay)
432          $newpage[$k++] = "\r";
433
434       // copy the rest of the page into the new array
435       $pagename = preg_quote($pagename);
436       for (; $i < $numlines; $i++) {
437          // skip previous entry for $pagename
438          if (preg_match("|\[$pagename\]|", $recentchanges["content"][$i])) {
439             continue;
440          } else {
441             $newpage[$k++] = $recentchanges["content"][$i];
442          }
443       }
444
445       $recentchanges["content"] = $newpage;
446
447       InsertPage($dbi, gettext ("RecentChanges"), $recentchanges);
448    }
449
450
451
452    function ParseAndLink($bracketlink) {
453       global $dbi, $ScriptUrl, $AllowedProtocols, $InlineImages;
454
455       // $bracketlink will start and end with brackets; in between
456       // will be either a page name, a URL or both separated by a pipe.
457
458       // strip brackets and leading space
459       preg_match("/(\[\s*)(.+?)(\s*\])/", $bracketlink, $match);
460       // match the contents 
461       preg_match("/([^|]+)(\|)?([^|]+)?/", $match[2], $matches);
462
463       if (isset($matches[3])) {
464          // named link of the form  "[some link name | http://blippy.com/]"
465          $URL = trim($matches[3]);
466          $linkname = htmlspecialchars(trim($matches[1]));
467          $linktype = 'named';
468       } else {
469          // unnamed link of the form "[http://blippy.com/] or [wiki page]"
470          $URL = trim($matches[1]);
471          $linkname = '';
472          $linktype = 'simple';
473       }
474
475       if (IsWikiPage($dbi, $URL)) {
476          $link['type'] = "wiki-$linktype";
477          $link['link'] = LinkExistingWikiWord($URL, $linkname);
478       } elseif (preg_match("#^($AllowedProtocols):#", $URL)) {
479         // if it's an image, embed it; otherwise, it's a regular link
480          if (preg_match("/($InlineImages)$/i", $URL)) {
481             $link['type'] = "image-$linktype";
482             $link['link'] = LinkImage($URL, $linkname);
483          } else {
484             $link['type'] = "url-$linktype";
485             $link['link'] = LinkURL($URL, $linkname);
486          }
487       } elseif (preg_match("#^phpwiki:(.*)#", $URL, $match)) {
488          $link['type'] = "url-wiki-$linktype";
489          $link['link'] = "<a href=\"$ScriptUrl$match[1]\">$linkname</a>";
490       } else {
491          $link['type'] = "wiki-unknown-$linktype";
492          $link['link'] = LinkUnknownWikiWord($URL, $linkname);
493       }
494
495       return $link;
496    }
497
498
499    function ExtractWikiPageLinks($content)
500    {
501       global $WikiNameRegexp;
502
503       $wikilinks = array();
504       $numlines = count($content);
505       for($l = 0; $l < $numlines; $l++)
506       {
507          $line = str_replace('[[', ' ', $content[$l]);  // remove escaped '['
508          $numBracketLinks = preg_match_all("/\[\s*([^\]|]+\|)?\s*(.+?)\s*\]/", $line, $brktlinks);
509          for ($i = 0; $i < $numBracketLinks; $i++) {
510             $link = ParseAndLink($brktlinks[0][$i]);
511             if (preg_match("#^wiki#", $link['type']))
512                $wikilinks[$brktlinks[2][$i]] = 1;
513
514             $brktlink = preg_quote($brktlinks[0][$i]);
515             $line = preg_replace("|$brktlink|", '', $line);
516          }
517
518          if (preg_match_all("/!?$WikiNameRegexp/", $line, $link)) {
519             for ($i = 0; isset($link[0][$i]); $i++) {
520                if($link[0][$i][0] <> '!')
521                   $wikilinks[$link[0][$i]] = 1;
522             }
523          }
524       }
525       return $wikilinks;
526    }      
527 ?>