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