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