]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/stdlib.php
log
[SourceForge/phpwiki.git] / lib / stdlib.php
1 <?php rcs_id('$Id: stdlib.php,v 1.29 2001-02-13 05:54:38 dairiki Exp $');
2
3
4    /*
5       Standard functions for Wiki functionality
6          ExitWiki($errormsg)
7          WikiURL($pagename, $args, $abs)
8          
9          LinkExistingWikiWord($wikiword, $linktext) 
10          LinkUnknownWikiWord($wikiword, $linktext) 
11          LinkURL($url, $linktext)
12          LinkImage($url, $alt)
13          LinkInterWikiLink($link, $linktext)
14          CookSpaces($pagearray) 
15          class Stack (push(), pop(), cnt(), top())
16          SetHTMLOutputMode($newmode, $depth)
17          UpdateRecentChanges($dbi, $pagename, $isnewpage) 
18          ParseAndLink($bracketlink)
19          ExtractWikiPageLinks($content)
20          LinkRelatedPages($dbi, $pagename)
21          GeneratePage($template, $content, $name, $hash)
22    */
23
24 function fix_magic_quotes_gpc (&$text)
25 {
26    if (get_magic_quotes_gpc()) {
27       $text = stripslashes($text);
28    }
29    return $text;
30 }
31
32
33 function get_remote_host () {
34    // Apache won't show REMOTE_HOST unless the admin configured it
35    // properly. We'll be nice and see if it's there.
36    if (getenv('REMOTE_HOST'))
37       return getenv('REMOTE_HOST');
38    $host = getenv('REMOTE_ADDR');
39    if (ENABLE_REVERSE_DNS)
40       return gethostbyaddr($host);
41    return $host;
42 }
43
44 function SearchPath ($file, $missing_ok = false, $path = false) 
45 {
46    if (ereg('^/', $file))
47       return $file;             // absolute path.
48
49    if (!$path)
50       $path = $GLOBALS['DataPath'];
51    
52    while (list($i, $dir) = each($path))
53    {
54       if (file_exists("$dir/$file"))
55          return "$dir/$file";
56    }
57    if ($missing_ok)
58       return false;
59    ExitWiki("$file: file not found");
60 }
61
62 function arrays_equal ($a, $b) 
63 {
64    if (sizeof($a) != sizeof($b))
65       return false;
66    for ($i = 0; $i < sizeof($a); $i++)
67       if ($a[$i] != $b[$i])
68          return false;
69    return true;
70 }
71
72    
73    
74
75
76    function DataURL($url) {
77       if (preg_match('@^(\w+:|/)@', $url))
78          return $url;
79       return SERVER_URL . DATA_PATH . "/$url";
80    }
81           
82    function WikiURL($pagename, $args = '') {
83       if (is_array($args))
84       {
85          reset($args);
86          $enc_args = array();
87          while (list ($key, $val) = each($args)) {
88             $enc_args[] = urlencode($key) . '=' . urlencode($val);
89          }
90          $args = join('&', $enc_args);
91       }
92
93       if (USE_PATH_INFO) {
94          $url = rawurlencode($pagename);
95          if ($args)
96             $url .= "?$args";
97       }
98       else {
99          $url = basename(SCRIPT_NAME) .
100              "?pagename=" . rawurlencode($pagename);
101          if ($args)
102             $url .= "&$args";
103       }
104
105       return $url;
106    }
107
108    define('NO_END_TAG_PAT',
109           '/^' . join('|', array('area', 'base', 'basefont',
110                                  'br', 'col', 'frame',
111                                  'hr', 'image', 'input',
112                                  'isindex', 'link', 'meta',
113                                  'param')) . '$/i');
114           
115    function Element($tag, $args = '', $content = '')
116    {
117       $html = "<$tag";
118       if (is_array($args))
119       {
120          while (list($key, $val) = each($args))
121          {
122             if (is_string($val) || is_numeric($val))
123                $html .= sprintf(' %s="%s"', $key, htmlspecialchars($val));
124             else if ($val)
125                $html .= " $key";
126          }
127       }
128       else
129          $content = $args;
130       
131       $html .= '>';
132       if (!preg_match(NO_END_TAG_PAT, $tag))
133       {
134          $html .= $content;
135          $html .= "</$tag>\n";//FIXME: newline might not always be desired.
136       }
137       return $html;
138    }
139
140    function QElement($tag, $args = '', $content = '')
141    {
142       if (is_array($args))
143          return Element($tag, $args, htmlspecialchars($content));
144       else
145       {
146          $content = $args;
147          return Element($tag, htmlspecialchars($content));
148       }
149    }
150    
151    function LinkURL($url, $linktext='') {
152       // FIXME: Is this needed (or sufficient?)
153       if(ereg("[<>\"]", $url)) {
154          return "<b><u>BAD URL -- remove all of &lt;, &gt;, &quot;</u></b>";
155       }
156       return QElement('a', array('href' => $url), ($linktext ? $linktext : $url));
157    }
158
159    function LinkExistingWikiWord($wikiword, $linktext='', $class = 'wikiword') {
160       return Element('a', array('href' => WikiURL($wikiword),
161                                 'class' => $class),
162                      $linktext ? $linktext : $wikiword);
163    }
164
165    function LinkUnknownWikiWord($wikiword, $linktext='', $class = 'unknownwikiword') {
166       if (empty($linktext))
167          $linktext = $wikiword;
168
169       return Element('span', array('class' => 'unknownwikiword'),
170                      QElement('u', array('class' => 'unknownwikiword'), $linktext) .
171                      QElement('a',
172                               array('href' => WikiURL($wikiword, array('action' => 'edit')),
173                                     'class' => 'unknownwikiword'),
174                               '?'));
175    }
176
177
178    function LinkImage($url, $alt='[External Image]') {
179       // FIXME: Is this needed (or sufficient?)
180       //  As long as the src in htmlspecialchars()ed I think it's safe.
181       if(ereg('[<>"]', $url)) {
182          return "<b><u>BAD URL -- remove all of &lt;, &gt;, &quot;</u></b>";
183       }
184       return Element('img', array('src' => $url, 'alt' => $alt));
185    }
186
187
188    // converts spaces to tabs
189    function CookSpaces($pagearray) {
190       return preg_replace("/ {3,8}/", "\t", $pagearray);
191    }
192
193
194    class Stack {
195       var $items = array();
196       var $size = 0;
197
198       function push($item) {
199          $this->items[$this->size] = $item;
200          $this->size++;
201          return true;
202       }  
203    
204       function pop() {
205          if ($this->size == 0) {
206             return false; // stack is empty
207          }  
208          $this->size--;
209          return $this->items[$this->size];
210       }  
211    
212       function cnt() {
213          return $this->size;
214       }  
215
216       function top() {
217          if($this->size)
218             return $this->items[$this->size - 1];
219          else
220             return '';
221       }  
222
223    }  
224    // end class definition
225
226
227    function MakeWikiForm ($pagename, $args, $button_text = '')
228    {
229       $formargs['action'] = USE_PATH_INFO ? WikiURL($pagename) : SCRIPT_NAME;
230       $formargs['method'] = 'post';
231       $contents = '';
232       $input_seen = 0;
233       
234       while (list($key, $val) = each($args))
235       {
236          $a = array('name' => $key, 'value' => $val, 'type' => 'hidden');
237          
238          if (preg_match('/^ (\d*) \( (.*) \) ((upload)?) $/xi', $val, $m))
239          {
240             $input_seen++;
241             $a['type'] = 'text';
242             $a['size'] = $m[1] ? $m[1] : 30;
243             $a['value'] = $m[2];
244             if ($m[3])
245             {
246                $a['type'] = 'file';
247                $formargs['enctype'] = 'multipart/form-data';
248                $contents .= Element('input',
249                                     array('name' => 'MAX_FILE_SIZE',
250                                           'value' => MAX_UPLOAD_SIZE,
251                                           'type' => 'hidden'));
252             }
253          }
254
255          $contents .= Element('input', $a);
256       }
257
258       $row = Element('td', $contents);
259       
260       if (!empty($button_text)) {
261          $row .= Element('td', Element('input', array('type' => 'submit',
262                                                       'value' => $button_text)));
263       }
264
265       return Element('form', $formargs,
266                      Element('table',
267                              Element('tr', $row)));
268    }
269
270    function SplitQueryArgs ($query_args = '') 
271    {
272       $split_args = split('&', $query_args);
273       $args = array();
274       while (list($key, $val) = each($split_args))
275          if (preg_match('/^ ([^=]+) =? (.*) /x', $val, $m))
276             $args[$m[1]] = $m[2];
277       return $args;
278    }
279    
280    function LinkPhpwikiURL($url, $text = '') {
281       global $pagename;
282       $args = array();
283       $page = $pagename;
284
285       if (!preg_match('/^ phpwiki: ([^?]*) [?]? (.*) $/x', $url, $m))
286          return "<b><u>BAD phpwiki: URL</u></b>";
287
288       if ($m[1])
289          $page = urldecode($m[1]);
290       $qargs = $m[2];
291       
292       if (!$page && preg_match('/^(diff|edit|links|info|diff)=([^&]+)$/', $qargs, $m))
293       {
294          // Convert old style links (to not break diff links in RecentChanges).
295          $page = urldecode($m[2]);
296          $args = array("action" => $m[1]);
297       }
298       else
299       {
300          $args = SplitQueryArgs($qargs);
301       }
302
303
304       // FIXME: ug, don't like this
305       
306       if (!empty($args['action']) && !IsSafeAction($args['action']))
307       {
308          // Don't allow administrative links on unlocked pages.
309          global $pagehash;
310          if (($pagehash['flags'] & FLAG_PAGE_LOCKED) == 0)
311             return QElement('u', gettext('Lock page to enable link'));
312       }
313       
314       // FIXME: ug, don't like this
315       if (preg_match('/=\d*\(/', $qargs))
316          return MakeWikiForm($page, $args, $text);
317       else
318          return LinkURL(WikiURL($page, $args), $text ? $text : $url);
319    }
320
321    function ParseAndLink($bracketlink) {
322       global $dbi, $AllowedProtocols, $InlineImages;
323       global $InterWikiLinkRegexp;
324
325       // $bracketlink will start and end with brackets; in between
326       // will be either a page name, a URL or both separated by a pipe.
327
328       // strip brackets and leading space
329       preg_match("/(\[\s*)(.+?)(\s*\])/", $bracketlink, $match);
330       // match the contents 
331       preg_match("/([^|]+)(\|)?([^|]+)?/", $match[2], $matches);
332
333       if (isset($matches[3])) {
334          // named link of the form  "[some link name | http://blippy.com/]"
335          $URL = trim($matches[3]);
336          $linkname = trim($matches[1]);
337          $linktype = 'named';
338       } else {
339          // unnamed link of the form "[http://blippy.com/] or [wiki page]"
340          $URL = trim($matches[1]);
341          $linkname = '';
342          $linktype = 'simple';
343       }
344
345       if (IsWikiPage($dbi, $URL)) {
346          $link['type'] = "wiki-$linktype";
347          $link['link'] = LinkExistingWikiWord($URL, $linkname);
348       } elseif (preg_match("#^($AllowedProtocols):#", $URL)) {
349         // if it's an image, embed it; otherwise, it's a regular link
350          if (preg_match("/($InlineImages)$/i", $URL)) {
351             $link['type'] = "image-$linktype";
352             $link['link'] = LinkImage($URL, $linkname);
353          } else {
354             $link['type'] = "url-$linktype";
355             $link['link'] = LinkURL($URL, $linkname);
356          }
357       } elseif (preg_match("#^phpwiki:(.*)#", $URL, $match)) {
358          $link['type'] = "url-wiki-$linktype";
359          $link['link'] = LinkPhpwikiURL($URL, $linkname);
360       } elseif (preg_match("#^\d+$#", $URL)) {
361          $link['type'] = "footnote-$linktype";
362          $link['link'] = $URL;
363       } elseif (function_exists('LinkInterWikiLink') &&
364                 preg_match("#^$InterWikiLinkRegexp:#", $URL)) {
365          $link['type'] = "interwiki-$linktype";
366          $link['link'] = LinkInterWikiLink($URL, $linkname);
367       } else {
368          $link['type'] = "wiki-unknown-$linktype";
369          $link['link'] = LinkUnknownWikiWord($URL, $linkname);
370       }
371
372       return $link;
373    }
374
375
376    function ExtractWikiPageLinks($content)
377    {
378       global $WikiNameRegexp;
379
380       $wikilinks = array();
381       $numlines = count($content);
382       for($l = 0; $l < $numlines; $l++)
383       {
384          // remove escaped '['
385          $line = str_replace('[[', ' ', $content[$l]);
386
387          // bracket links (only type wiki-* is of interest)
388          $numBracketLinks = preg_match_all("/\[\s*([^\]|]+\|)?\s*(.+?)\s*\]/", $line, $brktlinks);
389          for ($i = 0; $i < $numBracketLinks; $i++) {
390             $link = ParseAndLink($brktlinks[0][$i]);
391             if (preg_match("#^wiki#", $link['type']))
392                $wikilinks[$brktlinks[2][$i]] = 1;
393
394             $brktlink = preg_quote($brktlinks[0][$i]);
395             $line = preg_replace("|$brktlink|", '', $line);
396          }
397
398          // BumpyText old-style wiki links
399          if (preg_match_all("/!?$WikiNameRegexp/", $line, $link)) {
400             for ($i = 0; isset($link[0][$i]); $i++) {
401                if($link[0][$i][0] <> '!')
402                   $wikilinks[$link[0][$i]] = 1;
403             }
404          }
405       }
406       return $wikilinks;
407    }      
408
409    function LinkRelatedPages($dbi, $pagename)
410    {
411       // currently not supported everywhere
412       if(!function_exists('GetWikiPageLinks'))
413          return '';
414
415       $links = GetWikiPageLinks($dbi, $pagename);
416
417       $txt = "<b>";
418       $txt .= sprintf (gettext ("%d best incoming links:"), NUM_RELATED_PAGES);
419       $txt .= "</b>\n";
420       for($i = 0; $i < NUM_RELATED_PAGES; $i++) {
421          if(isset($links['in'][$i])) {
422             list($name, $score) = $links['in'][$i];
423             $txt .= LinkExistingWikiWord($name) . " ($score), ";
424          }
425       }
426
427       $txt .= "\n<br><b>";
428       $txt .= sprintf (gettext ("%d best outgoing links:"), NUM_RELATED_PAGES);
429       $txt .= "</b>\n";
430       for($i = 0; $i < NUM_RELATED_PAGES; $i++) {
431          if(isset($links['out'][$i])) {
432             list($name, $score) = $links['out'][$i];
433             if(IsWikiPage($dbi, $name))
434                $txt .= LinkExistingWikiWord($name) . " ($score), ";
435          }
436       }
437
438       $txt .= "\n<br><b>";
439       $txt .= sprintf (gettext ("%d most popular nearby:"), NUM_RELATED_PAGES);
440       $txt .= "</b>\n";
441       for($i = 0; $i < NUM_RELATED_PAGES; $i++) {
442          if(isset($links['popular'][$i])) {
443             list($name, $score) = $links['popular'][$i];
444             $txt .= LinkExistingWikiWord($name) . " ($score), ";
445          }
446       }
447       
448       return $txt;
449    }
450
451    
452    # GeneratePage() -- takes $content and puts it in the template $template
453    # this function contains all the template logic
454    #
455    # $template ... name of the template (see config.php for list of names)
456    # $content ... html content to put into the page
457    # $name ... page title
458    # $hash ... if called while creating a wiki page, $hash points to
459    #           the $pagehash array of that wiki page.
460
461    function GeneratePage($template, $content, $name, $hash)
462    {
463       global $templates;
464       global $datetimeformat, $dbi, $logo, $FieldSeparator;
465       global $user, $pagename;
466       
467       if (!is_array($hash))
468          unset($hash);
469
470       function _dotoken ($id, $val, &$page) {
471          global $FieldSeparator;
472          $page = str_replace("$FieldSeparator#$id$FieldSeparator#",
473                                 $val, $page);
474       }
475
476       function _iftoken ($id, $condition, &$page) {
477          global $FieldSeparator;
478
479          // line based IF directive
480          $lineyes = "$FieldSeparator#IF $id$FieldSeparator#";
481          $lineno = "$FieldSeparator#IF !$id$FieldSeparator#";
482          // block based IF directive
483          $blockyes = "$FieldSeparator#IF:$id$FieldSeparator#";
484          $blockyesend = "$FieldSeparator#ENDIF:$id$FieldSeparator#";
485          $blockno = "$FieldSeparator#IF:!$id$FieldSeparator#";
486          $blocknoend = "$FieldSeparator#ENDIF:!$id$FieldSeparator#";
487
488          if ($condition) {
489             $page = str_replace($lineyes, '', $page);
490             $page = str_replace($blockyes, '', $page);
491             $page = str_replace($blockyesend, '', $page);
492             $page = preg_replace("/$blockno(.*?)$blocknoend/s", '', $page);
493             $page = ereg_replace("${lineno}[^\n]*\n", '', $page);
494          } else {
495             $page = str_replace($lineno, '', $page);
496             $page = str_replace($blockno, '', $page);
497             $page = str_replace($blocknoend, '', $page);
498             $page = preg_replace("/$blockyes(.*?)$blockyesend/s", '', $page);
499             $page = ereg_replace("${lineyes}[^\n]*\n", '', $page);
500          }
501       }
502
503       $page = join('', file(SearchPath($templates[$template])));
504       $page = str_replace('###', "$FieldSeparator#", $page);
505
506       // valid for all pagetypes
507       _iftoken('COPY', isset($hash['copy']), $page);
508       _iftoken('LOCK',  (isset($hash['flags']) &&
509                         ($hash['flags'] & FLAG_PAGE_LOCKED)), $page);
510       _iftoken('ADMIN', $user->is_admin(), $page);
511       _iftoken('ANONYMOUS', !$user->is_authenticated(), $page);
512
513       if (empty($hash['minor_edit_checkbox']))
514           $hash['minor_edit_checkbox'] = '';
515       _iftoken('MINOR_EDIT_CHECKBOX', $hash['minor_edit_checkbox'], $page);
516       
517       _dotoken('MINOR_EDIT_CHECKBOX', $hash['minor_edit_checkbox'], $page);
518
519       _dotoken('USERID', htmlspecialchars($user->id()), $page);
520       _dotoken('PAGE', htmlspecialchars($name), $page);
521       _dotoken('LOGO', htmlspecialchars(DataURL($logo)), $page);
522
523       _dotoken('RCS_IDS', $GLOBALS['RCS_IDS'], $page);
524
525       $prefs = $user->getPreferences();
526       _dotoken('EDIT_AREA_WIDTH', $prefs['edit_area.width'], $page);
527       _dotoken('EDIT_AREA_HEIGHT', $prefs['edit_area.height'], $page);
528
529       // FIXME: Clean up this stuff
530       $browse_page = WikiURL($name);
531       _dotoken('BROWSE_PAGE', $browse_page, $page);
532       $arg_sep = strstr($browse_page, '?') ? '&amp;' : '?';
533       _dotoken('ACTION', $browse_page . $arg_sep . "action=", $page);
534       _dotoken('BROWSE', WikiURL(''), $page);
535
536       if (USE_PATH_INFO)
537          _dotoken('BASE_URL',
538                   SERVER_URL . VIRTUAL_PATH . "/" . WikiURL($pagename), $page);
539       else
540          _dotoken('BASE_URL', SERVER_URL . SCRIPT_NAME, $page);
541       
542       // invalid for messages (search results, error messages)
543       if ($template != 'MESSAGE') {
544          _dotoken('PAGEURL', rawurlencode($name), $page);
545          if (!empty($hash['lastmodified']))
546             _dotoken('LASTMODIFIED',
547                      date($datetimeformat, $hash['lastmodified']), $page);
548          if (!empty($hash['author']))
549             _dotoken('LASTAUTHOR', $hash['author'], $page);
550          if (!empty($hash['version']))
551             _dotoken('VERSION', $hash['version'], $page);
552          if (strstr($page, "$FieldSeparator#HITS$FieldSeparator#")) {
553             _dotoken('HITS', GetHitCount($dbi, $name), $page);
554          }
555          if (strstr($page, "$FieldSeparator#RELATEDPAGES$FieldSeparator#")) {
556             _dotoken('RELATEDPAGES', LinkRelatedPages($dbi, $name), $page);
557          }
558       }
559
560       _dotoken('CONTENT', $content, $page);
561       return $page;
562    }
563
564 function UpdateRecentChanges($dbi, $pagename, $isnewpage)
565 {
566    global $user;
567    global $dateformat;
568    global $WikiPageStore;
569
570    $recentchanges = RetrievePage($dbi, gettext ("RecentChanges"), $WikiPageStore);
571
572    // this shouldn't be necessary, since PhpWiki loads 
573    // default pages if this is a new baby Wiki
574    if ($recentchanges == -1) {
575       $recentchanges = array(); 
576    }
577
578    $now = time();
579    $today = date($dateformat, $now);
580
581    if (date($dateformat, $recentchanges['lastmodified']) != $today) {
582       $isNewDay = TRUE;
583       $recentchanges['lastmodified'] = $now;
584    } else {
585       $isNewDay = FALSE;
586    }
587
588    $numlines = sizeof($recentchanges['content']);
589    $newpage = array();
590    $k = 0;
591
592    // scroll through the page to the first date and break
593    // dates are marked with "____" at the beginning of the line
594    for ($i = 0; $i < $numlines; $i++) {
595       if (preg_match("/^____/",
596                      $recentchanges['content'][$i])) {
597          break;
598       } else {
599          $newpage[$k++] = $recentchanges['content'][$i];
600       }
601    }
602
603    // if it's a new date, insert it
604    $newpage[$k++] = $isNewDay ? "____$today\r"
605                               : $recentchanges['content'][$i++];
606
607    $userid = $user->id();
608
609    // add the updated page's name to the array
610    if($isnewpage) {
611       $newpage[$k++] = "* [$pagename] (new) ..... $userid\r";
612    } else {
613       $diffurl = "phpwiki:" . rawurlencode($pagename) . "?action=diff";
614       $newpage[$k++] = "* [$pagename] ([diff|$diffurl]) ..... $userid\r";
615    }
616    if ($isNewDay)
617       $newpage[$k++] = "\r";
618
619    // copy the rest of the page into the new array
620    // and skip previous entry for $pagename
621    $pagename = preg_quote($pagename);
622    for (; $i < $numlines; $i++) {
623       if (!preg_match("|\[$pagename\]|", $recentchanges['content'][$i])) {
624          $newpage[$k++] = $recentchanges['content'][$i];
625       }
626    }
627
628    $recentchanges['content'] = $newpage;
629
630    InsertPage($dbi, gettext ("RecentChanges"), $recentchanges);
631 }
632
633 // For emacs users
634 // Local Variables:
635 // mode: php
636 // c-file-style: "ellemtel"
637 // End:   
638 ?>