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