]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/loadsave.php
fixed the StartLoadDump html argument hack.
[SourceForge/phpwiki.git] / lib / loadsave.php
1 <?php //-*-php-*-
2 rcs_id('$Id: loadsave.php,v 1.111 2004-06-21 16:38:55 rurban Exp $');
3
4 /*
5  Copyright 1999, 2000, 2001, 2002 $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
25 require_once("lib/ziplib.php");
26 require_once("lib/Template.php");
27
28 /**
29  * ignore fatal errors during dump
30  */
31 function _dump_error_handler(&$error) {
32     if ($error->isFatal()) {
33         $error->errno = E_USER_WARNING;
34         return true;
35     }
36     return true;         // Ignore error
37     /*
38     if (preg_match('/Plugin/', $error->errstr))
39         return true;
40     */
41     // let the message come through: call the remaining handlers:
42     // return false; 
43 }
44
45 function StartLoadDump(&$request, $title, $html = '')
46 {
47     // FIXME: This is a hack
48     if ($html)
49         $html->pushContent('%BODY%');
50     $tmpl = Template('html', array('TITLE' => $title,
51                                    'HEADER' => $title,
52                                    'CONTENT' => $html ? $html : '%BODY%'));
53     echo ereg_replace('%BODY%.*', '', $tmpl->getExpansion($html));
54
55     /* Ignore fatals or warnings in any pagedumps (failing plugins). 
56      * WikiFunctionCb() fails with 4.0.6, works ok with 4.1.1 
57      */
58     if (!check_php_version(4,1) or DEBUG) return;
59     global $ErrorManager;
60     $ErrorManager->pushErrorHandler(new WikiFunctionCb('_dump_error_handler'));
61 }
62
63 function EndLoadDump(&$request)
64 {
65     if (check_php_version(4,1)) {
66         global $ErrorManager;
67         $ErrorManager->popErrorHandler();
68     }
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     $pagelink = WikiLink(new WikiPageName(_("PhpWikiAdministration"),false,$label));
82
83     PrintXML(HTML::p(HTML::strong(_("Complete."))),
84              HTML::p(fmt("Return to %s", $pagelink)));
85     echo "</body></html>\n";
86 }
87
88
89 ////////////////////////////////////////////////////////////////
90 //
91 //  Functions for dumping.
92 //
93 ////////////////////////////////////////////////////////////////
94
95 /**
96  * For reference see:
97  * http://www.nacs.uci.edu/indiv/ehood/MIME/2045/rfc2045.html
98  * http://www.faqs.org/rfcs/rfc2045.html
99  * (RFC 1521 has been superceeded by RFC 2045 & others).
100  *
101  * Also see http://www.faqs.org/rfcs/rfc2822.html
102  */
103 function MailifyPage ($page, $nversions = 1)
104 {
105     $current = $page->getCurrentRevision();
106     $head = '';
107
108     if (STRICT_MAILABLE_PAGEDUMPS) {
109         $from = defined('SERVER_ADMIN') ? SERVER_ADMIN : 'foo@bar';
110         //This is for unix mailbox format: (not RFC (2)822)
111         // $head .= "From $from  " . CTime(time()) . "\r\n";
112         $head .= "Subject: " . rawurlencode($page->getName()) . "\r\n";
113         $head .= "From: $from (PhpWiki)\r\n";
114         // RFC 2822 requires only a Date: and originator (From:)
115         // field, however the obsolete standard RFC 822 also
116         // requires a destination field.
117         $head .= "To: $from (PhpWiki)\r\n";
118     }
119     $head .= "Date: " . Rfc2822DateTime($current->get('mtime')) . "\r\n";
120     $head .= sprintf("Mime-Version: 1.0 (Produced by PhpWiki %s)\r\n",
121                      PHPWIKI_VERSION);
122
123     // This should just be entered by hand (or by script?)
124     // in the actual pgsrc files, since only they should have
125     // RCS ids.
126     //$head .= "X-Rcs-Id: \$Id\$\r\n";
127
128     $iter = $page->getAllRevisions();
129     $parts = array();
130     while ($revision = $iter->next()) {
131         $parts[] = MimeifyPageRevision($revision);
132         if ($nversions > 0 && count($parts) >= $nversions)
133             break;
134     }
135     if (count($parts) > 1)
136         return $head . MimeMultipart($parts);
137     assert($parts);
138     return $head . $parts[0];
139 }
140
141 /***
142  * Compute filename to used for storing contents of a wiki page.
143  *
144  * Basically we do a rawurlencode() which encodes everything except
145  * ASCII alphanumerics and '.', '-', and '_'.
146  *
147  * But we also want to encode leading dots to avoid filenames like
148  * '.', and '..'. (Also, there's no point in generating "hidden" file
149  * names, like '.foo'.)
150  *
151  * @param $pagename string Pagename.
152  * @return string Filename for page.
153  */
154 function FilenameForPage ($pagename)
155 {
156     $enc = rawurlencode($pagename);
157     return preg_replace('/^\./', '%2e', $enc);
158 }
159
160 /**
161  * The main() function which generates a zip archive of a PhpWiki.
162  *
163  * If $include_archive is false, only the current version of each page
164  * is included in the zip file; otherwise all archived versions are
165  * included as well.
166  */
167 function MakeWikiZip (&$request)
168 {
169     if ($request->getArg('include') == 'all') {
170         $zipname         = WIKI_NAME . _("FullDump") . date('Ymd-Hi') . '.zip';
171         $include_archive = true;
172     }
173     else {
174         $zipname         = WIKI_NAME . _("LatestSnapshot") . date('Ymd-Hi') . '.zip';
175         $include_archive = false;
176     }
177
178
179     $zip = new ZipWriter("Created by PhpWiki " . PHPWIKI_VERSION, $zipname);
180
181     /* ignore fatals in plugins */
182     if (check_php_version(4,1)) {
183         global $ErrorManager;
184         $ErrorManager->pushErrorHandler(new WikiFunctionCb('_dump_error_handler'));
185     }
186
187     $dbi = $request->getDbh();
188     $pages = $dbi->getAllPages();
189     while ($page = $pages->next()) {
190         if (! $request->getArg('start_debug'))
191             @set_time_limit(30); // Reset watchdog
192
193         $current = $page->getCurrentRevision();
194         if ($current->getVersion() == 0)
195             continue;
196
197         $wpn = new WikiPageName($page->getName());
198         if (!$wpn->isValid())
199             continue;
200
201         $attrib = array('mtime'    => $current->get('mtime'),
202                         'is_ascii' => 1);
203         if ($page->get('locked'))
204             $attrib['write_protected'] = 1;
205
206         if ($include_archive)
207             $content = MailifyPage($page, 0);
208         else
209             $content = MailifyPage($page);
210
211         $zip->addRegularFile( FilenameForPage($page->getName()),
212                               $content, $attrib);
213     }
214     $zip->finish();
215     if (check_php_version(4,1)) {
216         $ErrorManager->popErrorHandler();
217     }
218 }
219
220 function DumpToDir (&$request)
221 {
222     $directory = $request->getArg('directory');
223     if (empty($directory))
224         $directory = DEFAULT_DUMP_DIR; // See lib/plugin/WikiForm.php:87
225     if (empty($directory))
226         $request->finish(_("You must specify a directory to dump to"));
227
228     // see if we can access the directory the user wants us to use
229     if (! file_exists($directory)) {
230         if (! mkdir($directory, 0755))
231             $request->finish(fmt("Cannot create directory '%s'", $directory));
232         else
233             $html = HTML::p(fmt("Created directory '%s' for the page dump...",
234                                 $directory));
235     } else {
236         $html = HTML::p(fmt("Using directory '%s'", $directory));
237     }
238
239     StartLoadDump($request, _("Dumping Pages"), $html);
240
241     $dbi = $request->getDbh();
242     $pages = $dbi->getAllPages();
243
244     while ($page = $pages->next()) {
245         if (! $request->getArg('start_debug'))
246           @set_time_limit(30); // Reset watchdog.
247
248         $filename = FilenameForPage($page->getName());
249
250         $msg = HTML(HTML::br(), $page->getName(), ' ... ');
251
252         if($page->getName() != $filename) {
253             $msg->pushContent(HTML::small(fmt("saved as %s", $filename)),
254                               " ... ");
255         }
256
257         if ($request->getArg('include') == 'all')
258             $data = MailifyPage($page, 0);
259         else
260             $data = MailifyPage($page);
261
262         if ( !($fd = fopen("$directory/$filename", "wb")) ) {
263             $msg->pushContent(HTML::strong(fmt("couldn't open file '%s' for writing",
264                                                "$directory/$filename")));
265             $request->finish($msg);
266         }
267
268         $num = fwrite($fd, $data, strlen($data));
269         $msg->pushContent(HTML::small(fmt("%s bytes written", $num)));
270         PrintXML($msg);
271
272         flush();
273         assert($num == strlen($data));
274         fclose($fd);
275     }
276
277     EndLoadDump($request);
278 }
279
280
281 function DumpHtmlToDir (&$request)
282 {
283     $directory = $request->getArg('directory');
284     if (empty($directory))
285         $directory = HTML_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     $thispage = $request->getArg('pagename'); // for "Return to ..."
302
303     $dbi = $request->getDbh();
304     if ($whichpages = $request->getArg('pages')) {  // which pagenames
305         if ($whichpages == '[]') // current page
306             $whichpages = $thispage;
307         $pages = new WikiDB_Array_PageIterator(explodePageList($whichpages));
308     } else {
309         $pages = $dbi->getAllPages();
310     }
311
312     global $WikiTheme;
313     if (defined('HTML_DUMP_SUFFIX'))
314         $WikiTheme->HTML_DUMP_SUFFIX = HTML_DUMP_SUFFIX;
315     $WikiTheme->DUMP_MODE = 'HTML';
316
317     while ($page = $pages->next()) {
318         if (! $request->getArg('start_debug'))
319           @set_time_limit(30); // Reset watchdog.
320
321         $pagename = $page->getName();
322         $request->setArg('pagename',$pagename); // Template::_basepage fix
323         $filename = FilenameForPage($pagename) . $WikiTheme->HTML_DUMP_SUFFIX;
324
325         $msg = HTML(HTML::br(), $pagename, ' ... ');
326
327         $revision = $page->getCurrentRevision();
328         $transformedContent = $revision->getTransformedContent();
329         $template = new Template('browse', $request,
330                                  array('revision' => $revision,
331                                        'CONTENT' => $transformedContent));
332
333         $data = GeneratePageasXML($template, $pagename);
334
335         if ( !($fd = fopen("$directory/$filename", "wb")) ) {
336             $msg->pushContent(HTML::strong(fmt("couldn't open file '%s' for writing",
337                                                "$directory/$filename")));
338             $request->finish($msg);
339         }
340         $num = fwrite($fd, $data, strlen($data));
341         if($page->getName() != $filename) {
342             $prefix = '';
343             if (isWindows()) {  
344                 // drive where apache is installed
345                 $prefix = '/' . substr($_SERVER["DOCUMENT_ROOT"],0,2);
346             }
347             $link = LinkURL("file://".$prefix.$directory."/".$filename, 
348                             $filename);
349             $msg->pushContent(HTML::small(_("saved as "), $link, " ... "));
350         }
351         $msg->pushContent(HTML::small(fmt("%s bytes written", $num), "\n"));
352         PrintXML($msg);
353
354         flush();
355         assert($num == strlen($data));
356         fclose($fd);
357     }
358
359     if (is_array($WikiTheme->dumped_images)) {
360         @mkdir("$directory/images");
361         foreach ($WikiTheme->dumped_images as $img_file) {
362             if (($from = $WikiTheme->_findFile($img_file)) and basename($from)) {
363                 $target = "$directory/images/".basename($img_file);
364                 if (copy($WikiTheme->_path . $from, $target)) {
365                     $msg = HTML(HTML::br(), HTML($from), HTML::small(fmt("... copied to %s", $target)));
366                     PrintXML($msg);
367                 }
368             } else {
369                 $msg = HTML(HTML::br(), HTML($from), HTML::small(fmt("... not found", $target)));
370                 PrintXML($msg);
371             }
372         }
373     }
374     if (is_array($WikiTheme->dumped_buttons)) {
375         // Buttons also
376         @mkdir("$directory/images/buttons");
377         foreach ($WikiTheme->dumped_buttons as $text => $img_file) {
378             if (($from = $WikiTheme->_findFile($img_file)) and basename($from)) {
379                 $target = "$directory/images/buttons/".basename($img_file);
380                 if (copy($WikiTheme->_path . $from, $target)) {
381                     $msg = HTML(HTML::br(), HTML($from), HTML::small(fmt("... copied to %s", $target)));
382                     PrintXML($msg);
383                 }
384             } else {
385                 $msg = HTML(HTML::br(), HTML($from), HTML::small(fmt("... not found", $target)));
386                 PrintXML($msg);
387             }
388         }
389     }
390     if (is_array($WikiTheme->dumped_css)) {
391       foreach ($WikiTheme->dumped_css as $css_file) {
392           if (($from = $WikiTheme->_findFile(basename($css_file))) and basename($from)) {
393               $target = "$directory/" . basename($css_file);
394               if (copy($WikiTheme->_path . $from, $target)) {
395                   $msg = HTML(HTML::br(), HTML($from), HTML::small(fmt("... copied to %s", $target)));
396                   PrintXML($msg);
397               }
398           } else {
399               $msg = HTML(HTML::br(), HTML($from), HTML::small(fmt("... not found", $target)));
400               PrintXML($msg);
401           }
402       }
403     }
404     $WikiTheme->HTML_DUMP_SUFFIX = '';
405     $WikiTheme->DUMP_MODE = false;
406
407     $request->setArg('pagename',$thispage); // Template::_basepage fix
408     EndLoadDump($request);
409 }
410
411 /* Known problem: any plugins or other code which echo()s text will
412  * lead to a corrupted html zip file which may produce the following
413  * errors upon unzipping:
414  *
415  * warning [wikihtml.zip]:  2401 extra bytes at beginning or within zipfile
416  * file #58:  bad zipfile offset (local header sig):  177561
417  *  (attempting to re-compensate)
418  *
419  * However, the actual wiki page data should be unaffected.
420  */
421 function MakeWikiZipHtml (&$request)
422 {
423     $zipname = "wikihtml.zip";
424     $zip = new ZipWriter("Created by PhpWiki " . PHPWIKI_VERSION, $zipname);
425     $dbi = $request->getDbh();
426     $pages = $dbi->getAllPages();
427
428     global $WikiTheme;
429     if (defined('HTML_DUMP_SUFFIX'))
430         $WikiTheme->HTML_DUMP_SUFFIX = HTML_DUMP_SUFFIX;
431
432     /* ignore fatals in plugins */
433     if (check_php_version(4,1)) {
434         global $ErrorManager;
435         $ErrorManager->pushErrorHandler(new WikiFunctionCb('_dump_error_handler'));
436     }
437
438     while ($page = $pages->next()) {
439         if (! $request->getArg('start_debug'))
440             @set_time_limit(30); // Reset watchdog.
441
442         $current = $page->getCurrentRevision();
443         if ($current->getVersion() == 0)
444             continue;
445
446         $attrib = array('mtime'    => $current->get('mtime'),
447                         'is_ascii' => 1);
448         if ($page->get('locked'))
449             $attrib['write_protected'] = 1;
450
451         $pagename = $page->getName();
452         $request->setArg('pagename',$pagename); // Template::_basepage fix
453         $filename = FilenameForPage($pagename) . $WikiTheme->HTML_DUMP_SUFFIX;
454         $revision = $page->getCurrentRevision();
455
456         $transformedContent = $revision->getTransformedContent();
457
458         $template = new Template('browse', $request,
459                                  array('revision' => $revision,
460                                        'CONTENT' => $transformedContent));
461
462         $data = GeneratePageasXML($template, $pagename);
463
464         $zip->addRegularFile( $filename, $data, $attrib);
465     }
466     // FIXME: Deal with images here.
467     $zip->finish();
468     if (check_php_version(4,1)) {
469         $ErrorManager->popErrorHandler();
470     }
471     $WikiTheme->$HTML_DUMP_SUFFIX = '';
472 }
473
474
475 ////////////////////////////////////////////////////////////////
476 //
477 //  Functions for restoring.
478 //
479 ////////////////////////////////////////////////////////////////
480
481 function SavePage (&$request, $pageinfo, $source, $filename)
482 {
483     $pagedata    = $pageinfo['pagedata'];    // Page level meta-data.
484     $versiondata = $pageinfo['versiondata']; // Revision level meta-data.
485
486     if (empty($pageinfo['pagename'])) {
487         PrintXML(HTML::dt(HTML::strong(_("Empty pagename!"))));
488         return;
489     }
490
491     if (empty($versiondata['author_id']))
492         $versiondata['author_id'] = $versiondata['author'];
493
494     $pagename = $pageinfo['pagename'];
495     $content  = $pageinfo['content'];
496
497     if ($pagename ==_("InterWikiMap"))
498         $content = _tryinsertInterWikiMap($content);
499
500     $dbi = $request->getDbh();
501     $page = $dbi->getPage($pagename);
502
503     $current = $page->getCurrentRevision();
504     // Try to merge if updated pgsrc contents are different. This
505     // whole thing is hackish
506     //
507     // TODO: try merge unless:
508     // if (current contents = default contents && pgsrc_version >=
509     // pgsrc_version) then just upgrade this pgsrc
510     $needs_merge = false;
511     $merging = false;
512     $overwrite = false;
513
514     if ($request->getArg('merge')) {
515         $merging = true;
516     }
517     else if ($request->getArg('overwrite')) {
518         $overwrite = true;
519     }
520
521     if ( (! $current->hasDefaultContents())
522          && ($current->getPackedContent() != $content)
523          && ($merging == true) ) {
524         include_once('lib/editpage.php');
525         $request->setArg('pagename', $pagename);
526         $r = $current->getVersion();
527         $request->setArg('revision', $current->getVersion());
528         $p = new LoadFileConflictPageEditor($request);
529         $p->_content = $content;
530         $p->_currentVersion = $r - 1;
531         $p->editPage($saveFailed = true);
532         return; //early return
533     }
534
535     foreach ($pagedata as $key => $value) {
536         if (!empty($value))
537             $page->set($key, $value);
538     }
539
540     $mesg = HTML::dd();
541     $skip = false;
542     if ($source)
543         $mesg->pushContent(' ', fmt("from %s", $source));
544
545
546     $current = $page->getCurrentRevision();
547     if ($current->getVersion() == 0) {
548         $mesg->pushContent(' ', _("new page"));
549         $isnew = true;
550     }
551     else {
552         if ( (! $current->hasDefaultContents())
553              && ($current->getPackedContent() != $content) ) {
554             if ($overwrite) {
555                 $mesg->pushContent(' ',
556                                    fmt("has edit conflicts - overwriting anyway"));
557                 $skip = false;
558                 if (substr_count($source, 'pgsrc')) {
559                     $versiondata['author'] = _("The PhpWiki programming team");
560                     // but leave authorid as userid who loaded the file
561                 }
562             }
563             else {
564                 $mesg->pushContent(' ', fmt("has edit conflicts - skipped"));
565                 $needs_merge = true; // hackish
566                 $skip = true;
567             }
568         }
569         else if ($current->getPackedContent() == $content
570                  && $current->get('author') == $versiondata['author']) {
571             // The page metadata is already changed, we don't need a new revision.
572             // This was called previously "is identical to current version %d - skipped"
573             // which is wrong, since the pagedata was stored, not skipped.
574             $mesg->pushContent(' ',
575                                fmt("content is identical to current version %d - no new revision created",
576                                    $current->getVersion()));
577             $skip = true;
578         }
579         $isnew = false;
580     }
581
582     if (! $skip) {
583         $new = $page->save($content, WIKIDB_FORCE_CREATE, $versiondata);
584         $dbi->touch();
585         $mesg->pushContent(' ', fmt("- saved to database as version %d",
586                                     $new->getVersion()));
587     }
588     if ($needs_merge) {
589         $f = $source;
590         // hackish, $source contains needed path+filename
591         $f = str_replace(sprintf(_("MIME file %s"), ''), '', $f);
592         $f = str_replace(sprintf(_("Serialized file %s"), ''), '', $f);
593         $f = str_replace(sprintf(_("plain file %s"), ''), '', $f);
594         //check if uploaded file? they pass just the content, but the file is gone
595         if (@stat($f)) {
596             global $WikiTheme;
597             $meb = Button(array('action' => 'loadfile',
598                                 'merge'=> true,
599                                 'source'=> $f),
600                           _("Merge Edit"),
601                           _("PhpWikiAdministration"),
602                           'wikiadmin');
603             $owb = Button(array('action' => 'loadfile',
604                                 'overwrite'=> true,
605                                 'source'=> $f),
606                           _("Restore Anyway"),
607                           _("PhpWikiAdministration"),
608                           'wikiunsafe');
609             $mesg->pushContent(' ', $meb, " ", $owb);
610         } else {
611             $mesg->pushContent(HTML::em(_(" Sorry, cannot merge uploaded files.")));
612         }
613     }
614
615     if ($skip)
616         PrintXML(HTML::dt(HTML::em(WikiLink($pagename))), $mesg);
617     else
618         PrintXML(HTML::dt(WikiLink($pagename)), $mesg);
619     flush();
620 }
621
622 // action=revert (by diff)
623 function RevertPage (&$request)
624 {
625     $mesg = HTML::dd();
626     $pagename = $request->getArg('pagename');
627     $version = $request->getArg('version');
628     if (!$version) {
629         PrintXML(HTML::dt(fmt("Revert")," ",WikiLink($pagename)),
630                  HTML::dd(_("missing required version argument")));
631         return;
632     }
633     $dbi = $request->getDbh();
634     $page = $dbi->getPage($pagename);
635     $current = $page->getCurrentRevision();
636     if ($current->getVersion() == 0) {
637         $mesg->pushContent(' ', _("no page content"));
638         PrintXML(HTML::dt(fmt("Revert")," ",WikiLink($pagename)),
639                  $mesg);
640         return;
641     }
642     if ($current->getVersion() == $version) {
643         $mesg->pushContent(' ', _("same version page"));
644         return;
645     }
646     $rev = $page->getRevision($version);
647     $content = $rev->getPackedContent();
648     $versiondata = $rev->_data;
649     $versiondata['summary'] = sprintf(_("revert to version %d"), $version);
650     $new = $page->save($content, $current->getVersion() + 1, $versiondata);
651     $dbi->touch();
652     $mesg->pushContent(' ', fmt("- version %d saved to database as version %d",
653                                 $version, $new->getVersion()));
654     PrintXML(HTML::dt(fmt("Revert")," ",WikiLink($pagename)),
655              $mesg);
656     flush();
657 }
658
659 function _tryinsertInterWikiMap($content) {
660     $goback = false;
661     if (strpos($content, "<verbatim>")) {
662         //$error_html = " The newly loaded pgsrc already contains a verbatim block.";
663         $goback = true;
664     }
665     if (!$goback && !defined('INTERWIKI_MAP_FILE')) {
666         $error_html = sprintf(" "._("%s: not defined"), "INTERWIKI_MAP_FILE");
667         $goback = true;
668     }
669     $mapfile = FindFile(INTERWIKI_MAP_FILE,1);
670     if (!$goback && !file_exists($mapfile)) {
671         $error_html = sprintf(" "._("%s: file not found"), INTERWIKI_MAP_FILE);
672         $goback = true;
673     }
674
675     if (!empty($error_html))
676         trigger_error(_("Default InterWiki map file not loaded.")
677                       . $error_html, E_USER_NOTICE);
678     if ($goback)
679         return $content;
680
681     // if loading from virgin setup do echo, otherwise trigger_error E_USER_NOTICE
682     echo sprintf(_("Loading InterWikiMap from external file %s."), $mapfile),"<br />";
683
684     $fd = fopen ($mapfile, "rb");
685     $data = fread ($fd, filesize($mapfile));
686     fclose ($fd);
687     $content = $content . "\n<verbatim>\n$data</verbatim>\n";
688     return $content;
689 }
690
691 function ParseSerializedPage($text, $default_pagename, $user)
692 {
693     if (!preg_match('/^a:\d+:{[si]:\d+/', $text))
694         return false;
695
696     $pagehash = unserialize($text);
697
698     // Split up pagehash into four parts:
699     //   pagename
700     //   content
701     //   page-level meta-data
702     //   revision-level meta-data
703
704     if (!defined('FLAG_PAGE_LOCKED'))
705         define('FLAG_PAGE_LOCKED', 1);
706     $pageinfo = array('pagedata'    => array(),
707                       'versiondata' => array());
708
709     $pagedata = &$pageinfo['pagedata'];
710     $versiondata = &$pageinfo['versiondata'];
711
712     // Fill in defaults.
713     if (empty($pagehash['pagename']))
714         $pagehash['pagename'] = $default_pagename;
715     if (empty($pagehash['author'])) {
716         $pagehash['author'] = $user->getId();
717     }
718
719     foreach ($pagehash as $key => $value) {
720         switch($key) {
721             case 'pagename':
722             case 'version':
723             case 'hits':
724                 $pageinfo[$key] = $value;
725                 break;
726             case 'content':
727                 $pageinfo[$key] = join("\n", $value);
728                 break;
729             case 'flags':
730                 if (($value & FLAG_PAGE_LOCKED) != 0)
731                     $pagedata['locked'] = 'yes';
732                 break;
733             case 'owner':
734             case 'created':
735                 $pagedata[$key] = $value;
736                 break;
737             case 'acl':
738             case 'perm':
739                 $pagedata['perm'] = ParseMimeifiedPerm($value);
740                 break;
741             case 'lastmodified':
742                 $versiondata['mtime'] = $value;
743                 break;
744             case 'author':
745             case 'author_id':
746             case 'summary':
747                 $versiondata[$key] = $value;
748                 break;
749         }
750     }
751     return $pageinfo;
752 }
753
754 function SortByPageVersion ($a, $b) {
755     return $a['version'] - $b['version'];
756 }
757
758 function LoadFile (&$request, $filename, $text = false, $mtime = false)
759 {
760     if (!is_string($text)) {
761         // Read the file.
762         $stat  = stat($filename);
763         $mtime = $stat[9];
764         $text  = implode("", file($filename));
765     }
766
767     if (! $request->getArg('start_debug'))
768         @set_time_limit(30); // Reset watchdog.
769
770     // FIXME: basename("filewithnoslashes") seems to return garbage sometimes.
771     $basename = basename("/dummy/" . $filename);
772
773     if (!$mtime)
774         $mtime = time();    // Last resort.
775
776     $default_pagename = rawurldecode($basename);
777
778     if ( ($parts = ParseMimeifiedPages($text)) ) {
779         usort($parts, 'SortByPageVersion');
780         foreach ($parts as $pageinfo)
781             SavePage($request, $pageinfo, sprintf(_("MIME file %s"),
782                                                   $filename), $basename);
783     }
784     else if ( ($pageinfo = ParseSerializedPage($text, $default_pagename,
785                                                $request->getUser())) ) {
786         SavePage($request, $pageinfo, sprintf(_("Serialized file %s"),
787                                               $filename), $basename);
788     }
789     else {
790         $user = $request->getUser();
791
792         // Assume plain text file.
793         $pageinfo = array('pagename' => $default_pagename,
794                           'pagedata' => array(),
795                           'versiondata'
796                           => array('author' => $user->getId()),
797                           'content'  => preg_replace('/[ \t\r]*\n/', "\n",
798                                                      chop($text))
799                           );
800         SavePage($request, $pageinfo, sprintf(_("plain file %s"), $filename),
801                  $basename);
802     }
803 }
804
805 function LoadZip (&$request, $zipfile, $files = false, $exclude = false) {
806     $zip = new ZipReader($zipfile);
807     while (list ($fn, $data, $attrib) = $zip->readFile()) {
808         // FIXME: basename("filewithnoslashes") seems to return
809         // garbage sometimes.
810         $fn = basename("/dummy/" . $fn);
811         if ( ($files && !in_array($fn, $files))
812              || ($exclude && in_array($fn, $exclude)) ) {
813             PrintXML(HTML::dt(WikiLink($fn)),
814                      HTML::dd(_("Skipping")));
815             continue;
816         }
817
818         LoadFile($request, $fn, $data, $attrib['mtime']);
819     }
820 }
821
822 function LoadDir (&$request, $dirname, $files = false, $exclude = false) {
823     $fileset = new LimitedFileSet($dirname, $files, $exclude);
824
825     if (!$files and ($skiplist = $fileset->getSkippedFiles())) {
826         PrintXML(HTML::dt(HTML::strong(_("Skipping"))));
827         $list = HTML::ul();
828         foreach ($skiplist as $file)
829             $list->pushContent(HTML::li(WikiLink($file)));
830         PrintXML(HTML::dd($list));
831     }
832
833     // Defer HomePage loading until the end. If anything goes wrong
834     // the pages can still be loaded again.
835     $files = $fileset->getFiles();
836     if (in_array(HOME_PAGE, $files)) {
837         $files = array_diff($files, array(HOME_PAGE));
838         $files[] = HOME_PAGE;
839     }
840     foreach ($files as $file) {
841         if (substr($file,-1,1) != '~') // refuse to load backup files
842             LoadFile($request, "$dirname/$file");
843     }
844 }
845
846 class LimitedFileSet extends FileSet {
847     function LimitedFileSet($dirname, $_include, $exclude) {
848         $this->_includefiles = $_include;
849         $this->_exclude = $exclude;
850         $this->_skiplist = array();
851         parent::FileSet($dirname);
852     }
853
854     function _filenameSelector($fn) {
855         $incl = &$this->_includefiles;
856         $excl = &$this->_exclude;
857
858         if ( ($incl && !in_array($fn, $incl))
859              || ($excl && in_array($fn, $excl)) ) {
860             $this->_skiplist[] = $fn;
861             return false;
862         } else {
863             return true;
864         }
865     }
866
867     function getSkippedFiles () {
868         return $this->_skiplist;
869     }
870 }
871
872
873 function IsZipFile ($filename_or_fd)
874 {
875     // See if it looks like zip file
876     if (is_string($filename_or_fd))
877     {
878         $fd    = fopen($filename_or_fd, "rb");
879         $magic = fread($fd, 4);
880         fclose($fd);
881     }
882     else
883     {
884         $fpos  = ftell($filename_or_fd);
885         $magic = fread($filename_or_fd, 4);
886         fseek($filename_or_fd, $fpos);
887     }
888
889     return $magic == ZIP_LOCHEAD_MAGIC || $magic == ZIP_CENTHEAD_MAGIC;
890 }
891
892
893 function LoadAny (&$request, $file_or_dir, $files = false, $exclude = false)
894 {
895     // Try urlencoded filename for accented characters.
896     if (!file_exists($file_or_dir)) {
897         // Make sure there are slashes first to avoid confusing phps
898         // with broken dirname or basename functions.
899         // FIXME: windows uses \ and :
900         if (is_integer(strpos($file_or_dir, "/"))) {
901             $file_or_dir = FindFile($file_or_dir);
902             // Panic
903             if (!file_exists($file_or_dir))
904                 $file_or_dir = dirname($file_or_dir) . "/"
905                     . urlencode(basename($file_or_dir));
906         } else {
907             // This is probably just a file.
908             $file_or_dir = urlencode($file_or_dir);
909         }
910     }
911
912     $type = filetype($file_or_dir);
913     if ($type == 'link') {
914         // For symbolic links, use stat() to determine
915         // the type of the underlying file.
916         list(,,$mode) = stat($file_or_dir);
917         $type = ($mode >> 12) & 017;
918         if ($type == 010)
919             $type = 'file';
920         elseif ($type == 004)
921             $type = 'dir';
922     }
923
924     if (! $type) {
925         $request->finish(fmt("Unable to load: %s", $file_or_dir));
926     }
927     else if ($type == 'dir') {
928         LoadDir($request, $file_or_dir, $files, $exclude);
929     }
930     else if ($type != 'file' && !preg_match('/^(http|ftp):/', $file_or_dir))
931     {
932         $request->finish(fmt("Bad file type: %s", $type));
933     }
934     else if (IsZipFile($file_or_dir)) {
935         LoadZip($request, $file_or_dir, $files, $exclude);
936     }
937     else /* if (!$files || in_array(basename($file_or_dir), $files)) */
938     {
939         LoadFile($request, $file_or_dir);
940     }
941 }
942
943 function LoadFileOrDir (&$request)
944 {
945     $source = $request->getArg('source');
946     $finder = new FileFinder;
947     $source = $finder->slashifyPath($source);
948     $page = rawurldecode(basename($source));
949     StartLoadDump($request, fmt("Loading '%s'", 
950         HTML(dirname($source),
951              dirname($source) ? "/" : "",
952              WikiLink($page,'auto'))));
953     echo "<dl>\n";
954     LoadAny($request, $source);
955     echo "</dl>\n";
956     EndLoadDump($request);
957 }
958
959 function SetupWiki (&$request)
960 {
961     global $GenericPages, $LANG;
962
963
964     //FIXME: This is a hack (err, "interim solution")
965     // This is a bogo-bogo-login:  Login without
966     // saving login information in session state.
967     // This avoids logging in the unsuspecting
968     // visitor as "The PhpWiki programming team".
969     //
970     // This really needs to be cleaned up...
971     // (I'm working on it.)
972     $real_user = $request->_user;
973     if (ENABLE_USER_NEW)
974         $request->_user = new _BogoUser(_("The PhpWiki programming team"));
975
976     else
977         $request->_user = new WikiUser($request, _("The PhpWiki programming team"),
978                                        WIKIAUTH_BOGO);
979
980     StartLoadDump($request, _("Loading up virgin wiki"));
981     echo "<dl>\n";
982
983     $pgsrc = FindLocalizedFile(WIKI_PGSRC);
984     $default_pgsrc = FindFile(DEFAULT_WIKI_PGSRC);
985
986     $request->setArg('overwrite',true);
987     if ($default_pgsrc != $pgsrc) {
988         LoadAny($request, $default_pgsrc, $GenericPages);
989     }
990     $request->setArg('overwrite',false);
991     LoadAny($request, $pgsrc);
992
993     // Ensure that all mandatory pages are loaded
994     $finder = new FileFinder;
995     foreach (array_merge(explode(':','OldTextFormattingRules:TextFormattingRules:PhpWikiAdministration'),
996                          $GLOBALS['AllActionPages'],
997                          array(constant('HOME_PAGE'))) as $f) {
998         $page = gettext($f);
999         if (isSubPage($page))
1000             $page = urlencode($page);
1001         if (! $request->_dbi->isWikiPage(urldecode($page)) ) {
1002             // translated version provided?
1003             if ($lf = FindLocalizedFile($pgsrc . $finder->_pathsep . $page, 1))
1004                 LoadAny($request, $lf);
1005             else { // load english version of required action page
1006                 LoadAny($request, FindFile(DEFAULT_WIKI_PGSRC . $finder->_pathsep . urlencode($f)));
1007                 $page = $f;
1008             }
1009         }
1010         if (!$request->_dbi->isWikiPage(urldecode($page))) {
1011             trigger_error(sprintf("Mandatory file %s couldn't be loaded!", $page),
1012                           E_USER_WARNING);
1013         }
1014     }
1015
1016     echo "</dl>\n";
1017     EndLoadDump($request);
1018 }
1019
1020 function LoadPostFile (&$request)
1021 {
1022     $upload = $request->getUploadedFile('file');
1023
1024     if (!$upload)
1025         $request->finish(_("No uploaded file to upload?")); // FIXME: more concise message
1026
1027
1028     // Dump http headers.
1029     StartLoadDump($request, sprintf(_("Uploading %s"), $upload->getName()));
1030     echo "<dl>\n";
1031
1032     $fd = $upload->open();
1033     if (IsZipFile($fd))
1034         LoadZip($request, $fd, false, array(_("RecentChanges")));
1035     else
1036         LoadFile($request, $upload->getName(), $upload->getContents());
1037
1038     echo "</dl>\n";
1039     EndLoadDump($request);
1040 }
1041
1042 /**
1043  $Log: not supported by cvs2svn $
1044  Revision 1.110  2004/06/21 16:22:30  rurban
1045  add DEFAULT_DUMP_DIR and HTML_DUMP_DIR constants, for easier cmdline dumps,
1046  fixed dumping buttons locally (images/buttons/),
1047  support pages arg for dumphtml,
1048  optional directory arg for dumpserial + dumphtml,
1049  fix a AllPages warning,
1050  show dump warnings/errors on DEBUG,
1051  don't warn just ignore on wikilens pagelist columns, if not loaded.
1052  RateIt pagelist column is called "rating", not "ratingwidget" (Dan?)
1053
1054  Revision 1.109  2004/06/17 11:31:05  rurban
1055  jump back to label after dump/upgrade
1056
1057  Revision 1.108  2004/06/16 12:43:01  rurban
1058  4.0.6 cannot use this errorhandler (not found)
1059
1060  Revision 1.107  2004/06/14 11:31:37  rurban
1061  renamed global $Theme to $WikiTheme (gforge nameclash)
1062  inherit PageList default options from PageList
1063    default sortby=pagename
1064  use options in PageList_Selectable (limit, sortby, ...)
1065  added action revert, with button at action=diff
1066  added option regex to WikiAdminSearchReplace
1067
1068  Revision 1.106  2004/06/13 13:54:25  rurban
1069  Catch fatals on the four dump calls (as file and zip, as html and mimified)
1070  FoafViewer: Check against external requirements, instead of fatal.
1071  Change output for xhtmldumps: using file:// urls to the local fs.
1072  Catch SOAP fatal by checking for GOOGLE_LICENSE_KEY
1073  Import GOOGLE_LICENSE_KEY and FORTUNE_DIR from config.ini.
1074
1075  Revision 1.105  2004/06/08 19:48:16  rurban
1076  fixed foreign setup: no ugly skipped msg for the GenericPages, load english actionpages if translated not found
1077
1078  Revision 1.104  2004/06/08 13:51:57  rurban
1079  some comments only
1080
1081  Revision 1.103  2004/06/08 10:54:46  rurban
1082  better acl dump representation, read back acl and owner
1083
1084  Revision 1.102  2004/06/06 16:58:51  rurban
1085  added more required ActionPages for foreign languages
1086  install now english ActionPages if no localized are found. (again)
1087  fixed default anon user level to be 0, instead of -1
1088    (wrong "required administrator to view this page"...)
1089
1090  Revision 1.101  2004/06/04 20:32:53  rurban
1091  Several locale related improvements suggested by Pierrick Meignen
1092  LDAP fix by John Cole
1093  reanable admin check without ENABLE_PAGEPERM in the admin plugins
1094
1095  Revision 1.100  2004/05/02 21:26:38  rurban
1096  limit user session data (HomePageHandle and auth_dbi have to invalidated anyway)
1097    because they will not survive db sessions, if too large.
1098  extended action=upgrade
1099  some WikiTranslation button work
1100  revert WIKIAUTH_UNOBTAINABLE (need it for main.php)
1101  some temp. session debug statements
1102
1103  Revision 1.99  2004/05/02 15:10:07  rurban
1104  new finally reliable way to detect if /index.php is called directly
1105    and if to include lib/main.php
1106  new global AllActionPages
1107  SetupWiki now loads all mandatory pages: HOME_PAGE, action pages, and warns if not.
1108  WikiTranslation what=buttons for Carsten to create the missing MacOSX buttons
1109  PageGroupTestOne => subpages
1110  renamed PhpWikiRss to PhpWikiRecentChanges
1111  more docs, default configs, ...
1112
1113  Revision 1.98  2004/04/29 23:25:12  rurban
1114  re-ordered locale init (as in 1.3.9)
1115  fixed loadfile with subpages, and merge/restore anyway
1116    (sf.net bug #844188)
1117
1118  Revision 1.96  2004/04/19 23:13:03  zorloc
1119  Connect the rest of PhpWiki to the IniConfig system.  Also the keyword regular expression is not a config setting
1120
1121  Revision 1.95  2004/04/18 01:11:52  rurban
1122  more numeric pagename fixes.
1123  fixed action=upload with merge conflict warnings.
1124  charset changed from constant to global (dynamic utf-8 switching)
1125
1126  Revision 1.94  2004/03/14 16:36:37  rurban
1127  dont load backup files
1128
1129  Revision 1.93  2004/02/26 03:22:05  rurban
1130  also copy css and images with XHTML Dump
1131
1132  Revision 1.92  2004/02/26 02:25:54  rurban
1133  fix empty and #-anchored links in XHTML Dumps
1134
1135  Revision 1.91  2004/02/24 17:19:37  rurban
1136  debugging helpers only
1137
1138  Revision 1.90  2004/02/24 17:09:24  rurban
1139  fixed \r\r\n with dumping on windows
1140
1141  Revision 1.88  2004/02/22 23:20:31  rurban
1142  fixed DumpHtmlToDir,
1143  enhanced sortby handling in PageList
1144    new button_heading th style (enabled),
1145  added sortby and limit support to the db backends and plugins
1146    for paging support (<<prev, next>> links on long lists)
1147
1148  Revision 1.87  2004/01/26 09:17:49  rurban
1149  * changed stored pref representation as before.
1150    the array of objects is 1) bigger and 2)
1151    less portable. If we would import packed pref
1152    objects and the object definition was changed, PHP would fail.
1153    This doesn't happen with an simple array of non-default values.
1154  * use $prefs->retrieve and $prefs->store methods, where retrieve
1155    understands the interim format of array of objects also.
1156  * simplified $prefs->get() and fixed $prefs->set()
1157  * added $user->_userid and class '_WikiUser' portability functions
1158  * fixed $user object ->_level upgrading, mostly using sessions.
1159    this fixes yesterdays problems with loosing authorization level.
1160  * fixed WikiUserNew::checkPass to return the _level
1161  * fixed WikiUserNew::isSignedIn
1162  * added explodePageList to class PageList, support sortby arg
1163  * fixed UserPreferences for WikiUserNew
1164  * fixed WikiPlugin for empty defaults array
1165  * UnfoldSubpages: added pagename arg, renamed pages arg,
1166    removed sort arg, support sortby arg
1167
1168  Revision 1.86  2003/12/02 16:18:26  carstenklapp
1169  Minor enhancement: Provide more meaningful filenames for WikiDB zip
1170  dumps & snapshots.
1171
1172  Revision 1.85  2003/11/30 18:18:13  carstenklapp
1173  Minor code optimization: use include_once instead of require_once
1174  inside functions that might not always called.
1175
1176  Revision 1.84  2003/11/26 20:47:47  carstenklapp
1177  Redo bugfix: My last refactoring broke merge-edit & overwrite
1178  functionality again, should be fixed now. Sorry.
1179
1180  Revision 1.83  2003/11/20 22:18:54  carstenklapp
1181  New feature: h1 during merge-edit displays WikiLink to original page.
1182  Internal changes: Replaced some hackish url-generation code in
1183  function SavePage (for pgsrc merge-edit) with appropriate Button()
1184  calls.
1185
1186  Revision 1.82  2003/11/18 19:48:01  carstenklapp
1187  Fixed missing gettext _() for button name.
1188
1189  Revision 1.81  2003/11/18 18:28:35  carstenklapp
1190  Bugfix: In the Load File function of PhpWikiAdministration: When doing
1191  a "Merge Edit" or "Restore Anyway", page names containing accented
1192  letters (such as locale/de/pgsrc/G%E4steBuch) would produce a file not
1193  found error (Use FilenameForPage funtion to urlencode page names).
1194
1195  Revision 1.80  2003/03/07 02:46:57  dairiki
1196  Omit checks for safe_mode before set_time_limit().  Just prefix the
1197  set_time_limit() calls with @ so that they fail silently if not
1198  supported.
1199
1200  Revision 1.79  2003/02/26 01:56:05  dairiki
1201  Only zip pages with legal pagenames.
1202
1203  Revision 1.78  2003/02/24 02:05:43  dairiki
1204  Fix "n bytes written" message when dumping HTML.
1205
1206  Revision 1.77  2003/02/21 04:12:05  dairiki
1207  Minor fixes for new cached markup.
1208
1209  Revision 1.76  2003/02/16 19:47:17  dairiki
1210  Update WikiDB timestamp when editing or deleting pages.
1211
1212  Revision 1.75  2003/02/15 03:04:30  dairiki
1213  Fix for WikiUser constructor API change.
1214
1215  Revision 1.74  2003/02/15 02:18:04  dairiki
1216  When default language was English (at least), pgsrc was being
1217  loaded twice.
1218
1219  LimitedFileSet: Fix typo/bug. ($include was being ignored.)
1220
1221  SetupWiki(): Fix bugs in loading of $GenericPages.
1222
1223  Revision 1.73  2003/01/28 21:09:17  zorloc
1224  The get_cfg_var() function should only be used when one is
1225  interested in the value from php.ini or similar. Use ini_get()
1226  instead to get the effective value of a configuration variable.
1227  -- Martin Geisler
1228
1229  Revision 1.72  2003/01/03 22:25:53  carstenklapp
1230  Cosmetic fix to "Merge Edit" & "Overwrite" buttons. Added "The PhpWiki
1231  programming team" as author when loading from pgsrc. Source
1232  reformatting.
1233
1234  Revision 1.71  2003/01/03 02:48:05  carstenklapp
1235  function SavePage: Added loadfile options for overwriting or merge &
1236  compare a loaded pgsrc file with an existing page.
1237
1238  function LoadAny: Added a general error message when unable to load a
1239  file instead of defaulting to "Bad file type".
1240
1241  */
1242
1243 // For emacs users
1244 // Local Variables:
1245 // mode: php
1246 // tab-width: 8
1247 // c-basic-offset: 4
1248 // c-hanging-comment-ender-p: nil
1249 // indent-tabs-mode: nil
1250 // End:
1251 ?>