]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/stdlib.php
bugfix in ExtractWikiPageLinks for [text|WikiPage]
[SourceForge/phpwiki.git] / lib / stdlib.php
1 <?php
2    rcs_id('$Id: stdlib.php,v 1.11 2000-11-11 12:15:35 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) {
185       global $ScriptUrl;
186       if(ereg("[<>\"]", $url)) {
187          return "<b><u>BAD URL -- remove all of &lt;, &gt;, &quot;</u></b>";
188       }
189       $enc_url = htmlspecialchars($url);
190       return "<a href=\"$url\">$enc_url</a>";
191    }
192
193
194    function LinkImage($url, $alt="[External Image]") {
195       global $ScriptUrl;
196       if(ereg("[<>\"]", $url)) {
197          return "<b><u>BAD URL -- remove all of &lt;, &gt;, &quot;</u></b>";
198       }
199       return "<img src=\"$url\" ALT=\"$alt\">";
200    }
201
202    
203    function RenderQuickSearch($value = "") {
204       global $ScriptUrl;
205       return "<form action=\"$ScriptUrl\">\n" .
206              "<input type=text size=30 name=search value=\"$value\">\n" .
207              "<input type=submit value=\"". gettext("Search") .
208              "\"></form>\n";
209    }
210
211    function RenderFullSearch($value = "") {
212       global $ScriptUrl;
213       return "<form action=\"$ScriptUrl\">\n" .
214              "<input type=text size=30 name=full value=\"$value\">\n" .
215              "<input type=submit value=\"". gettext("Search") .
216              "\"></form>\n";
217    }
218
219    function RenderMostPopular() {
220       global $ScriptUrl, $dbi;
221       
222       $query = InitMostPopular($dbi, MOST_POPULAR_LIST_LENGTH);
223       $result = "<DL>\n";
224       while ($qhash = MostPopularNextMatch($dbi, $query)) {
225          $result .= "<DD>$qhash[hits] ... " . LinkExistingWikiWord($qhash['pagename']) . "\n";
226       }
227       $result .= "</DL>\n";
228       
229       return $result;
230    }
231
232    function ParseAdminTokens($line) {
233       global $ScriptUrl;
234       
235       while (preg_match("/%%ADMIN-INPUT-(.*?)-(\w+)%%/", $line, $matches)) {
236          $head = str_replace("_", " ", $matches[2]);
237          $form = "<FORM ACTION=\"$ScriptUrl\" METHOD=POST>"
238                 ."$head: <INPUT NAME=$matches[1] SIZE=20> "
239                 ."<INPUT TYPE=SUBMIT VALUE=\"" . gettext("Go") . "\">"
240                 ."</FORM>";
241          $line = str_replace($matches[0], $form, $line);
242       }
243       return $line;
244    }
245
246    // converts spaces to tabs
247    function CookSpaces($pagearray) {
248       return preg_replace("/ {3,8}/", "\t", $pagearray);
249    }
250
251
252    class Stack {
253       var $items = array();
254       var $size = 0;
255
256       function push($item) {
257          $this->items[$this->size] = $item;
258          $this->size++;
259          return true;
260       }  
261    
262       function pop() {
263          if ($this->size == 0) {
264             return false; // stack is empty
265          }  
266          $this->size--;
267          return $this->items[$this->size];
268       }  
269    
270       function cnt() {
271          return $this->size;
272       }  
273
274       function top() {
275          if($this->size)
276             return $this->items[$this->size - 1];
277          else
278             return '';
279       }  
280
281    }  
282    // end class definition
283
284
285    // I couldn't move this to lib/config.php because it 
286    // wasn't declared yet.
287    $stack = new Stack;
288
289    /* 
290       Wiki HTML output can, at any given time, be in only one mode.
291       It will be something like Unordered List, Preformatted Text,
292       plain text etc. When we change modes we have to issue close tags
293       for one mode and start tags for another.
294    */
295
296    function SetHTMLOutputMode($tag, $tagdepth, $tabcount) {
297       global $stack;
298       $retvar = "";
299    
300       if ($tagdepth == SINGLE_DEPTH) {
301          if ($tabcount < $stack->cnt()) {
302             // there are fewer tabs than stack,
303             // reduce stack to that tab count
304             while ($stack->cnt() > $tabcount) {
305                $closetag = $stack->pop();
306                if ($closetag == false) {
307                   //echo "bounds error in tag stack";
308                   break;
309                }
310                $retvar .= "</$closetag>\n";
311             }
312
313             // if list type isn't the same,
314             // back up one more and push new tag
315             if ($tag != $stack->top()) {
316                $closetag = $stack->pop();
317                $retvar .= "</$closetag><$tag>\n";
318                $stack->push($tag);
319             }
320    
321          } elseif ($tabcount > $stack->cnt()) {
322             // we add the diff to the stack
323             // stack might be zero
324             while ($stack->cnt() < $tabcount) {
325                #echo "<$tag>\n";
326                $retvar .= "<$tag>\n";
327                $stack->push($tag);
328                if ($stack->cnt() > 10) {
329                   // arbitrarily limit tag nesting
330                   ExitWiki(gettext ("Stack bounds exceeded in SetHTMLOutputMode"));
331                }
332             }
333    
334          } else {
335             if ($tag == $stack->top()) {
336                return;
337             } else {
338                $closetag = $stack->pop();
339                #echo "</$closetag>\n";
340                #echo "<$tag>\n";
341                $retvar .= "</$closetag>\n";
342                $retvar .= "<$tag>\n";
343                $stack->push($tag);
344             }
345          }
346    
347       } elseif ($tagdepth == ZERO_DEPTH) {
348          // empty the stack for $depth == 0;
349          // what if the stack is empty?
350          if ($tag == $stack->top()) {
351             return;
352          }
353          while ($stack->cnt() > 0) {
354             $closetag = $stack->pop();
355             #echo "</$closetag>\n";
356             $retvar .= "</$closetag>\n";
357          }
358    
359          if ($tag) {
360             #echo "<$tag>\n";
361             $retvar .= "<$tag>\n";
362             $stack->push($tag);
363          }
364    
365       } else {
366          // error
367          ExitWiki ("Passed bad tag depth value in SetHTMLOutputMode");
368       }
369
370       return $retvar;
371
372    }
373    // end SetHTMLOutputMode
374
375
376
377    // The Recent Changes file is solely handled here
378    function UpdateRecentChanges($dbi, $pagename, $isnewpage) {
379
380       global $remoteuser; // this is set in the config
381       global $dateformat;
382       global $WikiPageStore;
383
384       $recentchanges = RetrievePage($dbi, gettext ("RecentChanges"), 
385         $WikiPageStore);
386
387       // this shouldn't be necessary, since PhpWiki loads 
388       // default pages if this is a new baby Wiki
389       if ($recentchanges == -1) {
390          $recentchanges = array(); 
391       }
392
393       $now = time();
394       $today = date($dateformat, $now);
395
396       if (date($dateformat, $recentchanges["lastmodified"]) != $today) {
397          $isNewDay = TRUE;
398          $recentchanges["lastmodified"] = $now;
399       } else {
400          $isNewDay = FALSE;
401       }
402
403       $numlines = sizeof($recentchanges["content"]);
404       $newpage = array();
405       $k = 0;
406
407       // scroll through the page to the first date and break
408       // dates are marked with "____" at the beginning of the line
409       for ($i = 0; $i < ($numlines + 1); $i++) {
410          if (preg_match("/^____/",
411                         $recentchanges["content"][$i])) {
412             break;
413          } else {
414             $newpage[$k++] = $recentchanges["content"][$i];
415          }
416       }
417
418       // if it's a new date, insert it, else add the updated page's
419       // name to the array
420
421       if ($isNewDay) {
422          $newpage[$k++] = "____$today\r";
423          $newpage[$k++] = "\r";
424       } else {
425          $newpage[$k++] = $recentchanges["content"][$i++];
426       }
427       if($isnewpage) {
428          $newpage[$k++] = "* [$pagename] (new) ..... $remoteuser\r";
429       } else {
430          $diffurl = "phpwiki:?diff=" . rawurlencode($pagename);
431          $newpage[$k++] = "* [$pagename] ([diff|$diffurl]) ..... $remoteuser\r";
432       }
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 $matches[3] is set, this is a link in the form of:
464       // [some link name | http://blippy.com/]
465
466       if (isset($matches[3])) {
467          $URL = trim($matches[3]);
468          $linkname = htmlspecialchars(trim($matches[1]));
469          // assert proper URL's
470          if (IsWikiPage($dbi, $URL)) {
471             $link['type'] = 'wiki-named';
472             $link['link'] = LinkExistingWikiWord($URL, $linkname);
473          } elseif (preg_match("#^($AllowedProtocols):#", $URL)) {
474             if (preg_match("/($InlineImages)$/i", $URL)) {
475                $link['type'] = 'image-named';
476                $link['link'] = LinkImage($URL, $linkname);
477             } else {
478                $link['type'] = 'url-named';
479                $link['link'] = "<a href=\"$URL\">$linkname</a>";
480             }
481          } elseif (preg_match("#^phpwiki:(.*)#", $URL, $match)) {
482             $link['type'] = 'url-wiki-named';
483             $link['link'] = "<a href=\"$ScriptUrl$match[1]\">$linkname</a>";
484          } else {
485             $link['type'] = 'wiki-unknown-named';
486             $link['link'] = LinkUnknownWikiWord($URL, $linkname);
487          }
488          return $link;
489       }
490
491
492       // otherwise this is just a Wiki page like this: [page name]
493       // or a URL in brackets: [http://foo.com/]
494
495       if (isset($matches[1])) {
496          $linkname = trim($matches[1]);
497          if (IsWikiPage($dbi, $linkname)) {
498             $link['type'] = 'wiki';
499             $link['link'] = LinkExistingWikiWord($linkname);
500          } elseif (preg_match("#^($AllowedProtocols):#", $linkname)) {
501             // if it's an image, embed it; otherwise, it's a regular link
502             if (preg_match("/($InlineImages)$/i", $linkname)) {
503                $link['type'] = 'image-simple';
504                $link['link'] = LinkImage($linkname);
505             } else {
506                $link['type'] = 'url-simple';
507                $link['link'] = LinkURL($linkname);
508             }
509          } else {
510             $link['type'] = 'wiki-unknown';
511             $link['link'] = LinkUnknownWikiWord($linkname);
512          }
513          return $link;
514       }
515
516       $link['type'] = 'unknown';
517       $link['link'] = $bracketlink;
518       return $link;
519    }
520
521
522    function ExtractWikiPageLinks($content)
523    {
524       global $WikiNameRegexp;
525
526       $wikilinks = array();
527       $numlines = count($content);
528       for($l = 0; $l < $numlines; $l++)
529       {
530          $line = $content[$l];
531          $numBracketLinks = preg_match_all("/\[\s*(.+?)\s*\]/", $line, $brktlinks);
532          for ($i = 0; $i < $numBracketLinks; $i++) {
533             $link = ParseAndLink($brktlinks[0][$i]);
534             if ($link['type'] == 'wiki' || $link['type'] == 'wiki-unknown'
535                 || $link['type'] == 'wiki-named')
536                $wikilinks[$brktlinks[1][$i]] = 1;
537
538             $brktlink = preg_quote($brktlinks[0][$i]);
539             $line = preg_replace("|$brktlink|", '', $line);
540          }
541
542          if (preg_match_all("/!?$WikiNameRegexp/", $line, $link)) {
543             for ($i = 0; isset($link[0][$i]); $i++) {
544                if($link[0][$i][0] <> '!')
545                   $wikilinks[$link[0][$i]] = 1;
546             }
547          }
548       }
549
550       return $wikilinks;
551    }      
552 ?>