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