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