]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/loadsave.php
New function for dumping pages as html. It mostly works, but I couldn't get the Templ...
[SourceForge/phpwiki.git] / lib / loadsave.php
1 <?php rcs_id('$Id: loadsave.php,v 1.57 2002-02-20 00:14:40 carstenklapp Exp $');
2
3 require_once("lib/ziplib.php");
4 require_once("lib/Template.php");
5
6 function StartLoadDump(&$request, $title, $html = '')
7 {
8     // FIXME: This is a hack
9     $tmpl = Template('top', array('TITLE' => $title,
10                                   'HEADER' => $title,
11                                   'CONTENT' => '%BODY%'));
12     echo ereg_replace('%BODY%.*', '', $tmpl->getExpansion($html));
13 }
14
15 function EndLoadDump(&$request)
16 {
17     // FIXME: This is a hack
18     $pagelink = WikiLink($request->getPage());
19
20     PrintXML(HTML::p(HTML::strong(_("Complete."))),
21              HTML::p(fmt("Return to %s", $pagelink)));
22     echo "</body></html>\n";
23 }
24
25
26 ////////////////////////////////////////////////////////////////
27 //
28 //  Functions for dumping.
29 //
30 ////////////////////////////////////////////////////////////////
31
32 /**
33  * For reference see:
34  * http://www.nacs.uci.edu/indiv/ehood/MIME/2045/rfc2045.html
35  * http://www.faqs.org/rfcs/rfc2045.html
36  * (RFC 1521 has been superceeded by RFC 2045 & others).
37  *
38  * Also see http://www.faqs.org/rfcs/rfc2822.html
39  */
40 function MailifyPage ($page, $nversions = 1)
41 {
42     $current = $page->getCurrentRevision();
43     $head = '';
44
45     if (STRICT_MAILABLE_PAGEDUMPS) {
46         $from = defined('SERVER_ADMIN') ? SERVER_ADMIN : 'foo@bar';
47         //This is for unix mailbox format: (not RFC (2)822)
48         // $head .= "From $from  " . CTime(time()) . "\r\n";
49         $head .= "Subject: " . rawurlencode($page->getName()) . "\r\n";
50         $head .= "From: $from (PhpWiki)\r\n";
51         // RFC 2822 requires only a Date: and originator (From:)
52         // field, however the obsolete standard RFC 822 also
53         // requires a destination field.
54         $head .= "To: $from (PhpWiki)\r\n";
55     }
56     $head .= "Date: " . Rfc2822DateTime($current->get('mtime')) . "\r\n";
57     $head .= sprintf("Mime-Version: 1.0 (Produced by PhpWiki %s)\r\n",
58                      PHPWIKI_VERSION);
59
60     // This should just be entered by hand (or by script?)
61     // in the actual pgsrc files, since only they should have
62     // RCS ids.
63     //$head .= "X-Rcs-Id: \$Id\$\r\n";
64
65     $iter = $page->getAllRevisions();
66     $parts = array();
67     while ($revision = $iter->next()) {
68         $parts[] = MimeifyPageRevision($revision);
69         if ($nversions > 0 && count($parts) >= $nversions)
70             break;
71     }
72     if (count($parts) > 1)
73         return $head . MimeMultipart($parts);
74     assert($parts);
75     return $head . $parts[0];
76 }
77
78 /***
79  * Compute filename to used for storing contents of a wiki page.
80  *
81  * Basically we do a rawurlencode() which encodes everything except
82  * ASCII alphanumerics and '.', '-', and '_'.
83  *
84  * But we also want to encode leading dots to avoid filenames like
85  * '.', and '..'. (Also, there's no point in generating "hidden" file
86  * names, like '.foo'.)
87  *
88  * @param $pagename string Pagename.
89  * @return string Filename for page.
90  */
91 function FilenameForPage ($pagename)
92 {
93     $enc = rawurlencode($pagename);
94     return preg_replace('/^\./', '%2e', $enc);
95 }
96
97 /**
98  * The main() function which generates a zip archive of a PhpWiki.
99  *
100  * If $include_archive is false, only the current version of each page
101  * is included in the zip file; otherwise all archived versions are
102  * included as well.
103  */
104 function MakeWikiZip (&$request)
105 {
106     if ($request->getArg('include') == 'all') {
107         $zipname         = "wikidb.zip";
108         $include_archive = true;
109     }
110     else {
111         $zipname         = "wiki.zip";
112         $include_archive = false;
113     }
114
115
116
117     $zip = new ZipWriter("Created by PhpWiki", $zipname);
118
119     $dbi = $request->getDbh();
120     $pages = $dbi->getAllPages();
121     while ($page = $pages->next()) {
122         set_time_limit(30);     // Reset watchdog.
123
124         $current = $page->getCurrentRevision();
125         if ($current->getVersion() == 0)
126             continue;
127
128
129         $attrib = array('mtime'    => $current->get('mtime'),
130                         'is_ascii' => 1);
131         if ($page->get('locked'))
132             $attrib['write_protected'] = 1;
133
134         if ($include_archive)
135             $content = MailifyPage($page, 0);
136         else
137             $content = MailifyPage($page);
138
139         $zip->addRegularFile( FilenameForPage($page->getName()),
140                               $content, $attrib);
141     }
142     $zip->finish();
143 }
144
145 function DumpToDir (&$request)
146 {
147     $directory = $request->getArg('directory');
148     if (empty($directory))
149         $request->finish(_("You must specify a directory to dump to"));
150
151     // see if we can access the directory the user wants us to use
152     if (! file_exists($directory)) {
153         if (! mkdir($directory, 0755))
154             $request->finish(fmt("Cannot create directory '%s'", $directory));
155         else
156             $html = HTML::p(fmt("Created directory '%s' for the page dump...",
157                                 $directory));
158     } else {
159         $html = HTML::p(fmt("Using directory '%s'", $directory));
160     }
161
162     StartLoadDump($request, _("Dumping Pages"), $html);
163
164     $dbi = $request->getDbh();
165     $pages = $dbi->getAllPages();
166
167     while ($page = $pages->next()) {
168
169         $filename = FilenameForPage($page->getName());
170
171         $msg = HTML(HTML::br(), $page->getName(), ' ... ');
172
173         if($page->getName() != $filename) {
174             $msg->pushContent(HTML::small(fmt("saved as %s", $filename)),
175                               " ... ");
176         }
177
178         if ($request->getArg('include') == 'all')
179             $data = MailifyPage($page, 0);
180         else
181             $data = MailifyPage($page);
182
183         if ( !($fd = fopen("$directory/$filename", "w")) ) {
184             $msg->pushContent(HTML::strong(fmt("couldn't open file '%s' for writing",
185                                                "$directory/$filename")));
186             $request->finish($msg);
187         }
188
189         $num = fwrite($fd, $data, strlen($data));
190         $msg->pushContent(HTML::small(fmt("%s bytes written", $num)));
191         PrintXML($msg);
192
193         flush();
194         assert($num == strlen($data));
195         fclose($fd);
196     }
197
198     EndLoadDump($request);
199 }
200
201
202 function DumpHtmlToDir (&$request)
203 {
204     $directory = $request->getArg('directory');
205     if (empty($directory))
206         $request->finish(_("You must specify a directory to dump to"));
207
208     // see if we can access the directory the user wants us to use
209     if (! file_exists($directory)) {
210         if (! mkdir($directory, 0755))
211             $request->finish(fmt("Cannot create directory '%s'", $directory));
212         else
213             $html = HTML::p(fmt("Created directory '%s' for the page dump...",
214                                 $directory));
215     } else {
216         $html = HTML::p(fmt("Using directory '%s'", $directory));
217     }
218
219     StartLoadDump($request, _("Dumping Pages"), $html);
220
221     $dbi = $request->getDbh();
222     $pages = $dbi->getAllPages();
223
224     while ($page = $pages->next()) {
225
226         $filename = FilenameForPage($page->getName()) . ".html";
227
228         $msg = HTML(HTML::br(), $page->getName(), ' ... ');
229
230         if($page->getName() != $filename) {
231             $msg->pushContent(HTML::small(fmt("saved as %s", $filename)),
232                               " ... ");
233         }
234
235         $revision = $page->getCurrentRevision();
236
237         require_once('lib/PageType.php');
238         $transformedContent = PageType($revision);
239
240         require_once('lib/Template.php');
241         //Can't get the template to work...
242         //$transformedContent = array('CONTENT' => $transformedContent);
243         //$transformedContent = array_merge($request, array('CONTENT' => $transformedContent));
244         $template = $transformedContent; //this works but no template!!
245         //$template = Template('browse', $transformedContent);
246         $data = GeneratePageasXML($template, $page->getName());
247
248         if ( !($fd = fopen("$directory/$filename", "w")) ) {
249             $msg->pushContent(HTML::strong(fmt("couldn't open file '%s' for writing",
250                                                "$directory/$filename")));
251             $request->finish($msg);
252         }
253
254         $num = fwrite($fd, $data, strlen($data));
255         $msg->pushContent(HTML::small(fmt("%s bytes written", $num)));
256         PrintXML($msg);
257
258         flush();
259         assert($num == strlen($data));
260         fclose($fd);
261     }
262
263     EndLoadDump($request);
264 }
265
266
267 ////////////////////////////////////////////////////////////////
268 //
269 //  Functions for restoring.
270 //
271 ////////////////////////////////////////////////////////////////
272
273 function SavePage (&$request, $pageinfo, $source, $filename)
274 {
275     $pagedata    = $pageinfo['pagedata'];    // Page level meta-data.
276     $versiondata = $pageinfo['versiondata']; // Revision level meta-data.
277
278     if (empty($pageinfo['pagename'])) {
279         PrintXML(HTML::dt(HTML::strong(_("Empty pagename!"))));
280         return;
281     }
282
283     if (empty($versiondata['author_id']))
284         $versiondata['author_id'] = $versiondata['author'];
285
286     $pagename = $pageinfo['pagename'];
287     $content  = $pageinfo['content'];
288
289     if ($pagename ==_("InterWikiMap"))
290         $content = _tryinsertInterWikiMap($content);
291
292     $dbi = $request->getDbh();
293     $page = $dbi->getPage($pagename);
294
295     foreach ($pagedata as $key => $value) {
296         if (!empty($value))
297             $page->set($key, $value);
298     }
299
300     $mesg = HTML::dd();
301     $skip = false;
302     if ($source)
303         $mesg->pushContent(' ', fmt("from %s", $source));
304
305
306     $current = $page->getCurrentRevision();
307     if ($current->getVersion() == 0) {
308         $mesg->pushContent(' ', _("new page"));
309         $isnew = true;
310     }
311     else {
312         if ($current->getPackedContent() == $content
313             && $current->get('author') == $versiondata['author']) {
314             $mesg->pushContent(' ',
315                                fmt("is identical to current version %d - skipped",
316                                    $current->getVersion()));
317             $skip = true;
318         }
319         $isnew = false;
320     }
321
322     if (! $skip) {
323         $new = $page->createRevision(WIKIDB_FORCE_CREATE, $content,
324                                      $versiondata,
325                                      ExtractWikiPageLinks($content));
326
327         $mesg->pushContent(' ', fmt("- saved to database as version %d",
328                                     $new->getVersion()));
329     }
330
331     PrintXML(HTML::dt(WikiLink($pagename)), $mesg);
332     flush();
333 }
334
335 function _tryinsertInterWikiMap($content) {
336     $goback = false;
337     if (strpos($content, "<verbatim>")) {
338         //$error_html = " The newly loaded pgsrc already contains a verbatim block.";
339         $goback = true;
340     }
341     if (!$goback && !defined('INTERWIKI_MAP_FILE')) {
342         $error_html = sprintf(" "._("%s: not defined"), "INTERWIKI_MAP_FILE");
343         $goback = true;
344     }
345     if (!$goback && !file_exists(INTERWIKI_MAP_FILE)) {
346         $error_html = sprintf(" "._("%s: file not found"), INTERWIKI_MAP_FILE);
347         $goback = true;
348     }
349
350     if (!empty($error_html))
351         trigger_error(_("Default InterWiki map file not loaded.")
352                       . $error_html, E_USER_NOTICE);
353
354     if ($goback)
355         return $content;
356
357     $filename = INTERWIKI_MAP_FILE;
358     trigger_error(sprintf(_("Loading InterWikiMap from external file %s."),
359                           $filename), E_USER_NOTICE);
360
361     $fd = fopen ($filename, "rb");
362     $data = fread ($fd, filesize($filename));
363     fclose ($fd);
364     $content = $content . "\n<verbatim>\n$data</verbatim>\n";
365     return $content;
366 }
367
368 function ParseSerializedPage($text, $default_pagename, $user)
369 {
370     if (!preg_match('/^a:\d+:{[si]:\d+/', $text))
371         return false;
372     
373     $pagehash = unserialize($text);
374     
375     // Split up pagehash into four parts:
376     //   pagename
377     //   content
378     //   page-level meta-data
379     //   revision-level meta-data
380     
381     if (!defined('FLAG_PAGE_LOCKED'))
382         define('FLAG_PAGE_LOCKED', 1);
383     $pageinfo = array('pagedata'    => array(),
384                       'versiondata' => array());
385     
386     $pagedata = &$pageinfo['pagedata'];
387     $versiondata = &$pageinfo['versiondata'];
388     
389     // Fill in defaults.
390     if (empty($pagehash['pagename']))
391         $pagehash['pagename'] = $default_pagename;
392     if (empty($pagehash['author'])) {
393         $pagehash['author'] = $user->getId();
394     }
395     
396     foreach ($pagehash as $key => $value) {
397         switch($key) {
398             case 'pagename':
399             case 'version':
400                 $pageinfo[$key] = $value;
401                 break;
402             case 'content':
403                 $pageinfo[$key] = join("\n", $value);
404                 break;
405             case 'flags':
406                 if (($value & FLAG_PAGE_LOCKED) != 0)
407                     $pagedata['locked'] = 'yes';
408                 break;
409             case 'created':
410                 $pagedata[$key] = $value;
411                 break;
412             case 'lastmodified':
413                 $versiondata['mtime'] = $value;
414                 break;
415             case 'author':
416             case 'author_id':
417             case 'summary':
418             case 'hits':
419         }
420     }
421     return $pageinfo;
422 }
423
424 function SortByPageVersion ($a, $b) {
425     return $a['version'] - $b['version'];
426 }
427
428 function LoadFile (&$request, $filename, $text = false, $mtime = false)
429 {
430     if (!is_string($text)) {
431         // Read the file.
432         $stat  = stat($filename);
433         $mtime = $stat[9];
434         $text  = implode("", file($filename));
435     }
436
437     set_time_limit(30); // Reset watchdog.
438
439     // FIXME: basename("filewithnoslashes") seems to return garbage sometimes.
440     $basename = basename("/dummy/" . $filename);
441
442     if (!$mtime)
443         $mtime = time();        // Last resort.
444
445     $default_pagename = rawurldecode($basename);
446
447     if ( ($parts = ParseMimeifiedPages($text)) ) {
448         usort($parts, 'SortByPageVersion');
449         foreach ($parts as $pageinfo)
450             SavePage($request, $pageinfo, sprintf(_("MIME file %s"),
451                                                   $filename), $basename);
452     }
453     else if ( ($pageinfo = ParseSerializedPage($text, $default_pagename,
454                                                $request->getUser())) ) {
455         SavePage($request, $pageinfo, sprintf(_("Serialized file %s"),
456                                               $filename), $basename);
457     }
458     else {
459         $user = $request->getUser();
460
461         // Assume plain text file.
462         $pageinfo = array('pagename' => $default_pagename,
463                           'pagedata' => array(),
464                           'versiondata'
465                           => array('author' => $user->getId()),
466                           'content'  => preg_replace('/[ \t\r]*\n/', "\n",
467                                                      chop($text))
468                           );
469         SavePage($request, $pageinfo, sprintf(_("plain file %s"), $filename),
470                  $basename);
471     }
472 }
473
474 function LoadZip (&$request, $zipfile, $files = false, $exclude = false) {
475     $zip = new ZipReader($zipfile);
476     while (list ($fn, $data, $attrib) = $zip->readFile()) {
477         // FIXME: basename("filewithnoslashes") seems to return
478         // garbage sometimes.
479         $fn = basename("/dummy/" . $fn);
480         if ( ($files && !in_array($fn, $files)) || ($exclude && in_array($fn, $exclude)) ) {
481
482             PrintXML(HTML::dt(WikiLink($fn)),
483                      HTML::dd(_("Skipping")));
484             continue;
485         }
486
487         LoadFile($request, $fn, $data, $attrib['mtime']);
488     }
489 }
490
491 function LoadDir (&$request, $dirname, $files = false, $exclude = false) {
492     $fileset = new LimitedFileSet($dirname, $files, $exclude);
493
494     if (($skiplist = $fileset->getSkippedFiles())) {
495         PrintXML(HTML::dt(HTML::strong(_("Skipping"))));
496         $list = HTML::ul();
497         foreach ($skiplist as $file)
498             $list->pushContent(HTML::li(WikiLink($file)));
499         PrintXML(HTML::dd($list));
500     }
501
502     // Defer HomePage loading until the end. If anything goes wrong
503     // the pages can still be loaded again.
504     $files = $fileset->getFiles();
505     if ($home = array_search(HomePage, $files)) {
506         $files[$home]='';
507         array_push($files, HomePage);
508     }
509     foreach ($files as $file)
510         LoadFile($request, "$dirname/$file");
511 }
512
513 class LimitedFileSet extends FileSet {
514     function LimitedFileSet($dirname, $_include, $exclude) {
515         $this->_includefiles = $_include;
516         $this->_exclude = $exclude;
517         $this->_skiplist = array();
518         parent::FileSet($dirname);
519     }
520
521     function _filenameSelector($fn) {
522         $incl = &$this->_include;
523         $excl = &$this->_exclude;
524
525         if (($incl && !in_array($fn, $incl)) || ($excl && in_array($fn, $excl))) {
526             $this->_skiplist[] = $fn;
527             return false;
528         } else {
529             return true;
530         }
531     }
532
533     function getSkippedFiles () {
534         return $this->_skiplist;
535     }
536 }
537
538
539 function IsZipFile ($filename_or_fd)
540 {
541     // See if it looks like zip file
542     if (is_string($filename_or_fd))
543     {
544         $fd    = fopen($filename_or_fd, "rb");
545         $magic = fread($fd, 4);
546         fclose($fd);
547     }
548     else
549     {
550         $fpos  = ftell($filename_or_fd);
551         $magic = fread($filename_or_fd, 4);
552         fseek($filename_or_fd, $fpos);
553     }
554
555     return $magic == ZIP_LOCHEAD_MAGIC || $magic == ZIP_CENTHEAD_MAGIC;
556 }
557
558
559 function LoadAny (&$request, $file_or_dir, $files = false, $exclude = false)
560 {
561     // Try urlencoded filename for accented characters.
562     if (!file_exists($file_or_dir)) {
563         // Make sure there are slashes first to avoid confusing phps
564         // with broken dirname or basename functions.
565         // FIXME: windows uses \ and :
566         if (is_integer(strpos($file_or_dir, "/"))) {
567             $file_or_dir = dirname($file_or_dir) ."/".
568             urlencode(basename($file_or_dir));
569         } else {
570             // This is probably just a file.
571             $file_or_dir = urlencode($file_or_dir);
572         }
573     }
574
575     $type = filetype($file_or_dir);
576     if ($type == 'link') {
577         // For symbolic links, use stat() to determine
578         // the type of the underlying file.
579         list(,,$mode) = stat($file_or_dir);
580         $type = ($mode >> 12) & 017;
581         if ($type == 010)
582             $type = 'file';
583         elseif ($type == 004)
584             $type = 'dir';
585     }
586
587     if ($type == 'dir') {
588         LoadDir($request, $file_or_dir, $files, $exclude);
589     }
590     else if ($type != 'file' && !preg_match('/^(http|ftp):/', $file_or_dir))
591     {
592         $request->finish(fmt("Bad file type: %s", $type));
593     }
594     else if (IsZipFile($file_or_dir)) {
595         LoadZip($request, $file_or_dir, $files, $exclude);
596     }
597     else /* if (!$files || in_array(basename($file_or_dir), $files)) */
598     {
599         LoadFile($request, $file_or_dir);
600     }
601 }
602
603 function LoadFileOrDir (&$request)
604 {
605     $source = $request->getArg('source');
606     StartLoadDump($request, sprintf(_("Loading '%s'"), $source));
607     echo "<dl>\n";
608     LoadAny($request, $source);
609     echo "</dl>\n";
610     EndLoadDump($request);
611 }
612
613 function SetupWiki (&$request)
614 {
615     global $GenericPages, $LANG;
616
617
618     //FIXME: This is a hack (err, "interim solution")
619     // This is a bogo-bogo-login:  Login without
620     // saving login information in session state.
621     // This avoids logging in the unsuspecting
622     // visitor as "The PhpWiki programming team".
623     //
624     // This really needs to be cleaned up...
625     // (I'm working on it.)
626     $real_user = $request->_user;
627     $request->_user = new WikiUser(_("The PhpWiki programming team"),
628                                    WIKIAUTH_BOGO);
629
630     StartLoadDump($request, _("Loading up virgin wiki"));
631     echo "<dl>\n";
632
633     LoadAny($request, FindLocalizedFile(WIKI_PGSRC));
634     if ($LANG != "C")
635         LoadAny($request, FindFile(DEFAULT_WIKI_PGSRC),
636                 $GenericPages);
637
638     echo "</dl>\n";
639     EndLoadDump($request);
640 }
641
642 function LoadPostFile (&$request)
643 {
644     $upload = $request->getUploadedFile('file');
645
646     if (!$upload)
647         $request->finish(_("No uploaded file to upload?")); // FIXME: more concise message
648
649
650     // Dump http headers.
651     StartLoadDump($request, sprintf(_("Uploading %s"), $upload->getName()));
652     echo "<dl>\n";
653
654     $fd = $upload->open();
655     if (IsZipFile($fd))
656         LoadZip($request, $fd, false, array(_("RecentChanges")));
657     else
658         LoadFile($request, $upload->getName(), $upload->getContents());
659
660     echo "</dl>\n";
661     EndLoadDump($request);
662 }
663
664 // For emacs users
665 // Local Variables:
666 // mode: php
667 // tab-width: 8
668 // c-basic-offset: 4
669 // c-hanging-comment-ender-p: nil
670 // indent-tabs-mode: nil
671 // End:
672 ?>