1 <?php rcs_id('$Id: loadsave.php,v 1.43 2002-01-26 07:01:42 carstenklapp Exp $');
3 require_once("lib/ziplib.php");
4 require_once("lib/Template.php");
6 function StartLoadDump(&$request, $title, $html = '')
8 // FIXME: This is a hack
9 $tmpl = Template('top', array('TITLE' => $title, 'HEADER' => $title));
10 echo ereg_replace('</body>.*', '', $tmpl->getExpansion($html));
13 function EndLoadDump(&$request)
15 // FIXME: This is a hack
17 $pagelink = $Theme->LinkExistingWikiWord($request->getArg('pagename'));
19 PrintXML(array(HTML::p(HTML::strong(_("Complete."))),
20 HTML::p(fmt("Return to %s", $pagelink))));
21 echo "</body></html>\n";
25 ////////////////////////////////////////////////////////////////
27 // Functions for dumping.
29 ////////////////////////////////////////////////////////////////
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).
37 * Also see http://www.faqs.org/rfcs/rfc2822.html
39 function MailifyPage ($page, $nversions = 1)
41 $current = $page->getCurrentRevision();
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";
55 $head .= "Date: " . Rfc2822DateTime($current->get('mtime')) . "\r\n";
56 $head .= sprintf("Mime-Version: 1.0 (Produced by PhpWiki %s)\r\n",
59 // This should just be entered by hand (or by script?)
60 // in the actual pgsrc files, since only they should have
62 //$head .= "X-Rcs-Id: \"\$Id\$\"\r\n";
64 $iter = $page->getAllRevisions();
66 while ($revision = $iter->next()) {
67 $parts[] = MimeifyPageRevision($revision);
68 if ($nversions > 0 && count($parts) >= $nversions)
71 if (count($parts) > 1)
72 return $head . MimeMultipart($parts);
74 return $head . $parts[0];
78 * Compute filename to used for storing contents of a wiki page.
80 * Basically we do a rawurlencode() which encodes everything except
81 * ASCII alphanumerics and '.', '-', and '_'.
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'.)
87 * @param $pagename string Pagename.
88 * @return string Filename for page.
90 function FilenameForPage ($pagename)
92 $enc = rawurlencode($pagename);
93 return preg_replace('/^\./', '%2e', $enc);
97 * The main() function which generates a zip archive of a PhpWiki.
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
103 function MakeWikiZip (&$request)
105 if ($request->getArg('include') == 'all') {
106 $zipname = "wikidb.zip";
107 $include_archive = true;
110 $zipname = "wiki.zip";
111 $include_archive = false;
116 $zip = new ZipWriter("Created by PhpWiki", $zipname);
118 $dbi = $request->getDbh();
119 $pages = $dbi->getAllPages();
120 while ($page = $pages->next()) {
121 set_time_limit(30); // Reset watchdog.
123 $current = $page->getCurrentRevision();
124 if ($current->getVersion() == 0)
128 $attrib = array('mtime' => $current->get('mtime'),
130 if ($page->get('locked'))
131 $attrib['write_protected'] = 1;
133 if ($include_archive)
134 $content = MailifyPage($page, 0);
136 $content = MailifyPage($page);
138 $zip->addRegularFile( FilenameForPage($page->getName()),
144 function DumpToDir (&$request)
146 $directory = $request->getArg('directory');
147 if (empty($directory))
148 $request->finish(_("You must specify a directory to dump to"));
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));
155 $html = HTML::p(fmt("Created directory '%s' for the page dump...",
158 $html = HTML::p(fmt("Using directory '%s'", $directory));
161 StartLoadDump($request, _("Dumping Pages"), $html);
163 $dbi = $request->getDbh();
164 $pages = $dbi->getAllPages();
166 while ($page = $pages->next()) {
168 $filename = FilenameForPage($page->getName());
170 $msg = array(HTML::br(), $page->getName(), ' ... ');
172 if($page->getName() != $filename) {
173 $msg[] = HTML::small(fmt("saved as %s", $filename));
177 $data = MailifyPage($page);
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);
185 $num = fwrite($fd, $data, strlen($data));
186 $msg[] = HTML::small(fmt("%s bytes written", $num));
190 assert($num == strlen($data));
194 EndLoadDump($request);
197 ////////////////////////////////////////////////////////////////
199 // Functions for restoring.
201 ////////////////////////////////////////////////////////////////
203 function SavePage (&$request, $pageinfo, $source, $filename)
205 $pagedata = $pageinfo['pagedata']; // Page level meta-data.
206 $versiondata = $pageinfo['versiondata']; // Revision level meta-data.
208 if (empty($pageinfo['pagename'])) {
209 PrintXML(HTML::dt(HTML::strong(_("Empty pagename!"))));
213 if (empty($versiondata['author_id']))
214 $versiondata['author_id'] = $versiondata['author'];
216 $pagename = $pageinfo['pagename'];
217 $content = $pageinfo['content'];
219 $dbi = $request->getDbh();
220 $page = $dbi->getPage($pagename);
222 foreach ($pagedata as $key => $value) {
224 $page->set($key, $value);
230 $mesg->pushContent(' ', fmt("from %s", $source));
233 $current = $page->getCurrentRevision();
234 if ($current->getVersion() == 0) {
235 $mesg->pushContent(' ', _("new page"));
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()));
250 $new = $page->createRevision(WIKIDB_FORCE_CREATE, $content,
252 ExtractWikiPageLinks($content));
254 $mesg->pushContent(' ', fmt("- saved to database as version %d",
255 $new->getVersion()));
259 $pagelink = $Theme->LinkExistingWikiWord($pagename);
261 PrintXML(array(HTML::dt($pagelink), $mesg));
265 function ParseSerializedPage($text, $default_pagename, $user)
267 if (!preg_match('/^a:\d+:{[si]:\d+/', $text))
270 $pagehash = unserialize($text);
272 // Split up pagehash into four parts:
275 // page-level meta-data
276 // revision-level meta-data
278 if (!defined('FLAG_PAGE_LOCKED'))
279 define('FLAG_PAGE_LOCKED', 1);
280 $pageinfo = array('pagedata' => array(),
281 'versiondata' => array());
283 $pagedata = &$pageinfo['pagedata'];
284 $versiondata = &$pageinfo['versiondata'];
287 if (empty($pagehash['pagename']))
288 $pagehash['pagename'] = $default_pagename;
289 if (empty($pagehash['author'])) {
290 $pagehash['author'] = $user->getId();
293 foreach ($pagehash as $key => $value) {
297 $pageinfo[$key] = $value;
300 $pageinfo[$key] = join("\n", $value);
302 if (($value & FLAG_PAGE_LOCKED) != 0)
303 $pagedata['locked'] = 'yes';
306 $pagedata[$key] = $value;
309 $versiondata['mtime'] = $value;
312 $versiondata[$key] = $value;
319 function SortByPageVersion ($a, $b) {
320 return $a['version'] - $b['version'];
323 function LoadFile (&$request, $filename, $text = false, $mtime = false)
325 if (!is_string($text)) {
327 $stat = stat($filename);
329 $text = implode("", file($filename));
332 set_time_limit(30); // Reset watchdog.
334 // FIXME: basename("filewithnoslashes") seems to return garbage sometimes.
335 $basename = basename("/dummy/" . $filename);
338 $mtime = time(); // Last resort.
340 $default_pagename = rawurldecode($basename);
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);
348 else if ( ($pageinfo = ParseSerializedPage($text, $default_pagename,
349 $request->getUser())) ) {
350 SavePage($request, $pageinfo, sprintf(_("Serialized file %s"),
351 $filename), $basename);
355 $user = $request->getUser();
357 // Assume plain text file.
358 $pageinfo = array('pagename' => $default_pagename,
359 'pagedata' => array(),
361 => array('author' => $user->getId()),
362 'content' => preg_replace('/[ \t\r]*\n/', "\n",
365 SavePage($request, $pageinfo, sprintf(_("plain file %s"), $filename),
370 function LoadZip (&$request, $zipfile, $files = false, $exclude = false) {
371 $zip = new ZipReader($zipfile);
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)) ) {
379 PrintXML(array(HTML::dt($Theme->LinkExistingWikiWord($fn)),
380 HTML::dd(_("Skipping"))));
385 LoadFile($request, $fn, $data, $attrib['mtime']);
389 function LoadDir (&$request, $dirname, $files = false, $exclude = false) {
391 $loadfileset = new loadFileSet($dirname, $files, $exclude, $skiplist);
394 // FIXME: use PageList?
395 PrintXML(HTML::h2(_("Skipping")));
398 foreach ($skiplist as $line)
399 $list->pushContent(HTML::li($Theme->LinkExistingWikiWord($line)));
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");
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);
418 function _filenameSelector($filename) {
419 if (($this->_files && !in_array($fn, $this->_files)) || ($this->_exclude && in_array($fn, $this->_exclude)))
422 $this->_skiplist[] = $filename;
431 function IsZipFile ($filename_or_fd)
433 // See if it looks like zip file
434 if (is_string($filename_or_fd))
436 $fd = fopen($filename_or_fd, "rb");
437 $magic = fread($fd, 4);
442 $fpos = ftell($filename_or_fd);
443 $magic = fread($filename_or_fd, 4);
444 fseek($filename_or_fd, $fpos);
447 return $magic == ZIP_LOCHEAD_MAGIC || $magic == ZIP_CENTHEAD_MAGIC;
451 function LoadAny (&$request, $file_or_dir, $files = false, $exclude = false)
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));
462 // This is probably just a file.
463 $file_or_dir = urlencode($file_or_dir);
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;
475 elseif ($type == 004)
479 if ($type == 'dir') {
480 LoadDir($request, $file_or_dir, $files, $exclude);
482 else if ($type != 'file' && !preg_match('/^(http|ftp):/', $file_or_dir))
484 $request->finish(fmt("Bad file type: %s", $type));
486 else if (IsZipFile($file_or_dir)) {
487 LoadZip($request, $file_or_dir, $files, $exclude);
489 else /* if (!$files || in_array(basename($file_or_dir), $files)) */
491 LoadFile($request, $file_or_dir);
495 function LoadFileOrDir (&$request)
497 $source = $request->getArg('source');
498 StartLoadDump($request, sprintf(_("Loading '%s'"), $source));
500 LoadAny($request, $source/*, false, array(_("RecentChanges"))*/);
502 EndLoadDump($request);
505 function SetupWiki (&$request)
507 global $GenericPages, $LANG;
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".
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"),
522 StartLoadDump($request, _("Loading up virgin wiki"));
525 LoadAny($request, FindLocalizedFile(WIKI_PGSRC)/*, false, $ignore*/);
527 LoadAny($request, FindFile(DEFAULT_WIKI_PGSRC),
528 $GenericPages/*, $ignore*/);
531 EndLoadDump($request);
534 function LoadPostFile (&$request)
536 $upload = $request->getUploadedFile('file');
539 $request->finish(_("No uploaded file to upload?")); // FIXME: more concise message
542 // Dump http headers.
543 StartLoadDump($request, sprintf(_("Uploading %s"), $upload->getName()));
546 $fd = $upload->open();
548 LoadZip($request, $fd, false, array(_("RecentChanges")));
550 LoadFile($request, $upload->getName(), $upload->getContents());
553 EndLoadDump($request);
561 // c-hanging-comment-ender-p: nil
562 // indent-tabs-mode: nil