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