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