]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/loadsave.php
remove 1 extra line leftover from debugging
[SourceForge/phpwiki.git] / lib / loadsave.php
1 <?php rcs_id('$Id: loadsave.php,v 1.44 2002-01-26 07:03:31 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             $this->_skiplist[] = $filename;
422             return false;
423         } else {
424             return true;
425         }
426     }
427 }
428
429
430 function IsZipFile ($filename_or_fd)
431 {
432     // See if it looks like zip file
433     if (is_string($filename_or_fd))
434         {
435             $fd    = fopen($filename_or_fd, "rb");
436             $magic = fread($fd, 4);
437             fclose($fd);
438         }
439     else
440         {
441             $fpos  = ftell($filename_or_fd);
442             $magic = fread($filename_or_fd, 4);
443             fseek($filename_or_fd, $fpos);
444         }
445     
446     return $magic == ZIP_LOCHEAD_MAGIC || $magic == ZIP_CENTHEAD_MAGIC;
447 }
448
449
450 function LoadAny (&$request, $file_or_dir, $files = false, $exclude = false)
451 {
452     // Try urlencoded filename for accented characters.
453     if (!file_exists($file_or_dir)) {
454         // Make sure there are slashes first to avoid confusing phps
455         // with broken dirname or basename functions.
456         // FIXME: windows uses \ and :
457         if (is_integer(strpos($file_or_dir, "/"))) {
458             $file_or_dir = dirname($file_or_dir) ."/".
459                            urlencode(basename($file_or_dir));
460         } else {
461             // This is probably just a file.
462             $file_or_dir = urlencode($file_or_dir);
463         }
464     }
465
466     $type = filetype($file_or_dir);
467     if ($type == 'link') {
468         // For symbolic links, use stat() to determine
469         // the type of the underlying file.
470         list(,,$mode) = stat($file_or_dir);
471         $type = ($mode >> 12) & 017;
472         if ($type == 010)
473             $type = 'file';
474         elseif ($type == 004)
475             $type = 'dir';
476     }
477     
478     if ($type == 'dir') {
479         LoadDir($request, $file_or_dir, $files, $exclude);
480     }
481     else if ($type != 'file' && !preg_match('/^(http|ftp):/', $file_or_dir))
482     {
483         $request->finish(fmt("Bad file type: %s", $type));
484     }
485     else if (IsZipFile($file_or_dir)) {
486         LoadZip($request, $file_or_dir, $files, $exclude);
487     }
488     else /* if (!$files || in_array(basename($file_or_dir), $files)) */
489     {
490         LoadFile($request, $file_or_dir);
491     }
492 }
493
494 function LoadFileOrDir (&$request)
495 {
496     $source = $request->getArg('source');
497     StartLoadDump($request, sprintf(_("Loading '%s'"), $source));
498     echo "<dl>\n";
499     LoadAny($request, $source/*, false, array(_("RecentChanges"))*/);
500     echo "</dl>\n";
501     EndLoadDump($request);
502 }
503
504 function SetupWiki (&$request)
505 {
506     global $GenericPages, $LANG;
507     
508     
509     //FIXME: This is a hack (err, "interim solution")
510     // This is a bogo-bogo-login:  Login without
511     // saving login information in session state.
512     // This avoids logging in the unsuspecting
513     // visitor as "The PhpWiki programming team".
514     //
515     // This really needs to be cleaned up...
516     // (I'm working on it.)
517     $real_user = $request->_user;
518     $request->_user = new WikiUser(_("The PhpWiki programming team"),
519                                    WIKIAUTH_BOGO);
520     
521     StartLoadDump($request, _("Loading up virgin wiki"));
522     echo "<dl>\n";
523     
524     LoadAny($request, FindLocalizedFile(WIKI_PGSRC)/*, false, $ignore*/);
525     if ($LANG != "C")
526         LoadAny($request, FindFile(DEFAULT_WIKI_PGSRC),
527                 $GenericPages/*, $ignore*/);
528     
529     echo "</dl>\n";
530     EndLoadDump($request);
531 }
532
533 function LoadPostFile (&$request)
534 {
535     $upload = $request->getUploadedFile('file');
536     
537     if (!$upload)
538         $request->finish(_("No uploaded file to upload?")); // FIXME: more concise message
539
540     
541     // Dump http headers.
542     StartLoadDump($request, sprintf(_("Uploading %s"), $upload->getName()));
543     echo "<dl>\n";
544     
545     $fd = $upload->open();
546     if (IsZipFile($fd))
547         LoadZip($request, $fd, false, array(_("RecentChanges")));
548     else
549         LoadFile($request, $upload->getName(), $upload->getContents());
550     
551     echo "</dl>\n";
552     EndLoadDump($request);
553 }
554
555 // For emacs users
556 // Local Variables:
557 // mode: php
558 // tab-width: 8
559 // c-basic-offset: 4
560 // c-hanging-comment-ender-p: nil
561 // indent-tabs-mode: nil
562 // End:   
563 ?>