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