]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/loadsave.php
Copy locally uploaded images also
[SourceForge/phpwiki.git] / lib / loadsave.php
1 <?php //-*-php-*-
2 rcs_id('$Id: loadsave.php,v 1.155 2007-09-12 19:40:41 rurban Exp $');
3
4 /*
5  Copyright 1999,2000,2001,2002,2004,2005,2006,2007 $ThePhpWikiProgrammingTeam
6
7  This file is part of PhpWiki.
8
9  PhpWiki is free software; you can redistribute it and/or modify
10  it under the terms of the GNU General Public License as published by
11  the Free Software Foundation; either version 2 of the License, or
12  (at your option) any later version.
13
14  PhpWiki is distributed in the hope that it will be useful,
15  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  GNU General Public License for more details.
18
19  You should have received a copy of the GNU General Public License
20  along with PhpWiki; if not, write to the Free Software
21  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  */
23
24 require_once("lib/ziplib.php");
25 require_once("lib/Template.php");
26
27 /**
28  * ignore fatal errors during dump
29  */
30 function _dump_error_handler(&$error) {
31     if ($error->isFatal()) {
32         $error->errno = E_USER_WARNING;
33         return true;
34     }
35     return true;         // Ignore error
36     /*
37     if (preg_match('/Plugin/', $error->errstr))
38         return true;
39     */
40     // let the message come through: call the remaining handlers:
41     // return false; 
42 }
43
44 function StartLoadDump(&$request, $title, $html = '')
45 {
46     // MockRequest is from the unit testsuite, a faked request. (may be cmd-line)
47     // We are silent on unittests.
48     if (isa($request,'MockRequest'))
49         return;
50     // FIXME: This is a hack. This really is the worst overall hack in phpwiki.
51     if ($html)
52         $html->pushContent('%BODY%');
53     $tmpl = Template('html', array('TITLE' => $title,
54                                    'HEADER' => $title,
55                                    'CONTENT' => $html ? $html : '%BODY%'));
56     echo ereg_replace('%BODY%.*', '', $tmpl->getExpansion($html));
57     $request->chunkOutput();
58     
59     // set marker for sendPageChangeNotification()
60     $request->_deferredPageChangeNotification = array();
61 }
62
63 function EndLoadDump(&$request)
64 {
65     if (isa($request,'MockRequest'))
66         return;
67     $action = $request->getArg('action');
68     $label = '';
69     switch ($action) {
70     case 'zip':        $label = _("ZIP files of database"); break;
71     case 'dumpserial': $label = _("Dump to directory"); break;
72     case 'upload':     $label = _("Upload File"); break;
73     case 'loadfile':   $label = _("Load File"); break;
74     case 'upgrade':    $label = _("Upgrade"); break;
75     case 'dumphtml': 
76     case 'ziphtml':    $label = _("Dump pages as XHTML"); break;
77     }
78     if ($label) $label = str_replace(" ","_",$label);
79     if ($action == 'browse') // loading virgin 
80         $pagelink = WikiLink(HOME_PAGE);
81     else
82         $pagelink = WikiLink(new WikiPageName(_("PhpWikiAdministration"),false,$label));
83
84     // do deferred sendPageChangeNotification()
85     if (!empty($request->_deferredPageChangeNotification)) {
86         $pages = $all_emails = $all_users = array();
87         foreach ($request->_deferredPageChangeNotification as $p) {
88             list($pagename, $emails, $userids) = $p;
89             $pages[] = $pagename;
90             $all_emails = array_unique(array_merge($all_emails, $emails));
91             $all_users = array_unique(array_merge($all_users, $userids));
92         }
93         $editedby = sprintf(_("Edited by: %s"), $request->_user->getId());
94         $content = "Loaded the following pages:\n" . join("\n", $pages);
95         if (mail(join(',',$all_emails),"[".WIKI_NAME."] "._("LoadDump"), 
96                  _("LoadDump")."\n".
97                  $editedby."\n\n".
98                  $content))
99             trigger_error(sprintf(_("PageChange Notification of %s sent to %s"),
100                                   join("\n",$pages), join(',',$all_users)), E_USER_NOTICE);
101         else
102             trigger_error(sprintf(_("PageChange Notification Error: Couldn't send %s to %s"),
103                                   join("\n",$pages), join(',',$all_users)), E_USER_WARNING);
104         unset($pages);
105         unset($all_emails);
106         unset($all_users);
107     }
108     unset($request->_deferredPageChangeNotification);
109
110     PrintXML(HTML::p(HTML::strong(_("Complete."))),
111              HTML::p(fmt("Return to %s", $pagelink)));
112     echo "</body></html>\n";
113 }
114
115
116 ////////////////////////////////////////////////////////////////
117 //
118 //  Functions for dumping.
119 //
120 ////////////////////////////////////////////////////////////////
121
122 /**
123  * For reference see:
124  * http://www.nacs.uci.edu/indiv/ehood/MIME/2045/rfc2045.html
125  * http://www.faqs.org/rfcs/rfc2045.html
126  * (RFC 1521 has been superceeded by RFC 2045 & others).
127  *
128  * Also see http://www.faqs.org/rfcs/rfc2822.html
129  */
130 function MailifyPage ($page, $nversions = 1)
131 {
132     $current = $page->getCurrentRevision(false);
133     $head = '';
134
135     if (STRICT_MAILABLE_PAGEDUMPS) {
136         $from = defined('SERVER_ADMIN') ? SERVER_ADMIN : 'foo@bar';
137         //This is for unix mailbox format: (not RFC (2)822)
138         // $head .= "From $from  " . CTime(time()) . "\r\n";
139         $head .= "Subject: " . rawurlencode($page->getName()) . "\r\n";
140         $head .= "From: $from (PhpWiki)\r\n";
141         // RFC 2822 requires only a Date: and originator (From:)
142         // field, however the obsolete standard RFC 822 also
143         // requires a destination field.
144         $head .= "To: $from (PhpWiki)\r\n";
145     }
146     $head .= "Date: " . Rfc2822DateTime($current->get('mtime')) . "\r\n";
147     $head .= sprintf("Mime-Version: 1.0 (Produced by PhpWiki %s)\r\n",
148                      PHPWIKI_VERSION);
149
150     // This should just be entered by hand (or by script?)
151     // in the actual pgsrc files, since only they should have
152     // RCS ids.
153     //$head .= "X-Rcs-Id: \$Id\$\r\n";
154
155     $iter = $page->getAllRevisions();
156     $parts = array();
157     while ($revision = $iter->next()) {
158         $parts[] = MimeifyPageRevision($page, $revision);
159         if ($nversions > 0 && count($parts) >= $nversions)
160             break;
161     }
162     if (count($parts) > 1)
163         return $head . MimeMultipart($parts);
164     assert($parts);
165     return $head . $parts[0];
166 }
167
168 /***
169  * Compute filename to used for storing contents of a wiki page.
170  *
171  * Basically we do a rawurlencode() which encodes everything except
172  * ASCII alphanumerics and '.', '-', and '_'.
173  *
174  * But we also want to encode leading dots to avoid filenames like
175  * '.', and '..'. (Also, there's no point in generating "hidden" file
176  * names, like '.foo'.)
177  *
178  * We have to apply a different "/" logic for dumpserial, htmldump and zipdump.
179  * dirs are allowed for zipdump and htmldump, not for dumpserial
180  * 
181  *
182  * @param $pagename string Pagename.
183  * @return string Filename for page.
184  */
185 function FilenameForPage ($pagename, $action = false)
186 {
187     $enc = rawurlencode($pagename);
188     if (!$action) {
189         global $request;
190         $action = $request->getArg('action');
191     }
192     if ($action != 'dumpserial') { // zip, ziphtml, dumphtml
193         // For every %2F we will need to mkdir -p dirname($pagename)
194         $enc = preg_replace('/%2F/', '/', $enc);
195     }
196     $enc = preg_replace('/^\./', '%2E', $enc);
197     $enc = preg_replace('/%20/', ' ', $enc);
198     return $enc;
199 }
200
201 /**
202  * The main() function which generates a zip archive of a PhpWiki.
203  *
204  * If $include_archive is false, only the current version of each page
205  * is included in the zip file; otherwise all archived versions are
206  * included as well.
207  */
208 function MakeWikiZip (&$request)
209 {
210     if ($request->getArg('include') == 'all') {
211         $zipname         = WIKI_NAME . _("FullDump") . date('Ymd-Hi') . '.zip';
212         $include_archive = true;
213     }
214     else {
215         $zipname         = WIKI_NAME . _("LatestSnapshot") . date('Ymd-Hi') . '.zip';
216         $include_archive = false;
217     }
218
219
220     $zip = new ZipWriter("Created by PhpWiki " . PHPWIKI_VERSION, $zipname);
221
222     /* ignore fatals in plugins */
223     if (check_php_version(4,1)) {
224         global $ErrorManager;
225         $ErrorManager->pushErrorHandler(new WikiFunctionCb('_dump_error_handler'));
226     }
227
228     $dbi =& $request->_dbi;
229     $thispage = $request->getArg('pagename'); // for "Return to ..."
230     if ($exclude = $request->getArg('exclude')) {   // exclude which pagenames
231         $excludeList = explodePageList($exclude); 
232     } else {
233         $excludeList = array();
234     }
235     if ($pages = $request->getArg('pages')) {  // which pagenames
236         if ($pages == '[]') // current page
237             $pages = $thispage;
238         $page_iter = new WikiDB_Array_PageIterator(explodePageList($pages));
239     } else {
240         $page_iter = $dbi->getAllPages(false,false,false,$excludeList);
241     }
242     $request_args = $request->args;
243     $timeout = (! $request->getArg('start_debug')) ? 30 : 240;
244     
245     while ($page = $page_iter->next()) {
246         $request->args = $request_args; // some plugins might change them (esp. on POST)
247         longer_timeout($timeout);       // Reset watchdog
248
249         $current = $page->getCurrentRevision();
250         if ($current->getVersion() == 0)
251             continue;
252
253         $pagename = $page->getName();
254         $wpn = new WikiPageName($pagename);
255         if (!$wpn->isValid())
256             continue;
257         if (in_array($page->getName(), $excludeList)) {
258             continue;
259         }
260
261         $attrib = array('mtime'    => $current->get('mtime'),
262                         'is_ascii' => 1);
263         if ($page->get('locked'))
264             $attrib['write_protected'] = 1;
265
266         if ($include_archive)
267             $content = MailifyPage($page, 0);
268         else
269             $content = MailifyPage($page);
270
271         $zip->addRegularFile( FilenameForPage($pagename),
272                               $content, $attrib);
273     }
274     $zip->finish();
275     if (check_php_version(4,1)) {
276         global $ErrorManager;
277         $ErrorManager->popErrorHandler();
278     }
279 }
280
281 function DumpToDir (&$request)
282 {
283     $directory = $request->getArg('directory');
284     if (empty($directory))
285         $directory = DEFAULT_DUMP_DIR; // See lib/plugin/WikiForm.php:87
286     if (empty($directory))
287         $request->finish(_("You must specify a directory to dump to"));
288
289     // see if we can access the directory the user wants us to use
290     if (! file_exists($directory)) {
291         if (! mkdir($directory, 0755))
292             $request->finish(fmt("Cannot create directory '%s'", $directory));
293         else
294             $html = HTML::p(fmt("Created directory '%s' for the page dump...",
295                                 $directory));
296     } else {
297         $html = HTML::p(fmt("Using directory '%s'", $directory));
298     }
299
300     StartLoadDump($request, _("Dumping Pages"), $html);
301
302     $dbi =& $request->_dbi;
303     $thispage = $request->getArg('pagename'); // for "Return to ..."
304     if ($exclude = $request->getArg('exclude')) {   // exclude which pagenames
305         $excludeList = explodePageList($exclude); 
306     } else {
307         $excludeList = array();
308     }
309     if ($pages = $request->getArg('pages')) {  // which pagenames
310         if ($pages == '[]') // current page
311             $pages = $thispage;
312         $page_iter = new WikiDB_Array_PageIterator(explodePageList($pages));
313     } else {
314         $page_iter = $dbi->getAllPages(false,false,false,$excludeList);
315     }
316
317     $request_args = $request->args;
318     $timeout = (! $request->getArg('start_debug')) ? 30 : 240;
319
320     while ($page = $page_iter->next()) {
321         $request->args = $request_args; // some plugins might change them (esp. on POST)
322         longer_timeout($timeout);       // Reset watchdog
323
324         $pagename = $page->getName();
325         if (!isa($request,'MockRequest')) {
326             PrintXML(HTML::br(), $pagename, ' ... ');
327             flush();
328         }
329
330         if (in_array($pagename, $excludeList)) {
331             if (!isa($request, 'MockRequest')) {
332                 PrintXML(_("Skipped."));
333                 flush();
334             }
335             continue;
336         }
337         $filename = FilenameForPage($pagename);
338         $msg = HTML();
339         if($page->getName() != $filename) {
340             $msg->pushContent(HTML::small(fmt("saved as %s", $filename)),
341                               " ... ");
342         }
343
344         if ($request->getArg('include') == 'all')
345             $data = MailifyPage($page, 0);
346         else
347             $data = MailifyPage($page);
348
349         if ( !($fd = fopen($directory."/".$filename, "wb")) ) {
350             $msg->pushContent(HTML::strong(fmt("couldn't open file '%s' for writing",
351                                                "$directory/$filename")));
352             $request->finish($msg);
353         }
354
355         $num = fwrite($fd, $data, strlen($data));
356         $msg->pushContent(HTML::small(fmt("%s bytes written", $num)));
357         if (!isa($request, 'MockRequest')) {
358             PrintXML($msg);
359             flush();
360         }
361         assert($num == strlen($data));
362         fclose($fd);
363     }
364
365     EndLoadDump($request);
366 }
367
368 function _copyMsg($page, $smallmsg) {
369     if (!isa($GLOBALS['request'], 'MockRequest')) {
370         if ($page) $msg = HTML(HTML::br(), HTML($page), HTML::small($smallmsg));
371         else $msg = HTML::small($smallmsg);
372         PrintXML($msg);
373         flush();
374     }
375 }
376
377 function mkdir_p($pathname, $permission = 0777) {
378     $arr = explode("/", $pathname);
379     if (empty($arr)) {
380         return mkdir($pathname, $permission);
381     }
382     $s = array_shift($arr);
383     $ok = TRUE;
384     foreach ($arr as $p) {
385         $curr = "$s/$p";
386         if (!is_dir($curr))
387             $ok = mkdir($curr, $permission);
388         $s = $curr;
389         if (!$ok) return FALSE;
390     }
391     return TRUE;
392 }
393
394 /**
395  * Dump all pages as XHTML to a directory, as pagename.html.
396  * Copies all used css files to the directory, all used images to a 
397  * "images" subdirectory, and all used buttons to a "images/buttons" subdirectory.
398  * The webserver must have write permissions to these directories. 
399  *   chown httpd HTML_DUMP_DIR; chmod u+rwx HTML_DUMP_DIR 
400  * should be enough.
401  *
402  * @param string directory (optional) path to dump to. Default: HTML_DUMP_DIR
403  * @param string pages     (optional) Comma-seperated of glob-style pagenames to dump.
404  *                                    Also array of pagenames allowed.
405  * @param string exclude   (optional) Comma-seperated of glob-style pagenames to exclude
406  */
407 function DumpHtmlToDir (&$request)
408 {
409     $directory = $request->getArg('directory');
410     if (empty($directory))
411         $directory = HTML_DUMP_DIR; // See lib/plugin/WikiForm.php:87
412     if (empty($directory))
413         $request->finish(_("You must specify a directory to dump to"));
414
415     // See if we can access the directory the user wants us to use
416     if (! file_exists($directory)) {
417         if (! mkdir($directory, 0755))
418             $request->finish(fmt("Cannot create directory '%s'", $directory));
419         else
420             $html = HTML::p(fmt("Created directory '%s' for the page dump...",
421                                 $directory));
422     } else {
423         $html = HTML::p(fmt("Using directory '%s'", $directory));
424     }
425     $request->_TemplatesProcessed = array();
426     StartLoadDump($request, _("Dumping Pages"), $html);
427     $thispage = $request->getArg('pagename'); // for "Return to ..."
428
429     $dbi =& $request->_dbi;
430     if ($exclude = $request->getArg('exclude')) {   // exclude which pagenames
431         $excludeList = explodePageList($exclude);
432     } else {
433         $excludeList = array('DebugAuthInfo', 'DebugGroupInfo', 'AuthInfo');
434     }
435     if ($pages = $request->getArg('pages')) {  // which pagenames
436         if ($pages == '[]') // current page
437             $pages = $thispage;
438         $page_iter = new WikiDB_Array_PageIterator(explodePageList($pages));
439     // not at admin page: dump only the current page
440     } elseif ($thispage != _("PhpWikiAdministration")) { 
441         $page_iter = new WikiDB_Array_PageIterator(array($thispage));
442     } else {
443         $page_iter = $dbi->getAllPages(false,false,false,$excludeList);
444     }
445
446     global $WikiTheme;
447     if (defined('HTML_DUMP_SUFFIX'))
448         $WikiTheme->HTML_DUMP_SUFFIX = HTML_DUMP_SUFFIX;
449     $WikiTheme->DUMP_MODE = 'HTML';
450     $_bodyAttr = @$WikiTheme->_MoreAttr['body'];
451     unset($WikiTheme->_MoreAttr['body']);
452
453     // check if the dumped file will be accessible from outside
454     $doc_root = $request->get("DOCUMENT_ROOT");
455     $ldir = NormalizeLocalFileName($directory);
456     $wikiroot = NormalizeLocalFileName('');
457     if (string_starts_with($ldir, $doc_root)) {
458         $link_prefix = substr($directory, strlen($doc_root))."/";
459     } elseif (string_starts_with($ldir, $wikiroot)) {
460         $link_prefix = NormalizeWebFileName(substr($directory, strlen($wikiroot)))."/";
461     } else {
462         $prefix = '';
463         if (isWindows()) {
464             $prefix = '/'; // . substr($doc_root,0,2); // add drive where apache is installed
465         }
466         $link_prefix = "file://".$prefix.$directory."/";
467     }
468
469     $request_args = $request->args;
470     $timeout = (! $request->getArg('start_debug')) ? 20 : 240;
471     $SAVE_RCS_IDS = $GLOBALS['RCS_IDS'];
472     @mkdir("$directory/images");
473     $doc_root = $request->get("DOCUMENT_ROOT");
474     
475     while ($page = $page_iter->next()) {
476         $request->args = $request_args; // some plugins might change them (esp. on POST)
477         longer_timeout($timeout);       // Reset watchdog
478           
479         $pagename = $page->getName();
480         if (!isa($request,'MockRequest')) {
481             PrintXML(HTML::br(), $pagename, ' ... ');
482             flush();
483         }
484         if (in_array($pagename, $excludeList)) {
485             if (!isa($request,'MockRequest')) {
486                 PrintXML(_("Skipped."));
487                 flush();
488             }
489             continue;
490         }
491         $relative_base = '';
492         $request->setArg('pagename', $pagename); // Template::_basepage fix
493         $filename = FilenameForPage($pagename) . $WikiTheme->HTML_DUMP_SUFFIX;
494         $revision = $page->getCurrentRevision();
495         $args = array('revision' => $revision,
496                       'CONTENT' => $revision->getTransformedContent(),
497                       'relative_base' => $relative_base);
498         // For every %2F will need to mkdir -p dirname($pagename)
499         if (preg_match("/(%2F|\/)/", $filename)) {
500             // mkdir -p and set relative base for subdir pages
501             $filename = preg_replace("/%2F/", "/", $filename);
502             $count = substr_count($filename, "/");
503             $dirname = dirname($filename);
504             mkdir_p($directory."/".$dirname);
505             // Fails with "XX / YY", "XX" is created, "XX / YY" cannot be written
506             if (isWindows()) // interesting Windows bug: cannot mkdir "bla "
507                 $filename = preg_replace("/ \//", "/", $filename);
508             $relative_base = "../";
509             while ($count > 1) {
510                 $relative_base .= "../";
511                 $count--;
512             }
513             $args['relative_base'] = $relative_base;
514         }
515         $msg = HTML();
516         $template = new Template('browse', $request, $args);
517
518         $data = GeneratePageasXML($template, $pagename, $revision, $args);
519
520         $WikiTheme->other_images = array();
521         if (preg_match_all("/<img .*?src=\"(\/.+?)\"/", $data, $m)) {
522             // fix to local relative path for uploaded images, so that pdf will work
523             foreach ($m[1] as $img_file) {
524                 $base = basename($img_file);
525                 $data = str_replace('src="'.$img_file.'"','src="images/'.$base.'"', $data);
526                 // resolve src from webdata to file
527                 $src = $doc_root . $img_file;
528                 if (file_exists($src) and $base) {
529                     $target = "$directory/images/$base";
530                     if (copy($src, $target)) {
531                         _copyMsg($img_file, fmt("... copied to %s", $target));
532                     } else {
533                         _copyMsg($img_file, fmt("... not copied to %s", $target));
534                     }
535                 }
536             }
537         }
538         
539         if ( !($fd = fopen($directory."/".$filename, "wb")) ) {
540             $msg->pushContent(HTML::strong(fmt("couldn't open file '%s' for writing",
541                                                "$directory/$filename")));
542             $request->finish($msg);
543         }
544         $len = strlen($data);
545         $num = fwrite($fd, $data, $len);
546         if ($page->getName() != $filename) {
547             $link = LinkURL($link_prefix.$filename, $filename);
548             $msg->pushContent(HTML::small(_("saved as "), $link, " ... "));
549         }
550         $msg->pushContent(HTML::small(fmt("%s bytes written", $num), "\n"));
551         if (!isa($request, 'MockRequest')) {
552             PrintXML($msg);
553         }
554         flush();
555         $request->chunkOutput();
556
557         assert($num == $len);
558         fclose($fd);
559
560         if (USECACHE) {
561             $request->_dbi->_cache->invalidate_cache($pagename);
562             unset ($request->_dbi->_cache->_pagedata_cache);
563             unset ($request->_dbi->_cache->_versiondata_cache);
564             unset ($request->_dbi->_cache->_glv_cache);
565         }
566         unset ($request->_dbi->_cache->_backend->_page_data);
567
568         unset($msg);
569         unset($revision->_transformedContent);
570         unset($revision);
571         unset($template->_request);
572         unset($template);
573         unset($data);
574         if (DEBUG)
575             $GLOBALS['RCS_IDS'] = $SAVE_RCS_IDS;
576     }
577     $page_iter->free();
578
579     if (!empty($WikiTheme->dumped_images) and is_array($WikiTheme->dumped_images)) {
580         // @mkdir("$directory/images");
581         foreach ($WikiTheme->dumped_images as $img_file) {
582             if ($img_file 
583                 and ($from = $WikiTheme->_findFile($img_file, true)) 
584                 and basename($from)) 
585             {
586                 $target = "$directory/images/".basename($img_file);
587                 if (copy($WikiTheme->_path . $from, $target)) {
588                     _copyMsg($from, fmt("... copied to %s", $target));
589                 } else {
590                     _copyMsg($from, fmt("... not copied to %s", $target));
591                 }
592             } else {
593                 _copyMsg($from, _("... not found"));
594             }
595         }
596     }
597
598     if (!empty($WikiTheme->dumped_buttons) and is_array($WikiTheme->dumped_buttons)) {
599         // Buttons also
600         @mkdir("$directory/images/buttons");
601         foreach ($WikiTheme->dumped_buttons as $text => $img_file) {
602             if ($img_file 
603                 and ($from = $WikiTheme->_findFile($img_file, true)) 
604                 and basename($from)) 
605             {
606                 $target = "$directory/images/buttons/".basename($img_file);
607                 if (copy($WikiTheme->_path . $from, $target)) {
608                     _copyMsg($from, fmt("... copied to %s", $target));
609                 } else {
610                     _copyMsg($from, fmt("... not copied to %s", $target));
611                 }
612             } else {
613                 _copyMsg($from, _("... not found"));
614             }
615         }
616     }
617     if (!empty($WikiTheme->dumped_css) and is_array($WikiTheme->dumped_css)) {
618         foreach ($WikiTheme->dumped_css as $css_file) {
619             if ($css_file 
620                 and ($from = $WikiTheme->_findFile(basename($css_file), true)) 
621                 and basename($from)) 
622             {
623                 $target = "$directory/" . basename($css_file);
624                 if (copy($WikiTheme->_path . $from, $target)) {
625                     _copyMsg($from, fmt("... copied to %s", $target));
626                 } else {
627                     _copyMsg($from, fmt("... not copied to %s", $target));
628                 }
629                 // TODO: fix @import url(main.css);
630             } else {
631                 _copyMsg($from, _("... not found"));
632             }
633         }
634     }
635     $WikiTheme->HTML_DUMP_SUFFIX = '';
636     $WikiTheme->DUMP_MODE = false;
637     $WikiTheme->_MoreAttr['body'] = $_bodyAttr;
638
639     $request->setArg('pagename',$thispage); // Template::_basepage fix
640     EndLoadDump($request);
641 }
642
643 /* Known problem: any plugins or other code which echo()s text will
644  * lead to a corrupted html zip file which may produce the following
645  * errors upon unzipping:
646  *
647  * warning [wikihtml.zip]:  2401 extra bytes at beginning or within zipfile
648  * file #58:  bad zipfile offset (local header sig):  177561
649  *  (attempting to re-compensate)
650  *
651  * However, the actual wiki page data should be unaffected.
652  */
653 function MakeWikiZipHtml (&$request)
654 {
655     $request->_TemplatesProcessed = array();
656     if ($request->getArg('zipname')) {
657         $zipname = basename($request->getArg('zipname'));
658         if (!preg_match("/\.zip$/i", $zipname))
659             $zipname .= ".zip";
660         $request->setArg('zipname', false);
661     } else {
662         $zipname = "wikihtml.zip";
663     }
664     $zip = new ZipWriter("Created by PhpWiki " . PHPWIKI_VERSION, $zipname);
665     $dbi =& $request->_dbi;
666     $thispage = $request->getArg('pagename'); // for "Return to ..."
667     if ($exclude = $request->getArg('exclude')) {   // exclude which pagenames
668         $excludeList = explodePageList($exclude); 
669     } else {
670         $excludeList = array();
671     }
672     if ($pages = $request->getArg('pages')) {  // which pagenames
673         if ($pages == '[]') // current page
674             $pages = $thispage;
675         $page_iter = new WikiDB_Array_PageIterator(explodePageList($pages));
676     } else {
677         $page_iter = $dbi->getAllPages(false,false,false,$excludeList);
678     }
679
680     global $WikiTheme;
681     if (defined('HTML_DUMP_SUFFIX'))
682         $WikiTheme->HTML_DUMP_SUFFIX = HTML_DUMP_SUFFIX;
683     $WikiTheme->DUMP_MODE = 'ZIPHTML';
684     $WikiTheme->other_images = array();
685     $_bodyAttr = @$WikiTheme->_MoreAttr['body'];
686     unset($WikiTheme->_MoreAttr['body']);
687
688     /* ignore fatals in plugins */
689     if (check_php_version(4,1)) {
690         global $ErrorManager;
691         $ErrorManager->pushErrorHandler(new WikiFunctionCb('_dump_error_handler'));
692     }
693
694     $request_args = $request->args;
695     $timeout = (! $request->getArg('start_debug')) ? 20 : 240;
696     
697     while ($page = $page_iter->next()) {
698         $request->args = $request_args; // some plugins might change them (esp. on POST)
699         longer_timeout($timeout);       // Reset watchdog
700
701         $current = $page->getCurrentRevision();
702         if ($current->getVersion() == 0)
703             continue;
704         $pagename = $page->getName();
705         if (in_array($pagename, $excludeList)) {
706             continue;
707         }
708
709         $attrib = array('mtime'    => $current->get('mtime'),
710                         'is_ascii' => 1);
711         if ($page->get('locked'))
712             $attrib['write_protected'] = 1;
713
714         $request->setArg('pagename', $pagename); // Template::_basepage fix
715         $filename = FilenameForPage($pagename) . $WikiTheme->HTML_DUMP_SUFFIX;
716         $revision = $page->getCurrentRevision();
717
718         $transformedContent = $revision->getTransformedContent();
719
720         $template = new Template('browse', $request,
721                                  array('revision' => $revision,
722                                        'CONTENT' => $transformedContent));
723
724         $data = GeneratePageAsXML($template, $pagename);
725
726         // Detect local images. LinkImage et al...
727         // TODO: defer via Cached_InlinedImage
728         if (preg_match_all("/<img .*?src=\"(\/.+?)\"/", $data, $m)) {
729             foreach ($m[1] as $match) {
730                 $WikiTheme->other_images[] = $match; //copy to images. no collisions yet.
731                 $base = basename($match);
732                 $data = str_replace('src="'.$match.'"','src="images/'.$base.'"', $data);
733             }
734         }
735
736         $zip->addRegularFile( $filename, $data, $attrib );
737         
738         if (USECACHE) {
739             $request->_dbi->_cache->invalidate_cache($pagename);
740             unset ($request->_dbi->_cache->_pagedata_cache);
741             unset ($request->_dbi->_cache->_versiondata_cache);
742             unset ($request->_dbi->_cache->_glv_cache);
743         }
744         unset ($request->_dbi->_cache->_backend->_page_data);
745
746         unset($revision->_transformedContent);
747         unset($revision);
748         unset($template->_request);
749         unset($template);
750         unset($data);
751     }
752     $page_iter->free();
753
754     $attrib = false;
755     // Deal with theme css and theme images here.
756     if (!empty($WikiTheme->dumped_images) and is_array($WikiTheme->dumped_images)) {
757         // dirs are created automatically
758         //if ($WikiTheme->dumped_images) $zip->addRegularFile("images", "", $attrib);
759         foreach ($WikiTheme->dumped_images as $img_file) {
760             if (($from = $WikiTheme->_findFile($img_file, true)) and basename($from)) {
761                 $target = "images/".basename($img_file);
762                 if (check_php_version(4,3))
763                     $zip->addRegularFile($target, file_get_contents($WikiTheme->_path . $from), $attrib);
764                 else
765                     $zip->addRegularFile($target, join('', file($WikiTheme->_path . $from)), $attrib);
766             }
767         }
768     }
769     if (!empty($WikiTheme->other_images) and is_array($WikiTheme->other_images)) {
770         $doc_root = $request->get("DOCUMENT_ROOT");
771         foreach ($WikiTheme->other_images as $img_file) {
772             // resolve src from webdata to file
773             $src = $doc_root . $img_file;
774             if (file_exists($src) and basename($src)) {
775                 $target = "images/".basename($src);
776                 if (check_php_version(4,3))
777                     $zip->addRegularFile($target, file_get_contents($src), $attrib);
778                 else
779                     $zip->addRegularFile($target, join('', file($src)), $attrib);
780             }
781         }
782     }
783     if (!empty($WikiTheme->dumped_buttons) and is_array($WikiTheme->dumped_buttons)) {
784         //if ($WikiTheme->dumped_buttons) $zip->addRegularFile("images/buttons", "", $attrib);
785         foreach ($WikiTheme->dumped_buttons as $text => $img_file) {
786             if (($from = $WikiTheme->_findFile($img_file, true)) and basename($from)) {
787                 $target = "images/buttons/".basename($img_file);
788                 if (check_php_version(4,3))
789                     $zip->addRegularFile($target, file_get_contents($WikiTheme->_path . $from), $attrib);
790                 else
791                     $zip->addRegularFile($target, join('', file($WikiTheme->_path . $from)), $attrib);
792             }
793         }
794     }
795     if (!empty($WikiTheme->dumped_css) and is_array($WikiTheme->dumped_css)) {
796         foreach ($WikiTheme->dumped_css as $css_file) {
797             if (($from = $WikiTheme->_findFile(basename($css_file), true)) and basename($from)) {
798                 $target = basename($css_file);
799                 if (check_php_version(4,3))
800                     $zip->addRegularFile($target, file_get_contents($WikiTheme->_path . $from), $attrib);
801                 else
802                     $zip->addRegularFile($target, join('', file($WikiTheme->_path . $from)), $attrib);
803             }
804         }
805     }
806
807     $zip->finish();
808     if (check_php_version(4,1)) {
809         global $ErrorManager;
810         $ErrorManager->popErrorHandler();
811     }
812     $WikiTheme->HTML_DUMP_SUFFIX = '';
813     $WikiTheme->DUMP_MODE = false;
814     $WikiTheme->_MoreAttr['body'] = $_bodyAttr;
815 }
816
817
818 ////////////////////////////////////////////////////////////////
819 //
820 //  Functions for restoring.
821 //
822 ////////////////////////////////////////////////////////////////
823
824 function SavePage (&$request, &$pageinfo, $source, $filename)
825 {
826     static $overwite_all = false;
827     $pagedata    = $pageinfo['pagedata'];    // Page level meta-data.
828     $versiondata = $pageinfo['versiondata']; // Revision level meta-data.
829
830     if (empty($pageinfo['pagename'])) {
831         PrintXML(HTML::dt(HTML::strong(_("Empty pagename!"))));
832         return;
833     }
834
835     if (empty($versiondata['author_id']))
836         $versiondata['author_id'] = $versiondata['author'];
837
838     // remove invalid backend specific chars. utf8 issues mostly
839     $pagename_check = new WikiPagename($pageinfo['pagename']);
840     if (!$pagename_check->isValid()) {
841         PrintXML(HTML::dt(HTML::strong(_("Invalid pagename!")." ".$pageinfo['pagename'])));
842         return;
843     }
844     $pagename = $pagename_check->getName();
845     $content  = $pageinfo['content'];
846
847     if ($pagename == _("InterWikiMap"))
848         $content = _tryinsertInterWikiMap($content);
849
850     $dbi =& $request->_dbi;
851     $page = $dbi->getPage($pagename);
852
853     // Try to merge if updated pgsrc contents are different. This
854     // whole thing is hackish
855     //
856     // TODO: try merge unless:
857     // if (current contents = default contents && pgsrc_version >=
858     // pgsrc_version) then just upgrade this pgsrc
859     $needs_merge = false;
860     $merging = false;
861     $overwrite = false;
862
863     if ($request->getArg('merge')) {
864         $merging = true;
865     }
866     else if ($request->getArg('overwrite')) {
867         $overwrite = true;
868     }
869
870     $current = $page->getCurrentRevision();
871     $skip = false;
872     $edit = $request->getArg('edit');
873     if ($merging) { 
874         if (isset($edit['keep_old'])) {
875             $merging = false;
876             $skip = true;
877         }
878         elseif (isset($edit['overwrite'])) {
879             $merging = false;
880             $overwrite = true;
881         }
882         elseif ( $current and (! $current->hasDefaultContents())
883          && ($current->getPackedContent() != $content) ) 
884         {
885             include_once('lib/editpage.php');
886             $request->setArg('pagename', $pagename);
887             $v = $current->getVersion();
888             $request->setArg('revision', $current->getVersion());
889             $p = new LoadFileConflictPageEditor($request);
890             $p->_content = $content;
891             $p->_currentVersion = $v - 1;
892             $p->editPage($saveFailed = true);
893             return; //early return
894        }
895     }
896     if (!$skip)
897       foreach ($pagedata as $key => $value) {
898         if (!empty($value))
899             $page->set($key, $value);
900       }
901
902     $mesg = HTML::dd();
903     if ($source)
904         $mesg->pushContent(' ', fmt("from %s", $source));
905
906
907     if (!$current) {
908         //FIXME: This should not happen! (empty vdata, corrupt cache or db)
909         $current = $page->getCurrentRevision();
910     }
911     if ($current->getVersion() == 0) {
912         $mesg->pushContent(' - ', _("New page"));
913         $isnew = true;
914     }
915     else {
916         if ( (! $current->hasDefaultContents())
917              && ($current->getPackedContent() != $content) ) {
918             if ($overwrite) {
919                 $mesg->pushContent(' ',
920                                    fmt("has edit conflicts - overwriting anyway"));
921                 $skip = false;
922                 if (substr_count($source, 'pgsrc')) {
923                     $versiondata['author'] = _("The PhpWiki programming team");
924                     // but leave authorid as userid who loaded the file
925                 }
926             }
927             else {
928                 if (isset($edit['keep_old'])) {
929                     $mesg->pushContent(' ', fmt("keep old"));
930                 } else {
931                     $mesg->pushContent(' ', fmt("has edit conflicts - skipped"));
932                     $needs_merge = true; // hackish, to display the buttons
933                 }
934                 $skip = true;
935             }
936         }
937         else if ($current->getPackedContent() == $content
938                  && $current->get('author') == $versiondata['author']) {
939             // The page metadata is already changed, we don't need a new revision.
940             // This was called previously "is identical to current version %d - skipped"
941             // which is wrong, since the pagedata was stored, not skipped.
942             $mesg->pushContent(' ',
943                                fmt("content is identical to current version %d - no new revision created",
944                                    $current->getVersion()));
945             $skip = true;
946         }
947         $isnew = false;
948     }
949
950     if (! $skip ) {
951         // in case of failures print the culprit:
952         if (!isa($request,'MockRequest')) {
953             PrintXML(HTML::dt(WikiLink($pagename))); flush();
954         }
955         $new = $page->save($content, WIKIDB_FORCE_CREATE, $versiondata);
956         $dbi->touch();
957         $mesg->pushContent(' ', fmt("- saved to database as version %d",
958                                     $new->getVersion()));
959     }
960     if ($needs_merge) {
961         $f = $source;
962         // hackish, $source contains needed path+filename
963         $f = str_replace(sprintf(_("MIME file %s"), ''), '', $f);
964         $f = str_replace(sprintf(_("Serialized file %s"), ''), '', $f);
965         $f = str_replace(sprintf(_("plain file %s"), ''), '', $f);
966         //check if uploaded file? they pass just the content, but the file is gone
967         if (@stat($f)) {
968             global $WikiTheme;
969             $meb = Button(array('action' => 'loadfile',
970                                 'merge'=> true,
971                                 'source'=> $f),
972                           _("Merge Edit"),
973                           _("PhpWikiAdministration"),
974                           'wikiadmin');
975             $owb = Button(array('action' => 'loadfile',
976                                 'overwrite'=> true,
977                                 'source'=> $f),
978                           _("Restore Anyway"),
979                           _("PhpWikiAdministration"),
980                           'wikiunsafe');
981             $mesg->pushContent(' ', $meb, " ", $owb);
982             if (!$overwite_all) {
983                 $args = $request->getArgs();
984                 $args['overwrite'] = 1;
985                 $owb = Button($args,
986                               _("Overwrite All"),
987                               _("PhpWikiAdministration"),
988                               'wikiunsafe');
989                 $mesg->pushContent(HTML::div(array('class' => 'hint'), $owb));
990                 $overwite_all = true;
991             }
992         } else {
993             $mesg->pushContent(HTML::em(_(" Sorry, cannot merge.")));
994         }
995     }
996
997     if (!isa($request,'MockRequest')) {
998       if ($skip)
999         PrintXML(HTML::dt(HTML::em(WikiLink($pagename))), $mesg);
1000       else
1001         PrintXML($mesg);
1002       flush();
1003     }
1004 }
1005
1006 // action=revert (by diff)
1007 function RevertPage (&$request)
1008 {
1009     $mesg = HTML::dd();
1010     $pagename = $request->getArg('pagename');
1011     $version = $request->getArg('version');
1012     if (!$version) {
1013         PrintXML(HTML::dt(fmt("Revert")," ",WikiLink($pagename)),
1014                  HTML::dd(_("missing required version argument")));
1015         return;
1016     }
1017     $dbi =& $request->_dbi;
1018     $page = $dbi->getPage($pagename);
1019     $current = $page->getCurrentRevision();
1020     $currversion = $current->getVersion();
1021     if ($currversion == 0) {
1022         $mesg->pushContent(' ', _("no page content"));
1023         PrintXML(HTML::dt(fmt("Revert")," ",WikiLink($pagename)),
1024                  $mesg);
1025         flush();
1026         return;
1027     }
1028     if ($currversion == $version) {
1029         $mesg->pushContent(' ', _("same version page"));
1030         PrintXML(HTML::dt(fmt("Revert")," ",WikiLink($pagename)),
1031                  $mesg);
1032         flush();
1033         return;
1034     }
1035     if ($request->getArg('cancel')) {
1036         $mesg->pushContent(' ', _("Cancelled"));
1037         PrintXML(HTML::dt(fmt("Revert")," ",WikiLink($pagename)),
1038                  $mesg);
1039         flush();
1040         return;
1041     }
1042     if (!$request->getArg('verify')) {
1043         $mesg->pushContent(HTML::br(),
1044                            _("Are you sure?"),
1045                            HTML::br(),
1046                            HTML::form(array('action' => $request->getPostURL(),
1047                                             'method' => 'post'),
1048                                       HiddenInputs($request->getArgs(), false, array('verify')),
1049                                       HiddenInputs(array('verify' => 1)),
1050                                       Button('submit:verify', _("Yes"), 'button'),
1051                                       HTML::Raw('&nbsp;'),
1052                                       Button('submit:cancel', _("Cancel"), 'button')),
1053                            HTML::hr());
1054         $rev = $page->getRevision($version);
1055         $html = HTML(HTML::dt(fmt("Revert %s to version $version", WikiLink($pagename))), 
1056                      $mesg,
1057                      $rev->getTransformedContent()); 
1058         $template = Template('browse', 
1059                              array('CONTENT' => $html));
1060         GeneratePage($template, $pagename, $rev);
1061         $request->checkValidators();
1062         flush();
1063         return;
1064     }
1065     $rev = $page->getRevision($version);
1066     $content = $rev->getPackedContent();
1067     $versiondata = $rev->_data;
1068     $versiondata['summary'] = sprintf(_("revert to version %d"), $version);
1069     $new = $page->save($content, $currversion + 1, $versiondata);
1070     $dbi->touch();
1071     
1072     $pagelink = WikiLink($pagename);
1073     $mesg->pushContent(fmt("Revert: %s", $pagelink), 
1074                        fmt("- version %d saved to database as version %d",
1075                            $version, $new->getVersion()));
1076     // Force browse of current page version.
1077     $request->setArg('version', false);
1078     $template = Template('savepage', array());
1079     $template->replace('CONTENT', $new->getTransformedContent());
1080     
1081     GeneratePage($template, $mesg, $new);
1082     flush();
1083 }
1084
1085 function _tryinsertInterWikiMap($content) {
1086     $goback = false;
1087     if (strpos($content, "<verbatim>")) {
1088         //$error_html = " The newly loaded pgsrc already contains a verbatim block.";
1089         $goback = true;
1090     }
1091     if (!$goback && !defined('INTERWIKI_MAP_FILE')) {
1092         $error_html = sprintf(" "._("%s: not defined"), "INTERWIKI_MAP_FILE");
1093         $goback = true;
1094     }
1095     $mapfile = FindFile(INTERWIKI_MAP_FILE,1);
1096     if (!$goback && !file_exists($mapfile)) {
1097         $error_html = sprintf(" "._("%s: file not found"), INTERWIKI_MAP_FILE);
1098         $goback = true;
1099     }
1100
1101     if (!empty($error_html))
1102         trigger_error(_("Default InterWiki map file not loaded.")
1103                       . $error_html, E_USER_NOTICE);
1104     if ($goback)
1105         return $content;
1106
1107     // if loading from virgin setup do echo, otherwise trigger_error E_USER_NOTICE
1108     if (!isa($GLOBALS['request'], 'MockRequest'))
1109         echo sprintf(_("Loading InterWikiMap from external file %s."), $mapfile),"<br />";
1110
1111     $fd = fopen ($mapfile, "rb");
1112     $data = fread ($fd, filesize($mapfile));
1113     fclose ($fd);
1114     $content = $content . "\n<verbatim>\n$data</verbatim>\n";
1115     return $content;
1116 }
1117
1118 function ParseSerializedPage($text, $default_pagename, $user)
1119 {
1120     if (!preg_match('/^a:\d+:{[si]:\d+/', $text))
1121         return false;
1122
1123     $pagehash = unserialize($text);
1124
1125     // Split up pagehash into four parts:
1126     //   pagename
1127     //   content
1128     //   page-level meta-data
1129     //   revision-level meta-data
1130
1131     if (!defined('FLAG_PAGE_LOCKED'))
1132         define('FLAG_PAGE_LOCKED', 1);
1133     $pageinfo = array('pagedata'    => array(),
1134                       'versiondata' => array());
1135
1136     $pagedata = &$pageinfo['pagedata'];
1137     $versiondata = &$pageinfo['versiondata'];
1138
1139     // Fill in defaults.
1140     if (empty($pagehash['pagename']))
1141         $pagehash['pagename'] = $default_pagename;
1142     if (empty($pagehash['author'])) {
1143         $pagehash['author'] = $user->getId();
1144     }
1145
1146     foreach ($pagehash as $key => $value) {
1147         switch($key) {
1148             case 'pagename':
1149             case 'version':
1150             case 'hits':
1151                 $pageinfo[$key] = $value;
1152                 break;
1153             case 'content':
1154                 $pageinfo[$key] = join("\n", $value);
1155                 break;
1156             case 'flags':
1157                 if (($value & FLAG_PAGE_LOCKED) != 0)
1158                     $pagedata['locked'] = 'yes';
1159                 break;
1160             case 'owner':
1161             case 'created':
1162                 $pagedata[$key] = $value;
1163                 break;
1164             case 'acl':
1165             case 'perm':
1166                 $pagedata['perm'] = ParseMimeifiedPerm($value);
1167                 break;
1168             case 'lastmodified':
1169                 $versiondata['mtime'] = $value;
1170                 break;
1171             case 'author':
1172             case 'author_id':
1173             case 'summary':
1174                 $versiondata[$key] = $value;
1175                 break;
1176         }
1177     }
1178     if (empty($pagehash['charset']))
1179         $pagehash['charset'] = 'iso-8859-1';
1180     // compare to target charset
1181     if (strtolower($pagehash['charset']) != strtolower($GLOBALS['charset'])) {
1182         $pageinfo['content'] = charset_convert($params['charset'], $GLOBALS['charset'], $pageinfo['content']);
1183         $pageinfo['pagename'] = charset_convert($params['charset'], $GLOBALS['charset'], $pageinfo['pagename']);
1184     }
1185     return $pageinfo;
1186 }
1187
1188 function SortByPageVersion ($a, $b) {
1189     return $a['version'] - $b['version'];
1190 }
1191
1192 /**
1193  * Security alert! We should not allow to import config.ini into our wiki (or from a sister wiki?)
1194  * because the sql passwords are in plaintext there. And the webserver must be able to read it.
1195  * Detected by Santtu Jarvi.
1196  */
1197 function LoadFile (&$request, $filename, $text = false, $mtime = false)
1198 {
1199     if (preg_match("/config$/", dirname($filename))             // our or other config
1200         and preg_match("/config.*\.ini/", basename($filename))) // backups and other versions also
1201     {
1202         trigger_error(sprintf("Refused to load %s", $filename), E_USER_WARNING);
1203         return;
1204     }
1205     if (!is_string($text)) {
1206         // Read the file.
1207         $stat  = stat($filename);
1208         $mtime = $stat[9];
1209         $text  = implode("", file($filename));
1210     }
1211
1212     if (! $request->getArg('start_debug')) @set_time_limit(30); // Reset watchdog
1213     else @set_time_limit(240);
1214
1215     // FIXME: basename("filewithnoslashes") seems to return garbage sometimes.
1216     $basename = basename("/dummy/" . $filename);
1217
1218     if (!$mtime)
1219         $mtime = time();    // Last resort.
1220
1221     // DONE: check source - target charset for content and pagename
1222     // but only for pgsrc'ed content, not from the browser.
1223
1224     $default_pagename = rawurldecode($basename);
1225     if ( ($parts = ParseMimeifiedPages($text)) ) {
1226         if (count($parts) > 1)
1227             $overwrite = $request->getArg('overwrite');
1228         usort($parts, 'SortByPageVersion');
1229         foreach ($parts as $pageinfo) {
1230             // force overwrite
1231             if (count($parts) > 1)
1232                 $request->setArg('overwrite', 1);
1233             SavePage($request, $pageinfo, sprintf(_("MIME file %s"),
1234                                                   $filename), $basename);
1235     }
1236         if (count($parts) > 1)
1237             if ($overwrite) 
1238                 $request->setArg('overwrite', $overwrite);
1239             else     
1240                 unset($request->_args['overwrite']);
1241     }
1242     else if ( ($pageinfo = ParseSerializedPage($text, $default_pagename,
1243                                                $request->getUser())) ) {
1244         SavePage($request, $pageinfo, sprintf(_("Serialized file %s"),
1245                                               $filename), $basename);
1246     }
1247     else {
1248         // plain old file
1249         $user = $request->getUser();
1250
1251         $file_charset = 'iso-8859-1';
1252         // compare to target charset
1253         if ($file_charset != strtolower($GLOBALS['charset'])) {
1254             $text = charset_convert($file_charset, $GLOBALS['charset'], $text);
1255             $default_pagename = charset_convert($file_charset, $GLOBALS['charset'], $default_pagename);
1256         }
1257
1258         // Assume plain text file.
1259         $pageinfo = array('pagename' => $default_pagename,
1260                           'pagedata' => array(),
1261                           'versiondata'
1262                           => array('author' => $user->getId()),
1263                           'content'  => preg_replace('/[ \t\r]*\n/', "\n",
1264                                                      chop($text))
1265                           );
1266         SavePage($request, $pageinfo, sprintf(_("plain file %s"), $filename),
1267                  $basename);
1268     }
1269 }
1270
1271 function LoadZip (&$request, $zipfile, $files = false, $exclude = false) {
1272     $zip = new ZipReader($zipfile);
1273     $timeout = (! $request->getArg('start_debug')) ? 20 : 120;
1274     while (list ($fn, $data, $attrib) = $zip->readFile()) {
1275         // FIXME: basename("filewithnoslashes") seems to return
1276         // garbage sometimes.
1277         $fn = basename("/dummy/" . $fn);
1278         if ( ($files && !in_array($fn, $files))
1279              || ($exclude && in_array($fn, $exclude)) ) {
1280             PrintXML(HTML::dt(WikiLink($fn)),
1281                      HTML::dd(_("Skipping")));
1282             flush();
1283             continue;
1284         }
1285         longer_timeout($timeout);       // longer timeout per page
1286         LoadFile($request, $fn, $data, $attrib['mtime']);
1287     }
1288 }
1289
1290 function LoadDir (&$request, $dirname, $files = false, $exclude = false) {
1291     $fileset = new LimitedFileSet($dirname, $files, $exclude);
1292
1293     if (!$files and ($skiplist = $fileset->getSkippedFiles())) {
1294         PrintXML(HTML::dt(HTML::strong(_("Skipping"))));
1295         $list = HTML::ul();
1296         foreach ($skiplist as $file)
1297             $list->pushContent(HTML::li(WikiLink($file)));
1298         PrintXML(HTML::dd($list));
1299     }
1300
1301     // Defer HomePage loading until the end. If anything goes wrong
1302     // the pages can still be loaded again.
1303     $files = $fileset->getFiles();
1304     if (in_array(HOME_PAGE, $files)) {
1305         $files = array_diff($files, array(HOME_PAGE));
1306         $files[] = HOME_PAGE;
1307     }
1308     $timeout = (! $request->getArg('start_debug')) ? 20 : 120;
1309     foreach ($files as $file) {
1310         longer_timeout($timeout);       // longer timeout per page
1311         if (substr($file,-1,1) != '~')  // refuse to load backup files
1312             LoadFile($request, "$dirname/$file");
1313     }
1314 }
1315
1316 class LimitedFileSet extends FileSet {
1317     function LimitedFileSet($dirname, $_include, $exclude) {
1318         $this->_includefiles = $_include;
1319         $this->_exclude = $exclude;
1320         $this->_skiplist = array();
1321         parent::FileSet($dirname);
1322     }
1323
1324     function _filenameSelector($fn) {
1325         $incl = &$this->_includefiles;
1326         $excl = &$this->_exclude;
1327
1328         if ( ($incl && !in_array($fn, $incl))
1329              || ($excl && in_array($fn, $excl)) ) {
1330             $this->_skiplist[] = $fn;
1331             return false;
1332         } else {
1333             return true;
1334         }
1335     }
1336
1337     function getSkippedFiles () {
1338         return $this->_skiplist;
1339     }
1340 }
1341
1342
1343 function IsZipFile ($filename_or_fd)
1344 {
1345     // See if it looks like zip file
1346     if (is_string($filename_or_fd))
1347     {
1348         $fd    = fopen($filename_or_fd, "rb");
1349         $magic = fread($fd, 4);
1350         fclose($fd);
1351     }
1352     else
1353     {
1354         $fpos  = ftell($filename_or_fd);
1355         $magic = fread($filename_or_fd, 4);
1356         fseek($filename_or_fd, $fpos);
1357     }
1358
1359     return $magic == ZIP_LOCHEAD_MAGIC || $magic == ZIP_CENTHEAD_MAGIC;
1360 }
1361
1362
1363 function LoadAny (&$request, $file_or_dir, $files = false, $exclude = false)
1364 {
1365     // Try urlencoded filename for accented characters.
1366     if (!file_exists($file_or_dir)) {
1367         // Make sure there are slashes first to avoid confusing phps
1368         // with broken dirname or basename functions.
1369         // FIXME: windows uses \ and :
1370         if (is_integer(strpos($file_or_dir, "/"))) {
1371             $newfile = FindFile($file_or_dir, true);
1372             // Panic. urlencoded by the browser (e.g. San%20Diego => San Diego)
1373             if (!$newfile)
1374                 $file_or_dir = dirname($file_or_dir) . "/"
1375                     . rawurlencode(basename($file_or_dir));
1376         } else {
1377             // This is probably just a file.
1378             $file_or_dir = rawurlencode($file_or_dir);
1379         }
1380     }
1381
1382     $type = filetype($file_or_dir);
1383     if ($type == 'link') {
1384         // For symbolic links, use stat() to determine
1385         // the type of the underlying file.
1386         list(,,$mode) = stat($file_or_dir);
1387         $type = ($mode >> 12) & 017;
1388         if ($type == 010)
1389             $type = 'file';
1390         elseif ($type == 004)
1391             $type = 'dir';
1392     }
1393
1394     if (! $type) {
1395         $request->finish(fmt("Empty or not existing source. Unable to load: %s", $file_or_dir));
1396     }
1397     else if ($type == 'dir') {
1398         LoadDir($request, $file_or_dir, $files, $exclude);
1399     }
1400     else if ($type != 'file' && !preg_match('/^(http|ftp):/', $file_or_dir))
1401     {
1402         $request->finish(fmt("Bad file type: %s", $type));
1403     }
1404     else if (IsZipFile($file_or_dir)) {
1405         LoadZip($request, $file_or_dir, $files, $exclude);
1406     }
1407     else /* if (!$files || in_array(basename($file_or_dir), $files)) */
1408     {
1409         LoadFile($request, $file_or_dir);
1410     }
1411 }
1412
1413 function LoadFileOrDir (&$request)
1414 {
1415     $source = $request->getArg('source');
1416     $finder = new FileFinder;
1417     $source = $finder->slashifyPath($source);
1418     $page = rawurldecode(basename($source));
1419     StartLoadDump($request, fmt("Loading '%s'", 
1420         HTML(dirname($source),
1421              dirname($source) ? "/" : "",
1422              WikiLink($page,'auto'))));
1423     echo "<dl>\n";
1424     LoadAny($request, $source);
1425     echo "</dl>\n";
1426     EndLoadDump($request);
1427 }
1428
1429 /**
1430  * HomePage was not found so first-time install is supposed to run.
1431  * - import all pgsrc pages.
1432  * - Todo: installer interface to edit config/config.ini settings
1433  * - Todo: ask for existing old index.php to convert to config/config.ini
1434  * - Todo: theme-specific pages: 
1435  *   blog - HomePage, ADMIN_USER/Blogs
1436  */
1437 function SetupWiki (&$request)
1438 {
1439     global $GenericPages, $LANG;
1440
1441     //FIXME: This is a hack (err, "interim solution")
1442     // This is a bogo-bogo-login:  Login without
1443     // saving login information in session state.
1444     // This avoids logging in the unsuspecting
1445     // visitor as "The PhpWiki programming team".
1446     //
1447     // This really needs to be cleaned up...
1448     // (I'm working on it.)
1449     $real_user = $request->_user;
1450     if (ENABLE_USER_NEW)
1451         $request->_user = new _BogoUser(_("The PhpWiki programming team"));
1452
1453     else
1454         $request->_user = new WikiUser($request, _("The PhpWiki programming team"),
1455                                        WIKIAUTH_BOGO);
1456
1457     StartLoadDump($request, _("Loading up virgin wiki"));
1458     echo "<dl>\n";
1459
1460     $pgsrc = FindLocalizedFile(WIKI_PGSRC);
1461     $default_pgsrc = FindFile(DEFAULT_WIKI_PGSRC);
1462
1463     $request->setArg('overwrite', true);
1464     if ($default_pgsrc != $pgsrc) {
1465         LoadAny($request, $default_pgsrc, $GenericPages);
1466     }
1467     $request->setArg('overwrite', false);
1468     LoadAny($request, $pgsrc);
1469     $dbi =& $request->_dbi;
1470
1471     // Ensure that all mandatory pages are loaded
1472     $finder = new FileFinder;
1473     foreach (array_merge(explode(':','Help/OldTextFormattingRules:Help/TextFormattingRules:PhpWikiAdministration'),
1474                          $GLOBALS['AllActionPages'],
1475                          array(constant('HOME_PAGE'))) as $f) 
1476     {
1477         $page = gettext($f);
1478         $epage = urlencode($page);
1479         if (! $dbi->isWikiPage($page) ) {
1480             // translated version provided?
1481             if ($lf = FindLocalizedFile($pgsrc . $finder->_pathsep . $epage, 1)) {
1482                 LoadAny($request, $lf);
1483             } else { // load english version of required action page
1484                 LoadAny($request, FindFile(DEFAULT_WIKI_PGSRC . $finder->_pathsep . urlencode($f)));
1485                 $page = $f;
1486             }
1487         }
1488         if (! $dbi->isWikiPage($page)) {
1489             trigger_error(sprintf("Mandatory file %s couldn't be loaded!", $page),
1490                           E_USER_WARNING);
1491         }
1492     }
1493     echo "</dl>\n";
1494     
1495     $pagename = _("InterWikiMap");
1496     $map = $dbi->getPage($pagename);
1497     $map->set('locked', true);
1498     PrintXML(HTML::dt(HTML::em(WikiLink($pagename))), HTML::dd("locked"));
1499     EndLoadDump($request);
1500 }
1501
1502 function LoadPostFile (&$request)
1503 {
1504     $upload = $request->getUploadedFile('file');
1505
1506     if (!$upload)
1507         $request->finish(_("No uploaded file to upload?")); // FIXME: more concise message
1508
1509
1510     // Dump http headers.
1511     StartLoadDump($request, sprintf(_("Uploading %s"), $upload->getName()));
1512     echo "<dl>\n";
1513
1514     $fd = $upload->open();
1515     if (IsZipFile($fd))
1516         LoadZip($request, $fd, false, array(_("RecentChanges")));
1517     else
1518         LoadFile($request, $upload->getName(), $upload->getContents());
1519
1520     echo "</dl>\n";
1521     EndLoadDump($request);
1522 }
1523
1524 /**
1525  $Log: not supported by cvs2svn $
1526  Revision 1.154  2007/08/10 22:00:43  rurban
1527  FilenameForPage:
1528  We have to apply a different "/" logic for dumpserial, htmldump and
1529  zipdump. dirs are allowed for zipdump and htmldump, not for dumpserial.
1530
1531  Revision 1.153  2007/05/28 20:54:40  rurban
1532  fix DumpToHtml creating dirs
1533
1534  Revision 1.152  2007/05/01 16:22:41  rurban
1535  lock InterWikiMap on init
1536
1537  Revision 1.151  2007/02/17 14:17:34  rurban
1538  only media=print css for htmldump and pdf
1539
1540  Revision 1.150  2007/01/20 15:53:42  rurban
1541  Use WikiPagename treatment for imported pagenames
1542
1543  Revision 1.149  2007/01/03 21:25:10  rurban
1544  Use convert_charset()
1545
1546  Revision 1.148  2007/01/02 13:21:57  rurban
1547  omit want_content if not necessary. support keep_old and overwrite buttons
1548
1549  Revision 1.147  2006/12/22 17:44:15  rurban
1550  support importing foreign charsets. e.g latin1 => utf8
1551
1552  Revision 1.146  2006/12/17 18:35:23  rurban
1553  Create the right subdirectory name, urlencoded.
1554
1555  Revision 1.145  2006/09/06 06:01:18  rurban
1556  support loadfile multipart archives automatically
1557
1558  Revision 1.144  2006/08/25 22:06:13  rurban
1559  args fix to pass $args to the template
1560
1561  Revision 1.143  2006/08/25 21:48:39  rurban
1562  dumphtml subpages
1563
1564  Revision 1.142  2006/03/19 17:16:32  rurban
1565  remove remaining cruft
1566
1567  Revision 1.141  2006/03/19 17:11:32  rurban
1568  add verify to RevertPage, display reverted page as template
1569
1570  Revision 1.140  2006/03/07 20:45:43  rurban
1571  wikihash for php-5.1
1572
1573  Revision 1.139  2005/08/27 18:02:43  rurban
1574  fix and expand pages
1575
1576  Revision 1.138  2005/08/27 09:39:10  rurban
1577  dumphtml when not at admin page: dump the current or given page
1578
1579  Revision 1.137  2005/01/30 23:14:38  rurban
1580  simplify page names
1581
1582  Revision 1.136  2005/01/25 07:07:24  rurban
1583  remove body tags in html dumps, add css and images to zipdumps, simplify printing
1584
1585  Revision 1.135  2004/12/26 17:17:25  rurban
1586  announce dumps - mult.requests to avoid request::finish, e.g. LinkDatabase, PdfOut, ...
1587
1588  Revision 1.134  2004/12/20 16:05:01  rurban
1589  gettext msg unification
1590
1591  Revision 1.133  2004/12/08 12:57:41  rurban
1592  page-specific timeouts for long multi-page requests
1593
1594  Revision 1.132  2004/12/08 01:18:33  rurban
1595  Disallow loading config*.ini files. Detected by Santtu Jarvi.
1596
1597  Revision 1.131  2004/11/30 17:48:38  rurban
1598  just comments
1599
1600  Revision 1.130  2004/11/25 08:28:12  rurban
1601  dont fatal on missing css or imgfiles and actually print the miss
1602
1603  Revision 1.129  2004/11/25 08:11:40  rurban
1604  pass exclude to the get_all_pages backend
1605
1606  Revision 1.128  2004/11/16 16:16:44  rurban
1607  enable Overwrite All for upgrade
1608
1609  Revision 1.127  2004/11/01 10:43:57  rurban
1610  seperate PassUser methods into seperate dir (memory usage)
1611  fix WikiUser (old) overlarge data session
1612  remove wikidb arg from various page class methods, use global ->_dbi instead
1613  ...
1614
1615  Revision 1.126  2004/10/16 15:13:39  rurban
1616  new [Overwrite All] button
1617
1618  Revision 1.125  2004/10/14 19:19:33  rurban
1619  loadsave: check if the dumped file will be accessible from outside.
1620  and some other minor fixes. (cvsclient native not yet ready)
1621
1622  Revision 1.124  2004/10/04 23:44:28  rurban
1623  for older or CGI phps
1624
1625  Revision 1.123  2004/09/25 16:26:54  rurban
1626  deferr notifies (to be improved)
1627
1628  Revision 1.122  2004/09/17 14:25:45  rurban
1629  update comments
1630
1631  Revision 1.121  2004/09/08 13:38:00  rurban
1632  improve loadfile stability by using markup=2 as default for undefined markup-style.
1633  use more refs for huge objects.
1634  fix debug=static issue in WikiPluginCached
1635
1636  Revision 1.120  2004/07/08 19:04:42  rurban
1637  more unittest fixes (file backend, metadata RatingsDb)
1638
1639  Revision 1.119  2004/07/08 15:23:59  rurban
1640  less verbose for tests
1641
1642  Revision 1.118  2004/07/08 13:50:32  rurban
1643  various unit test fixes: print error backtrace on _DEBUG_TRACE; allusers fix; new PHPWIKI_NOMAIN constant for omitting the mainloop
1644
1645  Revision 1.117  2004/07/02 09:55:58  rurban
1646  more stability fixes: new DISABLE_GETIMAGESIZE if your php crashes when loading LinkIcons: failing getimagesize in old phps; blockparser stabilized
1647
1648  Revision 1.116  2004/07/01 09:05:41  rurban
1649  support pages and exclude arguments for all 4 dump methods
1650
1651  Revision 1.115  2004/07/01 08:51:22  rurban
1652  dumphtml: added exclude, print pagename before processing
1653
1654  Revision 1.114  2004/06/28 12:51:41  rurban
1655  improved dumphtml and virgin setup
1656
1657  Revision 1.113  2004/06/27 10:26:02  rurban
1658  oci8 patch by Philippe Vanhaesendonck + some ADODB notes+fixes
1659
1660  Revision 1.112  2004/06/25 14:29:20  rurban
1661  WikiGroup refactoring:
1662    global group attached to user, code for not_current user.
1663    improved helpers for special groups (avoid double invocations)
1664  new experimental config option ENABLE_XHTML_XML (fails with IE, and document.write())
1665  fixed a XHTML validation error on userprefs.tmpl
1666
1667  Revision 1.111  2004/06/21 16:38:55  rurban
1668  fixed the StartLoadDump html argument hack.
1669
1670  Revision 1.110  2004/06/21 16:22:30  rurban
1671  add DEFAULT_DUMP_DIR and HTML_DUMP_DIR constants, for easier cmdline dumps,
1672  fixed dumping buttons locally (images/buttons/),
1673  support pages arg for dumphtml,
1674  optional directory arg for dumpserial + dumphtml,
1675  fix a AllPages warning,
1676  show dump warnings/errors on DEBUG,
1677  don't warn just ignore on wikilens pagelist columns, if not loaded.
1678  RateIt pagelist column is called "rating", not "ratingwidget" (Dan?)
1679
1680  Revision 1.109  2004/06/17 11:31:05  rurban
1681  jump back to label after dump/upgrade
1682
1683  Revision 1.108  2004/06/16 12:43:01  rurban
1684  4.0.6 cannot use this errorhandler (not found)
1685
1686  Revision 1.107  2004/06/14 11:31:37  rurban
1687  renamed global $Theme to $WikiTheme (gforge nameclash)
1688  inherit PageList default options from PageList
1689    default sortby=pagename
1690  use options in PageList_Selectable (limit, sortby, ...)
1691  added action revert, with button at action=diff
1692  added option regex to WikiAdminSearchReplace
1693
1694  Revision 1.106  2004/06/13 13:54:25  rurban
1695  Catch fatals on the four dump calls (as file and zip, as html and mimified)
1696  FoafViewer: Check against external requirements, instead of fatal.
1697  Change output for xhtmldumps: using file:// urls to the local fs.
1698  Catch SOAP fatal by checking for GOOGLE_LICENSE_KEY
1699  Import GOOGLE_LICENSE_KEY and FORTUNE_DIR from config.ini.
1700
1701  Revision 1.105  2004/06/08 19:48:16  rurban
1702  fixed foreign setup: no ugly skipped msg for the GenericPages, load english actionpages if translated not found
1703
1704  Revision 1.104  2004/06/08 13:51:57  rurban
1705  some comments only
1706
1707  Revision 1.103  2004/06/08 10:54:46  rurban
1708  better acl dump representation, read back acl and owner
1709
1710  Revision 1.102  2004/06/06 16:58:51  rurban
1711  added more required ActionPages for foreign languages
1712  install now english ActionPages if no localized are found. (again)
1713  fixed default anon user level to be 0, instead of -1
1714    (wrong "required administrator to view this page"...)
1715
1716  Revision 1.101  2004/06/04 20:32:53  rurban
1717  Several locale related improvements suggested by Pierrick Meignen
1718  LDAP fix by John Cole
1719  reenable admin check without ENABLE_PAGEPERM in the admin plugins
1720
1721  Revision 1.100  2004/05/02 21:26:38  rurban
1722  limit user session data (HomePageHandle and auth_dbi have to invalidated anyway)
1723    because they will not survive db sessions, if too large.
1724  extended action=upgrade
1725  some WikiTranslation button work
1726  revert WIKIAUTH_UNOBTAINABLE (need it for main.php)
1727  some temp. session debug statements
1728
1729  Revision 1.99  2004/05/02 15:10:07  rurban
1730  new finally reliable way to detect if /index.php is called directly
1731    and if to include lib/main.php
1732  new global AllActionPages
1733  SetupWiki now loads all mandatory pages: HOME_PAGE, action pages, and warns if not.
1734  WikiTranslation what=buttons for Carsten to create the missing MacOSX buttons
1735  PageGroupTestOne => subpages
1736  renamed PhpWikiRss to PhpWikiRecentChanges
1737  more docs, default configs, ...
1738
1739  Revision 1.98  2004/04/29 23:25:12  rurban
1740  re-ordered locale init (as in 1.3.9)
1741  fixed loadfile with subpages, and merge/restore anyway
1742    (sf.net bug #844188)
1743
1744  Revision 1.96  2004/04/19 23:13:03  zorloc
1745  Connect the rest of PhpWiki to the IniConfig system.  Also the keyword regular expression is not a config setting
1746
1747  Revision 1.95  2004/04/18 01:11:52  rurban
1748  more numeric pagename fixes.
1749  fixed action=upload with merge conflict warnings.
1750  charset changed from constant to global (dynamic utf-8 switching)
1751
1752  Revision 1.94  2004/03/14 16:36:37  rurban
1753  dont load backup files
1754
1755  Revision 1.93  2004/02/26 03:22:05  rurban
1756  also copy css and images with XHTML Dump
1757
1758  Revision 1.92  2004/02/26 02:25:54  rurban
1759  fix empty and #-anchored links in XHTML Dumps
1760
1761  Revision 1.91  2004/02/24 17:19:37  rurban
1762  debugging helpers only
1763
1764  Revision 1.90  2004/02/24 17:09:24  rurban
1765  fixed \r\r\n with dumping on windows
1766
1767  Revision 1.88  2004/02/22 23:20:31  rurban
1768  fixed DumpHtmlToDir,
1769  enhanced sortby handling in PageList
1770    new button_heading th style (enabled),
1771  added sortby and limit support to the db backends and plugins
1772    for paging support (<<prev, next>> links on long lists)
1773
1774  Revision 1.87  2004/01/26 09:17:49  rurban
1775  * changed stored pref representation as before.
1776    the array of objects is 1) bigger and 2)
1777    less portable. If we would import packed pref
1778    objects and the object definition was changed, PHP would fail.
1779    This doesn't happen with an simple array of non-default values.
1780  * use $prefs->retrieve and $prefs->store methods, where retrieve
1781    understands the interim format of array of objects also.
1782  * simplified $prefs->get() and fixed $prefs->set()
1783  * added $user->_userid and class '_WikiUser' portability functions
1784  * fixed $user object ->_level upgrading, mostly using sessions.
1785    this fixes yesterdays problems with loosing authorization level.
1786  * fixed WikiUserNew::checkPass to return the _level
1787  * fixed WikiUserNew::isSignedIn
1788  * added explodePageList to class PageList, support sortby arg
1789  * fixed UserPreferences for WikiUserNew
1790  * fixed WikiPlugin for empty defaults array
1791  * UnfoldSubpages: added pagename arg, renamed pages arg,
1792    removed sort arg, support sortby arg
1793
1794  Revision 1.86  2003/12/02 16:18:26  carstenklapp
1795  Minor enhancement: Provide more meaningful filenames for WikiDB zip
1796  dumps & snapshots.
1797
1798  Revision 1.85  2003/11/30 18:18:13  carstenklapp
1799  Minor code optimization: use include_once instead of require_once
1800  inside functions that might not always called.
1801
1802  Revision 1.84  2003/11/26 20:47:47  carstenklapp
1803  Redo bugfix: My last refactoring broke merge-edit & overwrite
1804  functionality again, should be fixed now. Sorry.
1805
1806  Revision 1.83  2003/11/20 22:18:54  carstenklapp
1807  New feature: h1 during merge-edit displays WikiLink to original page.
1808  Internal changes: Replaced some hackish url-generation code in
1809  function SavePage (for pgsrc merge-edit) with appropriate Button()
1810  calls.
1811
1812  Revision 1.82  2003/11/18 19:48:01  carstenklapp
1813  Fixed missing gettext _() for button name.
1814
1815  Revision 1.81  2003/11/18 18:28:35  carstenklapp
1816  Bugfix: In the Load File function of PhpWikiAdministration: When doing
1817  a "Merge Edit" or "Restore Anyway", page names containing accented
1818  letters (such as locale/de/pgsrc/G%E4steBuch) would produce a file not
1819  found error (Use FilenameForPage funtion to urlencode page names).
1820
1821  Revision 1.80  2003/03/07 02:46:57  dairiki
1822  Omit checks for safe_mode before set_time_limit().  Just prefix the
1823  set_time_limit() calls with @ so that they fail silently if not
1824  supported.
1825
1826  Revision 1.79  2003/02/26 01:56:05  dairiki
1827  Only zip pages with legal pagenames.
1828
1829  Revision 1.78  2003/02/24 02:05:43  dairiki
1830  Fix "n bytes written" message when dumping HTML.
1831
1832  Revision 1.77  2003/02/21 04:12:05  dairiki
1833  Minor fixes for new cached markup.
1834
1835  Revision 1.76  2003/02/16 19:47:17  dairiki
1836  Update WikiDB timestamp when editing or deleting pages.
1837
1838  Revision 1.75  2003/02/15 03:04:30  dairiki
1839  Fix for WikiUser constructor API change.
1840
1841  Revision 1.74  2003/02/15 02:18:04  dairiki
1842  When default language was English (at least), pgsrc was being
1843  loaded twice.
1844
1845  LimitedFileSet: Fix typo/bug. ($include was being ignored.)
1846
1847  SetupWiki(): Fix bugs in loading of $GenericPages.
1848
1849  Revision 1.73  2003/01/28 21:09:17  zorloc
1850  The get_cfg_var() function should only be used when one is
1851  interested in the value from php.ini or similar. Use ini_get()
1852  instead to get the effective value of a configuration variable.
1853  -- Martin Geisler
1854
1855  Revision 1.72  2003/01/03 22:25:53  carstenklapp
1856  Cosmetic fix to "Merge Edit" & "Overwrite" buttons. Added "The PhpWiki
1857  programming team" as author when loading from pgsrc. Source
1858  reformatting.
1859
1860  Revision 1.71  2003/01/03 02:48:05  carstenklapp
1861  function SavePage: Added loadfile options for overwriting or merge &
1862  compare a loaded pgsrc file with an existing page.
1863
1864  function LoadAny: Added a general error message when unable to load a
1865  file instead of defaulting to "Bad file type".
1866
1867  */
1868
1869 // For emacs users
1870 // Local Variables:
1871 // mode: php
1872 // tab-width: 8
1873 // c-basic-offset: 4
1874 // c-hanging-comment-ender-p: nil
1875 // indent-tabs-mode: nil
1876 // End:
1877 ?>