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