]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/stdlib.php
Fix broken switch statement (which cause my PHP to segfault ?!?)
[SourceForge/phpwiki.git] / lib / stdlib.php
1 <?php rcs_id('$Id: stdlib.php,v 1.54 2001-12-06 18:29:15 dairiki Exp $');
2
3    /*
4       Standard functions for Wiki functionality
5          WikiURL($pagename, $args, $abs)
6          LinkWikiWord($wikiword, $linktext) 
7          LinkExistingWikiWord($wikiword, $linktext) 
8          LinkUnknownWikiWord($wikiword, $linktext) 
9          LinkURL($url, $linktext)
10          LinkImage($url, $alt)
11          LinkInterWikiLink($link, $linktext)
12          CookSpaces($pagearray) 
13          class Stack (push(), pop(), cnt(), top())
14          UpdateRecentChanges($dbi, $pagename, $isnewpage) 
15          ParseAndLink($bracketlink)
16          ExtractWikiPageLinks($content)
17          LinkRelatedPages($dbi, $pagename)
18    */
19
20
21    function DataURL($url) {
22       if (preg_match('@^(\w+:|/)@', $url))
23          return $url;
24       return SERVER_URL . DATA_PATH . "/$url";
25    }
26           
27 function WikiURL($pagename, $args = '', $get_abs_url = false) {
28     if (is_array($args)) {
29         $enc_args = array();
30         foreach  ($args as $key => $val) {
31             $enc_args[] = urlencode($key) . '=' . urlencode($val);
32         }
33         $args = join('&', $enc_args);
34     }
35
36     if (USE_PATH_INFO) {
37         $url = $get_abs_url ? SERVER_URL . VIRTUAL_PATH . "/" : '';
38         $url .= rawurlencode($pagename);
39         if ($args)
40             $url .= "?$args";
41     }
42     else {
43         $url = $get_abs_url ? SERVER_URL . SCRIPT_NAME : basename(SCRIPT_NAME);
44         $url .= "?pagename=" . rawurlencode($pagename);
45         if ($args)
46             $url .= "&$args";
47     }
48
49     return $url;
50 }
51
52 define('NO_END_TAG_PAT',
53        '/^' . join('|', array('area', 'base', 'basefont',
54                               'br', 'col', 'frame',
55                               'hr', 'img', 'input',
56                               'isindex', 'link', 'meta',
57                               'param')) . '$/i');
58
59 function StartTag($tag, $args = '')
60 {
61    $s = "<$tag";
62    if (is_array($args))
63    {
64       while (list($key, $val) = each($args))
65       {
66          if (is_string($val) || is_numeric($val))
67             $s .= sprintf(' %s="%s"', $key, htmlspecialchars($val));
68          else if ($val)
69             $s .= " $key=\"$key\"";
70       }
71    }
72    return "$s>";
73 }
74
75    
76 function Element($tag, $args = '', $content = '')
77 {
78     $html = "<$tag";
79     if (!is_array($args)) {
80         $content = $args;
81         $args = false;
82     }
83     $html = StartTag($tag, $args);
84     if (preg_match(NO_END_TAG_PAT, $tag)) {
85         assert(! $content);
86         return preg_replace('/>$/', " />", $html);
87     } else {
88         $html .= $content;
89         $html .= "</$tag>";//FIXME: newline might not always be desired.
90     }
91     return $html;
92 }
93
94 function QElement($tag, $args = '', $content = '')
95 {
96     if (is_array($args))
97         return Element($tag, $args, htmlspecialchars($content));
98     else {
99         $content = $args;
100         return Element($tag, htmlspecialchars($content));
101     }
102 }
103    
104    function LinkURL($url, $linktext='') {
105       // FIXME: Is this needed (or sufficient?)
106       if(ereg("[<>\"]", $url)) {
107           return Element('strong',
108                          QElement('u', array('class' => 'baduri'),
109                                   'BAD URL -- remove all of <, >, "')); //"
110       }
111
112       if (empty($linktext)) {
113           $linktext = $url;
114           $class = 'rawurl';
115       }
116       else {
117           $class = 'namedurl';
118       }
119
120       if (!defined('USE_LINK_ICONS')) {
121           return QElement('a',
122                      array('href' => $url, 'class' => $class),
123                      $linktext);
124       } else {
125           //ideally the link image would be specified by a map file
126           //similar to the interwiki.map
127           $linkproto = substr($url, 0, strrpos($url, ":"));
128           switch($linkproto) {
129           case "mailto":
130               $linkimg = "/images/mailto.png";
131               break;
132           case "http":
133               $linkimg = "/images/http.png";
134               break;
135           case "https":
136               $linkimg = "/images/https.png";
137               break;
138           case "ftp":
139               $linkimg = "/images/ftp.png";
140               break;
141           default:
142               $linkimg = "/images/http.png";
143               break;
144           }
145       return Element('a',
146                      array('href' => $url, 'class' => $class),
147                      Element('img', array('src' => DATA_PATH . $linkimg,
148                                           'alt' => $linkproto))
149                      . $linktext);
150       }
151    }
152
153 function LinkWikiWord($wikiword, $linktext='') {
154     global $dbi;
155     if ($dbi->isWikiPage($wikiword))
156         return LinkExistingWikiWord($wikiword, $linktext);
157     else
158         return LinkUnknownWikiWord($wikiword, $linktext);
159 }
160
161     
162    function LinkExistingWikiWord($wikiword, $linktext='') {
163       if (empty($linktext)) {
164           $linktext = $wikiword;
165           if (defined("autosplit_wikiwords"))
166               $linktext=split_pagename($linktext);
167          $class = 'wiki';
168       }
169       else
170           $class = 'named-wiki';
171       
172       return QElement('a', array('href' => WikiURL($wikiword),
173                                  'class' => $class),
174                      $linktext);
175    }
176
177    function LinkUnknownWikiWord($wikiword, $linktext='') {
178       if (empty($linktext)) {
179           $linktext = $wikiword;
180           if (defined("autosplit_wikiwords"))
181               $linktext=split_pagename($linktext);
182           $class = 'wikiunknown';
183       }
184       else {
185           $class = 'named-wikiunknown';
186       }
187
188       return Element('span', array('class' => $class),
189                      QElement('a',
190                               array('href' => WikiURL($wikiword, array('action' => 'edit'))),
191                               '?')
192                      . Element('u', $linktext));
193    }
194
195    function LinkImage($url, $alt='[External Image]') {
196       // FIXME: Is this needed (or sufficient?)
197       //  As long as the src in htmlspecialchars()ed I think it's safe.
198       if(ereg('[<>"]', $url)) {
199          return Element('strong',
200                         QElement('u', array('class' => 'baduri'),
201                                  'BAD URL -- remove all of <, >, "'));
202       }
203       return Element('img', array('src' => $url, 'alt' => $alt));
204    }
205
206    // converts spaces to tabs
207    function CookSpaces($pagearray) {
208       return preg_replace("/ {3,8}/", "\t", $pagearray);
209    }
210
211
212    class Stack {
213       var $items = array();
214       var $size = 0;
215
216       function push($item) {
217          $this->items[$this->size] = $item;
218          $this->size++;
219          return true;
220       }  
221    
222       function pop() {
223          if ($this->size == 0) {
224             return false; // stack is empty
225          }  
226          $this->size--;
227          return $this->items[$this->size];
228       }  
229    
230       function cnt() {
231          return $this->size;
232       }  
233
234       function top() {
235          if($this->size)
236             return $this->items[$this->size - 1];
237          else
238             return '';
239       }  
240
241    }  
242    // end class definition
243
244
245    function MakeWikiForm ($pagename, $args, $class, $button_text = '')
246    {
247       $formargs['action'] = USE_PATH_INFO ? WikiURL($pagename) : SCRIPT_NAME;
248       $formargs['method'] = 'get';
249       $formargs['class'] = $class;
250       
251       $contents = '';
252       $input_seen = 0;
253       
254       while (list($key, $val) = each($args))
255       {
256          $a = array('name' => $key, 'value' => $val, 'type' => 'hidden');
257          
258          if (preg_match('/^ (\d*) \( (.*) \) ((upload)?) $/xi', $val, $m))
259          {
260             $input_seen++;
261             $a['type'] = 'text';
262             $a['size'] = $m[1] ? $m[1] : 30;
263             $a['value'] = $m[2];
264             if ($m[3])
265             {
266                $a['type'] = 'file';
267                $formargs['enctype'] = 'multipart/form-data';
268                $contents .= Element('input',
269                                     array('name' => 'MAX_FILE_SIZE',
270                                           'value' => MAX_UPLOAD_SIZE,
271                                           'type' => 'hidden'));
272                $formargs['method'] = 'post';
273             }
274          }
275
276          $contents .= Element('input', $a);
277       }
278
279       $row = Element('td', $contents);
280       
281       if (!empty($button_text)) {
282          $row .= Element('td', Element('input', array('type' => 'submit',
283                                                       'class' => 'button',
284                                                       'value' => $button_text)));
285       }
286
287       return Element('form', $formargs,
288                      Element('table', array('cellspacing' => 0, 'cellpadding' => 2, 'border' => 0),
289                              Element('tr', $row)));
290    }
291
292    function SplitQueryArgs ($query_args = '') 
293    {
294       $split_args = split('&', $query_args);
295       $args = array();
296       while (list($key, $val) = each($split_args))
297          if (preg_match('/^ ([^=]+) =? (.*) /x', $val, $m))
298             $args[$m[1]] = $m[2];
299       return $args;
300    }
301    
302 function LinkPhpwikiURL($url, $text = '') {
303         $args = array();
304
305         if (!preg_match('/^ phpwiki: ([^?]*) [?]? (.*) $/x', $url, $m))
306             return Element('strong',
307                            QElement('u', array('class' => 'baduri'),
308                                     'BAD phpwiki: URL'));
309         if ($m[1])
310                 $pagename = urldecode($m[1]);
311         $qargs = $m[2];
312       
313         if (empty($pagename) && preg_match('/^(diff|edit|links|info)=([^&]+)$/', $qargs, $m)) {
314                 // Convert old style links (to not break diff links in RecentChanges).
315                 $pagename = urldecode($m[2]);
316                 $args = array("action" => $m[1]);
317         }
318         else {
319                 $args = SplitQueryArgs($qargs);
320         }
321
322         if (empty($pagename))
323                 $pagename = $GLOBALS['pagename'];
324
325         if (isset($args['action']) && $args['action'] == 'browse')
326                 unset($args['action']);
327         /*FIXME:
328         if (empty($args['action']))
329                 $class = 'wikilink';
330         else if (is_safe_action($args['action']))
331                 $class = 'wikiaction';
332         */
333         if (empty($args['action']) || is_safe_action($args['action']))
334                 $class = 'wikiaction';
335         else {
336                 // Don't allow administrative links on unlocked pages.
337                 // FIXME: Ugh: don't like this...
338                 global $dbi;
339                 $page = $dbi->getPage($GLOBALS['pagename']);
340                 if (!$page->get('locked'))
341                         return QElement('u', array('class' => 'wikiunsafe'),
342                                                         gettext('Lock page to enable link'));
343
344                 $class = 'wikiadmin';
345         }
346       
347         // FIXME: ug, don't like this
348         if (preg_match('/=\d*\(/', $qargs))
349                 return MakeWikiForm($pagename, $args, $class, $text);
350         if ($text)
351                 $text = htmlspecialchars($text);
352         else
353                 $text = QElement('span', array('class' => 'rawurl'), $url);
354
355         return Element('a', array('href' => WikiURL($pagename, $args),
356                                                           'class' => $class),
357                                    $text);
358 }
359
360    function ParseAndLink($bracketlink) {
361       global $dbi, $AllowedProtocols, $InlineImages;
362       global $InterWikiLinkRegexp;
363
364       // $bracketlink will start and end with brackets; in between
365       // will be either a page name, a URL or both separated by a pipe.
366
367       // strip brackets and leading space
368       preg_match("/(\[\s*)(.+?)(\s*\])/", $bracketlink, $match);
369       // match the contents 
370       preg_match("/([^|]+)(\|)?([^|]+)?/", $match[2], $matches);
371
372       if (isset($matches[3])) {
373          // named link of the form  "[some link name | http://blippy.com/]"
374          $URL = trim($matches[3]);
375          $linkname = trim($matches[1]);
376          $linktype = 'named';
377       } else {
378          // unnamed link of the form "[http://blippy.com/] or [wiki page]"
379          $URL = trim($matches[1]);
380          $linkname = '';
381          $linktype = 'simple';
382       }
383
384       if ($dbi->isWikiPage($URL)) {
385          $link['type'] = "wiki-$linktype";
386          $link['link'] = LinkExistingWikiWord($URL, $linkname);
387       } elseif (preg_match("#^($AllowedProtocols):#", $URL)) {
388         // if it's an image, embed it; otherwise, it's a regular link
389          if (preg_match("/($InlineImages)$/i", $URL)) {
390             $link['type'] = "image-$linktype";
391             $link['link'] = LinkImage($URL, $linkname);
392          } else {
393             $link['type'] = "url-$linktype";
394             $link['link'] = LinkURL($URL, $linkname);
395          }
396       } elseif (preg_match("#^phpwiki:(.*)#", $URL, $match)) {
397          $link['type'] = "url-wiki-$linktype";
398          $link['link'] = LinkPhpwikiURL($URL, $linkname);
399       } elseif (preg_match("#^\d+$#", $URL)) {
400          $link['type'] = "footnote-$linktype";
401          $link['link'] = $URL;
402       } elseif (function_exists('LinkInterWikiLink') &&
403                 preg_match("#^$InterWikiLinkRegexp:#", $URL)) {
404          $link['type'] = "interwiki-$linktype";
405          $link['link'] = LinkInterWikiLink($URL, $linkname);
406       } else {
407          $link['type'] = "wiki-unknown-$linktype";
408          $link['link'] = LinkUnknownWikiWord($URL, $linkname);
409       }
410
411       return $link;
412    }
413
414
415 function ExtractWikiPageLinks($content)
416 {
417     global $WikiNameRegexp;
418     
419     if (is_string($content))
420         $content = explode("\n", $content);
421
422     $wikilinks = array();
423     foreach ($content as $line) {
424         // remove plugin code
425         $line = preg_replace('/<\?plugin\s+\w.*?\?>/', '', $line);
426         // remove escaped '['
427         $line = str_replace('[[', ' ', $line);
428
429   // bracket links (only type wiki-* is of interest)
430   $numBracketLinks = preg_match_all("/\[\s*([^\]|]+\|)?\s*(.+?)\s*\]/", $line, $brktlinks);
431   for ($i = 0; $i < $numBracketLinks; $i++) {
432      $link = ParseAndLink($brktlinks[0][$i]);
433      if (preg_match("#^wiki#", $link['type']))
434         $wikilinks[$brktlinks[2][$i]] = 1;
435
436          $brktlink = preg_quote($brktlinks[0][$i]);
437          $line = preg_replace("|$brktlink|", '', $line);
438   }
439
440       // BumpyText old-style wiki links
441       if (preg_match_all("/!?$WikiNameRegexp/", $line, $link)) {
442          for ($i = 0; isset($link[0][$i]); $i++) {
443             if($link[0][$i][0] <> '!')
444                $wikilinks[$link[0][$i]] = 1;
445      }
446       }
447    }
448    return array_keys($wikilinks);
449 }      
450
451    function LinkRelatedPages($dbi, $pagename)
452    {
453       // currently not supported everywhere
454       if(!function_exists('GetWikiPageLinks'))
455          return '';
456
457       //FIXME: fix or toss?
458       $links = GetWikiPageLinks($dbi, $pagename);
459
460       $txt = QElement('strong',
461                       sprintf (gettext ("%d best incoming links:"), NUM_RELATED_PAGES));
462       for($i = 0; $i < NUM_RELATED_PAGES; $i++) {
463          if(isset($links['in'][$i])) {
464             list($name, $score) = $links['in'][$i];
465             $txt .= LinkExistingWikiWord($name) . " ($score), ";
466          }
467       }
468       
469       $txt .= "\n" . Element('br');
470       $txt .= Element('strong',
471                       sprintf (gettext ("%d best outgoing links:"), NUM_RELATED_PAGES));
472       for($i = 0; $i < NUM_RELATED_PAGES; $i++) {
473          if(isset($links['out'][$i])) {
474             list($name, $score) = $links['out'][$i];
475             if($dbi->isWikiPage($name))
476                $txt .= LinkExistingWikiWord($name) . " ($score), ";
477          }
478       }
479
480       $txt .= "\n" . Element('br');
481       $txt .= Element('strong',
482                       sprintf (gettext ("%d most popular nearby:"), NUM_RELATED_PAGES));
483       for($i = 0; $i < NUM_RELATED_PAGES; $i++) {
484          if(isset($links['popular'][$i])) {
485             list($name, $score) = $links['popular'][$i];
486             $txt .= LinkExistingWikiWord($name) . " ($score), ";
487          }
488       }
489       
490       return $txt;
491    }
492
493
494 /**
495  * Split WikiWords in page names.
496  *
497  * It has been deemed useful to split WikiWords (into "Wiki Words")
498  * in places like page titles.  This is rumored to help search engines
499  * quite a bit.
500  *
501  * @param $page string The page name.
502  *
503  * @return string The split name.
504  */
505 function split_pagename ($page) {
506     
507     if (preg_match("/\s/", $page))
508         return $page;           // Already split --- don't split any more.
509
510     // FIXME: this algorithm is Anglo-centric.
511     static $RE;
512     if (!isset($RE)) {
513         // This mess splits between a lower-case letter followed by either an upper-case
514         // or a numeral; except that it wont split the prefixes 'Mc', 'De', or 'Di' off
515         // of their tails.
516                         $RE[] = '/([[:lower:]])((?<!Mc|De|Di)[[:upper:]]|\d)/';
517         // This the single-letter words 'I' and 'A' from any following capitalized words.
518         $RE[] = '/(?: |^)([AI])([[:upper:]])/';
519         // Split numerals from following letters.
520         $RE[] = '/(\d)([[:alpha:]])/';
521
522         foreach ($RE as $key => $val)
523             $RE[$key] = pcre_fix_posix_classes($val);
524     }
525
526     foreach ($RE as $regexp)
527         $page = preg_replace($regexp, '\\1 \\2', $page);
528     return $page;
529 }
530
531 function NoSuchRevision ($page, $version) {
532     $html = Element('p', QElement('strong', gettext("Bad Version"))) . "\n";
533     $html .= QElement('p',
534                       sprintf(gettext("I'm sorry.  Version %d of %s is not in my database."),
535                               $version, $page->getName())) . "\n";
536
537     include_once('lib/Template.php');
538     echo GeneratePage('MESSAGE', $html, gettext("Bad Version"));
539     ExitWiki ("");
540 }
541
542
543 // (c-file-style: "gnu")
544 // Local Variables:
545 // mode: php
546 // tab-width: 8
547 // c-basic-offset: 4
548 // c-hanging-comment-ender-p: nil
549 // indent-tabs-mode: nil
550 // End:   
551 ?>