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