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