]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/loadsave.php
New constant in config.php 'HomePage' as _("HomePage"), to make it easier to rename...
[SourceForge/phpwiki.git] / lib / loadsave.php
1 <?php rcs_id('$Id: loadsave.php,v 1.47 2002-01-27 22:00:58 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     global $Theme;
19     $pagelink = $Theme->LinkExistingWikiWord($request->getArg('pagename'));
20     
21     PrintXML(array(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 = array(HTML::br(), $page->getName(), ' ... ');
173         
174         if($page->getName() != $filename) {
175             $msg[] = HTML::small(fmt("saved as %s", $filename));
176             $msg[] = " ... ";
177         }
178         
179         $data = MailifyPage($page);
180         
181         if ( !($fd = fopen("$directory/$filename", "w")) ) {
182             $msg[] = HTML::strong(fmt("couldn't open file '%s' for writing",
183                                       "$directory/$filename"));
184             $request->finish($msg);
185         }
186         
187         $num = fwrite($fd, $data, strlen($data));
188         $msg[] = HTML::small(fmt("%s bytes written", $num));
189         PrintXML($msg);
190         
191         flush();
192         assert($num == strlen($data));
193         fclose($fd);
194     }
195     
196     EndLoadDump($request);
197 }
198
199 ////////////////////////////////////////////////////////////////
200 //
201 //  Functions for restoring.
202 //
203 ////////////////////////////////////////////////////////////////
204
205 function SavePage (&$request, $pageinfo, $source, $filename)
206 {
207     $pagedata    = $pageinfo['pagedata'];    // Page level meta-data.
208     $versiondata = $pageinfo['versiondata']; // Revision level meta-data.
209     
210     if (empty($pageinfo['pagename'])) {
211         PrintXML(HTML::dt(HTML::strong(_("Empty pagename!"))));
212         return;
213     }
214     
215     if (empty($versiondata['author_id']))
216         $versiondata['author_id'] = $versiondata['author'];
217     
218     $pagename = $pageinfo['pagename'];
219     $content  = $pageinfo['content'];
220
221     $dbi = $request->getDbh();
222     $page = $dbi->getPage($pagename);
223     
224     foreach ($pagedata as $key => $value) {
225         if (!empty($value))
226             $page->set($key, $value);
227     }
228     
229     $mesg = HTML::dd();
230     $skip = false;
231     if ($source)
232         $mesg->pushContent(' ', fmt("from %s", $source));
233     
234
235     $current = $page->getCurrentRevision();
236     if ($current->getVersion() == 0) {
237         $mesg->pushContent(' ', _("new page"));
238         $isnew = true;
239     }
240     else {
241         if ($current->getPackedContent() == $content
242             && $current->get('author') == $versiondata['author']) {
243             $mesg->pushContent(' ',
244                                fmt("is identical to current version %d - skipped",
245                                    $current->getVersion()));
246             $skip = true;
247         }
248         $isnew = false;
249     }
250     
251     if (! $skip) {
252         $new = $page->createRevision(WIKIDB_FORCE_CREATE, $content,
253                                      $versiondata,
254                                      ExtractWikiPageLinks($content));
255         
256         $mesg->pushContent(' ', fmt("- saved to database as version %d",
257                                     $new->getVersion()));
258     }
259
260     global $Theme;
261     $pagelink = $Theme->LinkExistingWikiWord($pagename);
262     
263     PrintXML(array(HTML::dt($pagelink), $mesg));
264     flush();
265 }
266
267 function ParseSerializedPage($text, $default_pagename, $user)
268 {
269     if (!preg_match('/^a:\d+:{[si]:\d+/', $text))
270         return false;
271     
272     $pagehash = unserialize($text);
273     
274     // Split up pagehash into four parts:
275     //   pagename
276     //   content
277     //   page-level meta-data
278     //   revision-level meta-data
279     
280     if (!defined('FLAG_PAGE_LOCKED'))
281         define('FLAG_PAGE_LOCKED', 1);
282     $pageinfo = array('pagedata'    => array(),
283                       'versiondata' => array());
284     
285     $pagedata = &$pageinfo['pagedata'];
286     $versiondata = &$pageinfo['versiondata'];
287     
288     // Fill in defaults.
289     if (empty($pagehash['pagename']))
290         $pagehash['pagename'] = $default_pagename;
291     if (empty($pagehash['author'])) {
292         $pagehash['author'] = $user->getId();
293     }
294     
295     foreach ($pagehash as $key => $value) {
296         switch($key) {
297         case 'pagename':
298         case 'version':
299             $pageinfo[$key] = $value;
300             break;
301         case 'content':
302             $pageinfo[$key] = join("\n", $value);
303         case 'flags':
304             if (($value & FLAG_PAGE_LOCKED) != 0)
305                 $pagedata['locked'] = 'yes';
306             break;
307         case 'created':
308             $pagedata[$key] = $value;
309             break;
310         case 'lastmodified':
311             $versiondata['mtime'] = $value;
312             break;
313         case 'author':
314             $versiondata[$key] = $value;
315             break;
316         }
317     }
318     return $pageinfo;
319 }
320  
321 function SortByPageVersion ($a, $b) {
322     return $a['version'] - $b['version'];
323 }
324
325 function LoadFile (&$request, $filename, $text = false, $mtime = false)
326 {
327     if (!is_string($text)) {
328         // Read the file.
329         $stat  = stat($filename);
330         $mtime = $stat[9];
331         $text  = implode("", file($filename));
332     }
333    
334     set_time_limit(30); // Reset watchdog.
335     
336     // FIXME: basename("filewithnoslashes") seems to return garbage sometimes.
337     $basename = basename("/dummy/" . $filename);
338    
339     if (!$mtime)
340         $mtime = time();        // Last resort.
341     
342     $default_pagename = rawurldecode($basename);
343     
344     if ( ($parts = ParseMimeifiedPages($text)) ) {
345         usort($parts, 'SortByPageVersion');
346         foreach ($parts as $pageinfo)
347             SavePage($request, $pageinfo, sprintf(_("MIME file %s"), 
348                                                   $filename), $basename);
349     }
350     else if ( ($pageinfo = ParseSerializedPage($text, $default_pagename,
351                                                $request->getUser())) ) {
352         SavePage($request, $pageinfo, sprintf(_("Serialized file %s"), 
353                                               $filename), $basename);
354     }
355     else {
356         $user = $request->getUser();
357         
358         // Assume plain text file.
359         $pageinfo = array('pagename' => $default_pagename,
360                           'pagedata' => array(),
361                           'versiondata'
362                           => array('author' => $user->getId()),
363                           'content'  => preg_replace('/[ \t\r]*\n/', "\n",
364                                                      chop($text))
365                           );
366         SavePage($request, $pageinfo, sprintf(_("plain file %s"), $filename),
367                  $basename);
368     }
369 }
370
371 function LoadZip (&$request, $zipfile, $files = false, $exclude = false) {
372     $zip = new ZipReader($zipfile);
373     global $Theme;
374     while (list ($fn, $data, $attrib) = $zip->readFile()) {
375         // FIXME: basename("filewithnoslashes") seems to return
376         // garbage sometimes.
377         $fn = basename("/dummy/" . $fn);
378         if ( ($files && !in_array($fn, $files)) || ($exclude && in_array($fn, $exclude)) ) {
379
380             PrintXML(array(HTML::dt($Theme->LinkExistingWikiWord($fn)),
381                            HTML::dd(_("Skipping"))));
382            
383             continue;
384         }
385        
386         LoadFile($request, $fn, $data, $attrib['mtime']);
387     }
388 }
389
390 function LoadDir (&$request, $dirname, $files = false, $exclude = false) {
391     $fileset = new LimitedFileSet($dirname, $files, $exclude);
392
393     if (($skiplist = $fileset->getSkippedFiles())) {
394         PrintXML(HTML::dt(HTML::strong(_("Skipping"))));
395         $list = HTML::ul();
396         global $Theme;
397         foreach ($skiplist as $file)
398             $list->pushContent(HTML::li($Theme->LinkExistingWikiWord($file)));
399         PrintXML(HTML::dd($list));
400     }
401
402     // Defer HomePage loading until the end. If anything goes wrong
403     // the pages can still be loaded again.
404     $files = $fileset->getFiles();
405     if ($home = array_search(HomePage, $files)) {
406         $files[$home]='';
407         array_push($files, HomePage);
408     }
409     foreach ($files as $file)
410         LoadFile($request, "$dirname/$file");
411 }
412
413 class LimitedFileSet extends FileSet {
414     function LimitedFileSet($dirname, $_include, $exclude) {
415         $this->_includefiles = $_include;
416         $this->_exclude = $exclude;
417         $this->_skiplist = array();
418         parent::FileSet($dirname);
419     }
420
421     function _filenameSelector($fn) {
422         $incl = &$this->_include;
423         $excl = &$this->_exclude;
424
425         if (($incl && !in_array($fn, $incl)) || ($excl && in_array($fn, $excl))) {
426             $this->_skiplist[] = $fn;
427             return false;
428         } else {
429             return true;
430         }
431     }
432
433     function getSkippedFiles () {
434         return $this->_skiplist;
435     }
436 }
437
438
439 function IsZipFile ($filename_or_fd)
440 {
441     // See if it looks like zip file
442     if (is_string($filename_or_fd))
443         {
444             $fd    = fopen($filename_or_fd, "rb");
445             $magic = fread($fd, 4);
446             fclose($fd);
447         }
448     else
449         {
450             $fpos  = ftell($filename_or_fd);
451             $magic = fread($filename_or_fd, 4);
452             fseek($filename_or_fd, $fpos);
453         }
454     
455     return $magic == ZIP_LOCHEAD_MAGIC || $magic == ZIP_CENTHEAD_MAGIC;
456 }
457
458
459 function LoadAny (&$request, $file_or_dir, $files = false, $exclude = false)
460 {
461     // Try urlencoded filename for accented characters.
462     if (!file_exists($file_or_dir)) {
463         // Make sure there are slashes first to avoid confusing phps
464         // with broken dirname or basename functions.
465         // FIXME: windows uses \ and :
466         if (is_integer(strpos($file_or_dir, "/"))) {
467             $file_or_dir = dirname($file_or_dir) ."/".
468                            urlencode(basename($file_or_dir));
469         } else {
470             // This is probably just a file.
471             $file_or_dir = urlencode($file_or_dir);
472         }
473     }
474
475     $type = filetype($file_or_dir);
476     if ($type == 'link') {
477         // For symbolic links, use stat() to determine
478         // the type of the underlying file.
479         list(,,$mode) = stat($file_or_dir);
480         $type = ($mode >> 12) & 017;
481         if ($type == 010)
482             $type = 'file';
483         elseif ($type == 004)
484             $type = 'dir';
485     }
486     
487     if ($type == 'dir') {
488         LoadDir($request, $file_or_dir, $files, $exclude);
489     }
490     else if ($type != 'file' && !preg_match('/^(http|ftp):/', $file_or_dir))
491     {
492         $request->finish(fmt("Bad file type: %s", $type));
493     }
494     else if (IsZipFile($file_or_dir)) {
495         LoadZip($request, $file_or_dir, $files, $exclude);
496     }
497     else /* if (!$files || in_array(basename($file_or_dir), $files)) */
498     {
499         LoadFile($request, $file_or_dir);
500     }
501 }
502
503 function LoadFileOrDir (&$request)
504 {
505     $source = $request->getArg('source');
506     StartLoadDump($request, sprintf(_("Loading '%s'"), $source));
507     echo "<dl>\n";
508     LoadAny($request, $source);
509     echo "</dl>\n";
510     EndLoadDump($request);
511 }
512
513 function SetupWiki (&$request)
514 {
515     global $GenericPages, $LANG;
516     
517     
518     //FIXME: This is a hack (err, "interim solution")
519     // This is a bogo-bogo-login:  Login without
520     // saving login information in session state.
521     // This avoids logging in the unsuspecting
522     // visitor as "The PhpWiki programming team".
523     //
524     // This really needs to be cleaned up...
525     // (I'm working on it.)
526     $real_user = $request->_user;
527     $request->_user = new WikiUser(_("The PhpWiki programming team"),
528                                    WIKIAUTH_BOGO);
529     
530     StartLoadDump($request, _("Loading up virgin wiki"));
531     echo "<dl>\n";
532     
533     LoadAny($request, FindLocalizedFile(WIKI_PGSRC));
534     if ($LANG != "C")
535         LoadAny($request, FindFile(DEFAULT_WIKI_PGSRC),
536                 $GenericPages);
537     
538     echo "</dl>\n";
539     EndLoadDump($request);
540 }
541
542 function LoadPostFile (&$request)
543 {
544     $upload = $request->getUploadedFile('file');
545     
546     if (!$upload)
547         $request->finish(_("No uploaded file to upload?")); // FIXME: more concise message
548
549     
550     // Dump http headers.
551     StartLoadDump($request, sprintf(_("Uploading %s"), $upload->getName()));
552     echo "<dl>\n";
553     
554     $fd = $upload->open();
555     if (IsZipFile($fd))
556         LoadZip($request, $fd, false, array(_("RecentChanges")));
557     else
558         LoadFile($request, $upload->getName(), $upload->getContents());
559     
560     echo "</dl>\n";
561     EndLoadDump($request);
562 }
563
564 // For emacs users
565 // Local Variables:
566 // mode: php
567 // tab-width: 8
568 // c-basic-offset: 4
569 // c-hanging-comment-ender-p: nil
570 // indent-tabs-mode: nil
571 // End:   
572 ?>