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