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