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