2 rcs_id('$Id: loadsave.php,v 1.117 2004-07-02 09:55:58 rurban Exp $');
5 Copyright 1999, 2000, 2001, 2002 $ThePhpWikiProgrammingTeam
7 This file is part of PhpWiki.
9 PhpWiki is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 PhpWiki is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with PhpWiki; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 require_once("lib/ziplib.php");
26 require_once("lib/Template.php");
29 * ignore fatal errors during dump
31 function _dump_error_handler(&$error) {
32 if ($error->isFatal()) {
33 $error->errno = E_USER_WARNING;
36 return true; // Ignore error
38 if (preg_match('/Plugin/', $error->errstr))
41 // let the message come through: call the remaining handlers:
45 function StartLoadDump(&$request, $title, $html = '')
47 // FIXME: This is a hack
49 $html->pushContent('%BODY%');
50 $tmpl = Template('html', array('TITLE' => $title,
52 'CONTENT' => $html ? $html : '%BODY%'));
53 echo ereg_replace('%BODY%.*', '', $tmpl->getExpansion($html));
55 /* Ignore fatals or warnings in any pagedumps (failing plugins).
56 * WikiFunctionCb() fails with 4.0.6, works ok with 4.1.1
58 if (!check_php_version(4,1) or (DEBUG & _DEBUG_VERBOSE)) return;
60 $ErrorManager->pushErrorHandler(new WikiFunctionCb('_dump_error_handler'));
63 function EndLoadDump(&$request)
65 if (check_php_version(4,1)) {
67 $ErrorManager->popErrorHandler();
69 $action = $request->getArg('action');
72 case 'zip': $label = _("ZIP files of database"); break;
73 case 'dumpserial': $label = _("Dump to directory"); break;
74 case 'upload': $label = _("Upload File"); break;
75 case 'loadfile': $label = _("Load File"); break;
76 case 'upgrade': $label = _("Upgrade"); break;
78 case 'ziphtml': $label = _("Dump pages as XHTML"); break;
80 if ($label) $label = str_replace(" ","_",$label);
81 if ($action == 'browse') // loading virgin
82 $pagelink = WikiLink(HOME_PAGE);
84 $pagelink = WikiLink(new WikiPageName(_("PhpWikiAdministration"),false,$label));
86 PrintXML(HTML::p(HTML::strong(_("Complete."))),
87 HTML::p(fmt("Return to %s", $pagelink)));
88 echo "</body></html>\n";
92 ////////////////////////////////////////////////////////////////
94 // Functions for dumping.
96 ////////////////////////////////////////////////////////////////
100 * http://www.nacs.uci.edu/indiv/ehood/MIME/2045/rfc2045.html
101 * http://www.faqs.org/rfcs/rfc2045.html
102 * (RFC 1521 has been superceeded by RFC 2045 & others).
104 * Also see http://www.faqs.org/rfcs/rfc2822.html
106 function MailifyPage ($page, $nversions = 1)
108 $current = $page->getCurrentRevision();
111 if (STRICT_MAILABLE_PAGEDUMPS) {
112 $from = defined('SERVER_ADMIN') ? SERVER_ADMIN : 'foo@bar';
113 //This is for unix mailbox format: (not RFC (2)822)
114 // $head .= "From $from " . CTime(time()) . "\r\n";
115 $head .= "Subject: " . rawurlencode($page->getName()) . "\r\n";
116 $head .= "From: $from (PhpWiki)\r\n";
117 // RFC 2822 requires only a Date: and originator (From:)
118 // field, however the obsolete standard RFC 822 also
119 // requires a destination field.
120 $head .= "To: $from (PhpWiki)\r\n";
122 $head .= "Date: " . Rfc2822DateTime($current->get('mtime')) . "\r\n";
123 $head .= sprintf("Mime-Version: 1.0 (Produced by PhpWiki %s)\r\n",
126 // This should just be entered by hand (or by script?)
127 // in the actual pgsrc files, since only they should have
129 //$head .= "X-Rcs-Id: \$Id\$\r\n";
131 $iter = $page->getAllRevisions();
133 while ($revision = $iter->next()) {
134 $parts[] = MimeifyPageRevision($revision);
135 if ($nversions > 0 && count($parts) >= $nversions)
138 if (count($parts) > 1)
139 return $head . MimeMultipart($parts);
141 return $head . $parts[0];
145 * Compute filename to used for storing contents of a wiki page.
147 * Basically we do a rawurlencode() which encodes everything except
148 * ASCII alphanumerics and '.', '-', and '_'.
150 * But we also want to encode leading dots to avoid filenames like
151 * '.', and '..'. (Also, there's no point in generating "hidden" file
152 * names, like '.foo'.)
154 * @param $pagename string Pagename.
155 * @return string Filename for page.
157 function FilenameForPage ($pagename)
159 $enc = rawurlencode($pagename);
160 return preg_replace('/^\./', '%2e', $enc);
164 * The main() function which generates a zip archive of a PhpWiki.
166 * If $include_archive is false, only the current version of each page
167 * is included in the zip file; otherwise all archived versions are
170 function MakeWikiZip (&$request)
172 if ($request->getArg('include') == 'all') {
173 $zipname = WIKI_NAME . _("FullDump") . date('Ymd-Hi') . '.zip';
174 $include_archive = true;
177 $zipname = WIKI_NAME . _("LatestSnapshot") . date('Ymd-Hi') . '.zip';
178 $include_archive = false;
182 $zip = new ZipWriter("Created by PhpWiki " . PHPWIKI_VERSION, $zipname);
184 /* ignore fatals in plugins */
185 if (check_php_version(4,1)) {
186 global $ErrorManager;
187 $ErrorManager->pushErrorHandler(new WikiFunctionCb('_dump_error_handler'));
190 $dbi = $request->getDbh();
191 $thispage = $request->getArg('pagename'); // for "Return to ..."
192 if ($exclude = $request->getArg('exclude')) { // exclude which pagenames
193 $excludeList = explodePageList($exclude);
195 $excludeList = array();
197 if ($whichpages = $request->getArg('pages')) { // which pagenames
198 if ($whichpages == '[]') // current page
199 $whichpages = $thispage;
200 $pages = new WikiDB_Array_PageIterator(explodePageList($whichpages));
202 $pages = $dbi->getAllPages();
204 $request_args = $request->args;
206 while ($page = $pages->next()) {
207 $request->args = $request_args; // some plugins might change them (esp. on POST)
208 if (! $request->getArg('start_debug'))
209 @set_time_limit(30); // Reset watchdog
211 $current = $page->getCurrentRevision();
212 if ($current->getVersion() == 0)
215 $pagename = $page->getName();
216 $wpn = new WikiPageName($pagename);
217 if (!$wpn->isValid())
219 if (in_array($page->getName(), $excludeList)) {
223 $attrib = array('mtime' => $current->get('mtime'),
225 if ($page->get('locked'))
226 $attrib['write_protected'] = 1;
228 if ($include_archive)
229 $content = MailifyPage($page, 0);
231 $content = MailifyPage($page);
233 $zip->addRegularFile( FilenameForPage($pagename),
237 if (check_php_version(4,1)) {
238 $ErrorManager->popErrorHandler();
242 function DumpToDir (&$request)
244 $directory = $request->getArg('directory');
245 if (empty($directory))
246 $directory = DEFAULT_DUMP_DIR; // See lib/plugin/WikiForm.php:87
247 if (empty($directory))
248 $request->finish(_("You must specify a directory to dump to"));
250 // see if we can access the directory the user wants us to use
251 if (! file_exists($directory)) {
252 if (! mkdir($directory, 0755))
253 $request->finish(fmt("Cannot create directory '%s'", $directory));
255 $html = HTML::p(fmt("Created directory '%s' for the page dump...",
258 $html = HTML::p(fmt("Using directory '%s'", $directory));
261 StartLoadDump($request, _("Dumping Pages"), $html);
263 $dbi = $request->getDbh();
264 $thispage = $request->getArg('pagename'); // for "Return to ..."
265 if ($exclude = $request->getArg('exclude')) { // exclude which pagenames
266 $excludeList = explodePageList($exclude);
268 $excludeList = array();
270 if ($whichpages = $request->getArg('pages')) { // which pagenames
271 if ($whichpages == '[]') // current page
272 $whichpages = $thispage;
273 $pages = new WikiDB_Array_PageIterator(explodePageList($whichpages));
275 $pages = $dbi->getAllPages();
278 $request_args = $request->args;
280 while ($page = $pages->next()) {
281 $request->args = $request_args; // some plugins might change them (esp. on POST)
282 if (! $request->getArg('start_debug'))
283 @set_time_limit(30); // Reset watchdog.
285 $pagename = $page->getName();
286 PrintXML(HTML::br(), $pagename, ' ... ');
289 if (in_array($pagename, $excludeList)) {
290 PrintXML(_("Skipped."));
294 $filename = FilenameForPage($pagename);
296 if($page->getName() != $filename) {
297 $msg->pushContent(HTML::small(fmt("saved as %s", $filename)),
301 if ($request->getArg('include') == 'all')
302 $data = MailifyPage($page, 0);
304 $data = MailifyPage($page);
306 if ( !($fd = fopen("$directory/$filename", "wb")) ) {
307 $msg->pushContent(HTML::strong(fmt("couldn't open file '%s' for writing",
308 "$directory/$filename")));
309 $request->finish($msg);
312 $num = fwrite($fd, $data, strlen($data));
313 $msg->pushContent(HTML::small(fmt("%s bytes written", $num)));
317 assert($num == strlen($data));
321 EndLoadDump($request);
325 * Dump all pages as XHTML to a directory, as pagename.html.
326 * Copies all used css files to the directory, all used images to a
327 * "images" subdirectory, and all used buttons to a "images/buttons" subdirectory.
328 * The webserver must have write permissions to these directories.
329 * chown httpd HTML_DUMP_DIR; chmod u+rwx HTML_DUMP_DIR
332 * @param string directory (optional) path to dump to. Default: HTML_DUMP_DIR
333 * @param string pages (optional) Comma-seperated of glob-style pagenames to dump
334 * @param string exclude (optional) Comma-seperated of glob-style pagenames to exclude
336 function DumpHtmlToDir (&$request)
338 $directory = $request->getArg('directory');
339 if (empty($directory))
340 $directory = HTML_DUMP_DIR; // See lib/plugin/WikiForm.php:87
341 if (empty($directory))
342 $request->finish(_("You must specify a directory to dump to"));
344 // see if we can access the directory the user wants us to use
345 if (! file_exists($directory)) {
346 if (! mkdir($directory, 0755))
347 $request->finish(fmt("Cannot create directory '%s'", $directory));
349 $html = HTML::p(fmt("Created directory '%s' for the page dump...",
352 $html = HTML::p(fmt("Using directory '%s'", $directory));
355 StartLoadDump($request, _("Dumping Pages"), $html);
356 $thispage = $request->getArg('pagename'); // for "Return to ..."
358 $dbi = $request->getDbh();
359 if ($exclude = $request->getArg('exclude')) { // exclude which pagenames
360 $excludeList = explodePageList($exclude);
362 $excludeList = array();
364 if ($whichpages = $request->getArg('pages')) { // which pagenames
365 if ($whichpages == '[]') // current page
366 $whichpages = $thispage;
367 $pages = new WikiDB_Array_PageIterator(explodePageList($whichpages));
369 $pages = $dbi->getAllPages();
373 if (defined('HTML_DUMP_SUFFIX'))
374 $WikiTheme->HTML_DUMP_SUFFIX = HTML_DUMP_SUFFIX;
375 $WikiTheme->DUMP_MODE = 'HTML';
376 $request_args = $request->args;
378 while ($page = $pages->next()) {
379 $request->args = $request_args; // some plugins might change them (esp. on POST)
380 if (!$request->getArg('start_debug'))
381 @set_time_limit(30); // Reset watchdog.
383 $pagename = $page->getName();
384 PrintXML(HTML::br(), $pagename, ' ... ');
387 if (in_array($pagename, $excludeList)) {
388 PrintXML(_("Skipped."));
393 $request->setArg('pagename', $pagename); // Template::_basepage fix
394 $filename = FilenameForPage($pagename) . $WikiTheme->HTML_DUMP_SUFFIX;
397 $revision = $page->getCurrentRevision();
398 $transformedContent = $revision->getTransformedContent();
399 $template = new Template('browse', $request,
400 array('revision' => $revision,
401 'CONTENT' => $transformedContent));
403 $data = GeneratePageasXML($template, $pagename);
405 if ( !($fd = fopen("$directory/$filename", "wb")) ) {
406 $msg->pushContent(HTML::strong(fmt("couldn't open file '%s' for writing",
407 "$directory/$filename")));
408 $request->finish($msg);
410 $num = fwrite($fd, $data, strlen($data));
411 if($page->getName() != $filename) {
414 // drive where apache is installed
415 $prefix = '/' . substr($_SERVER["DOCUMENT_ROOT"],0,2);
417 $link = LinkURL("file://".$prefix.$directory."/".$filename,
419 $msg->pushContent(HTML::small(_("saved as "), $link, " ... "));
421 $msg->pushContent(HTML::small(fmt("%s bytes written", $num), "\n"));
425 assert($num == strlen($data));
429 if (!empty($WikiTheme->dumped_images) and is_array($WikiTheme->dumped_images)) {
430 @mkdir("$directory/images");
431 foreach ($WikiTheme->dumped_images as $img_file) {
432 if (($from = $WikiTheme->_findFile($img_file)) and basename($from)) {
433 $target = "$directory/images/".basename($img_file);
434 if (copy($WikiTheme->_path . $from, $target)) {
435 $msg = HTML(HTML::br(), HTML($from), HTML::small(fmt("... copied to %s", $target)));
439 $msg = HTML(HTML::br(), HTML($from), HTML::small(fmt("... not found", $target)));
444 if (!empty($WikiTheme->dumped_buttons) and is_array($WikiTheme->dumped_buttons)) {
446 @mkdir("$directory/images/buttons");
447 foreach ($WikiTheme->dumped_buttons as $text => $img_file) {
448 if (($from = $WikiTheme->_findFile($img_file)) and basename($from)) {
449 $target = "$directory/images/buttons/".basename($img_file);
450 if (copy($WikiTheme->_path . $from, $target)) {
451 $msg = HTML(HTML::br(), HTML($from), HTML::small(fmt("... copied to %s", $target)));
455 $msg = HTML(HTML::br(), HTML($from), HTML::small(fmt("... not found", $target)));
460 if (!empty($WikiTheme->dumped_css) and is_array($WikiTheme->dumped_css)) {
461 foreach ($WikiTheme->dumped_css as $css_file) {
462 if (($from = $WikiTheme->_findFile(basename($css_file))) and basename($from)) {
463 $target = "$directory/" . basename($css_file);
464 if (copy($WikiTheme->_path . $from, $target)) {
465 $msg = HTML(HTML::br(), HTML($from), HTML::small(fmt("... copied to %s", $target)));
469 $msg = HTML(HTML::br(), HTML($from), HTML::small(fmt("... not found", $target)));
474 $WikiTheme->HTML_DUMP_SUFFIX = '';
475 $WikiTheme->DUMP_MODE = false;
477 $request->setArg('pagename',$thispage); // Template::_basepage fix
478 EndLoadDump($request);
481 /* Known problem: any plugins or other code which echo()s text will
482 * lead to a corrupted html zip file which may produce the following
483 * errors upon unzipping:
485 * warning [wikihtml.zip]: 2401 extra bytes at beginning or within zipfile
486 * file #58: bad zipfile offset (local header sig): 177561
487 * (attempting to re-compensate)
489 * However, the actual wiki page data should be unaffected.
491 function MakeWikiZipHtml (&$request)
493 $zipname = "wikihtml.zip";
494 $zip = new ZipWriter("Created by PhpWiki " . PHPWIKI_VERSION, $zipname);
495 $dbi = $request->getDbh();
496 $thispage = $request->getArg('pagename'); // for "Return to ..."
497 if ($exclude = $request->getArg('exclude')) { // exclude which pagenames
498 $excludeList = explodePageList($exclude);
500 $excludeList = array();
502 if ($whichpages = $request->getArg('pages')) { // which pagenames
503 if ($whichpages == '[]') // current page
504 $whichpages = $thispage;
505 $pages = new WikiDB_Array_PageIterator(explodePageList($whichpages));
507 $pages = $dbi->getAllPages();
511 if (defined('HTML_DUMP_SUFFIX'))
512 $WikiTheme->HTML_DUMP_SUFFIX = HTML_DUMP_SUFFIX;
514 /* ignore fatals in plugins */
515 if (check_php_version(4,1)) {
516 global $ErrorManager;
517 $ErrorManager->pushErrorHandler(new WikiFunctionCb('_dump_error_handler'));
520 $request_args = $request->args;
522 while ($page = $pages->next()) {
523 $request->args = $request_args; // some plugins might change them (esp. on POST)
524 if (! $request->getArg('start_debug'))
525 @set_time_limit(30); // Reset watchdog.
527 $current = $page->getCurrentRevision();
528 if ($current->getVersion() == 0)
530 $pagename = $page->getName();
531 if (in_array($pagename, $excludeList)) {
535 $attrib = array('mtime' => $current->get('mtime'),
537 if ($page->get('locked'))
538 $attrib['write_protected'] = 1;
540 $request->setArg('pagename',$pagename); // Template::_basepage fix
541 $filename = FilenameForPage($pagename) . $WikiTheme->HTML_DUMP_SUFFIX;
542 $revision = $page->getCurrentRevision();
544 $transformedContent = $revision->getTransformedContent();
546 $template = new Template('browse', $request,
547 array('revision' => $revision,
548 'CONTENT' => $transformedContent));
550 $data = GeneratePageasXML($template, $pagename);
552 $zip->addRegularFile( $filename, $data, $attrib);
554 // FIXME: Deal with images here.
556 if (check_php_version(4,1)) {
557 $ErrorManager->popErrorHandler();
559 $WikiTheme->$HTML_DUMP_SUFFIX = '';
563 ////////////////////////////////////////////////////////////////
565 // Functions for restoring.
567 ////////////////////////////////////////////////////////////////
569 function SavePage (&$request, $pageinfo, $source, $filename)
571 $pagedata = $pageinfo['pagedata']; // Page level meta-data.
572 $versiondata = $pageinfo['versiondata']; // Revision level meta-data.
574 if (empty($pageinfo['pagename'])) {
575 PrintXML(HTML::dt(HTML::strong(_("Empty pagename!"))));
579 if (empty($versiondata['author_id']))
580 $versiondata['author_id'] = $versiondata['author'];
582 $pagename = $pageinfo['pagename'];
583 $content = $pageinfo['content'];
585 if ($pagename ==_("InterWikiMap"))
586 $content = _tryinsertInterWikiMap($content);
588 $dbi = $request->getDbh();
589 $page = $dbi->getPage($pagename);
591 // Try to merge if updated pgsrc contents are different. This
592 // whole thing is hackish
594 // TODO: try merge unless:
595 // if (current contents = default contents && pgsrc_version >=
596 // pgsrc_version) then just upgrade this pgsrc
597 $needs_merge = false;
601 if ($request->getArg('merge')) {
604 else if ($request->getArg('overwrite')) {
608 $current = $page->getCurrentRevision();
609 if ( (! $current->hasDefaultContents())
610 && ($current->getPackedContent() != $content)
611 && ($merging == true) ) {
612 include_once('lib/editpage.php');
613 $request->setArg('pagename', $pagename);
614 $r = $current->getVersion();
615 $request->setArg('revision', $current->getVersion());
616 $p = new LoadFileConflictPageEditor($request);
617 $p->_content = $content;
618 $p->_currentVersion = $r - 1;
619 $p->editPage($saveFailed = true);
620 return; //early return
623 foreach ($pagedata as $key => $value) {
625 $page->set($key, $value);
631 $mesg->pushContent(' ', fmt("from %s", $source));
634 //$current = $page->getCurrentRevision();
635 if ($current->getVersion() == 0) {
636 $mesg->pushContent(' ', _("new page"));
640 if ( (! $current->hasDefaultContents())
641 && ($current->getPackedContent() != $content) ) {
643 $mesg->pushContent(' ',
644 fmt("has edit conflicts - overwriting anyway"));
646 if (substr_count($source, 'pgsrc')) {
647 $versiondata['author'] = _("The PhpWiki programming team");
648 // but leave authorid as userid who loaded the file
652 $mesg->pushContent(' ', fmt("has edit conflicts - skipped"));
653 $needs_merge = true; // hackish
657 else if ($current->getPackedContent() == $content
658 && $current->get('author') == $versiondata['author']) {
659 // The page metadata is already changed, we don't need a new revision.
660 // This was called previously "is identical to current version %d - skipped"
661 // which is wrong, since the pagedata was stored, not skipped.
662 $mesg->pushContent(' ',
663 fmt("content is identical to current version %d - no new revision created",
664 $current->getVersion()));
671 // in case of failures print the culprit:
672 PrintXML(HTML::dt(WikiLink($pagename))); flush();
673 $new = $page->save($content, WIKIDB_FORCE_CREATE, $versiondata);
675 $mesg->pushContent(' ', fmt("- saved to database as version %d",
676 $new->getVersion()));
680 // hackish, $source contains needed path+filename
681 $f = str_replace(sprintf(_("MIME file %s"), ''), '', $f);
682 $f = str_replace(sprintf(_("Serialized file %s"), ''), '', $f);
683 $f = str_replace(sprintf(_("plain file %s"), ''), '', $f);
684 //check if uploaded file? they pass just the content, but the file is gone
687 $meb = Button(array('action' => 'loadfile',
691 _("PhpWikiAdministration"),
693 $owb = Button(array('action' => 'loadfile',
697 _("PhpWikiAdministration"),
699 $mesg->pushContent(' ', $meb, " ", $owb);
701 $mesg->pushContent(HTML::em(_(" Sorry, cannot merge uploaded files.")));
706 PrintXML(HTML::dt(HTML::em(WikiLink($pagename))), $mesg);
712 // action=revert (by diff)
713 function RevertPage (&$request)
716 $pagename = $request->getArg('pagename');
717 $version = $request->getArg('version');
719 PrintXML(HTML::dt(fmt("Revert")," ",WikiLink($pagename)),
720 HTML::dd(_("missing required version argument")));
723 $dbi = $request->getDbh();
724 $page = $dbi->getPage($pagename);
725 $current = $page->getCurrentRevision();
726 if ($current->getVersion() == 0) {
727 $mesg->pushContent(' ', _("no page content"));
728 PrintXML(HTML::dt(fmt("Revert")," ",WikiLink($pagename)),
732 if ($current->getVersion() == $version) {
733 $mesg->pushContent(' ', _("same version page"));
736 $rev = $page->getRevision($version);
737 $content = $rev->getPackedContent();
738 $versiondata = $rev->_data;
739 $versiondata['summary'] = sprintf(_("revert to version %d"), $version);
740 $new = $page->save($content, $current->getVersion() + 1, $versiondata);
742 $mesg->pushContent(' ', fmt("- version %d saved to database as version %d",
743 $version, $new->getVersion()));
744 PrintXML(HTML::dt(fmt("Revert")," ",WikiLink($pagename)),
749 function _tryinsertInterWikiMap($content) {
751 if (strpos($content, "<verbatim>")) {
752 //$error_html = " The newly loaded pgsrc already contains a verbatim block.";
755 if (!$goback && !defined('INTERWIKI_MAP_FILE')) {
756 $error_html = sprintf(" "._("%s: not defined"), "INTERWIKI_MAP_FILE");
759 $mapfile = FindFile(INTERWIKI_MAP_FILE,1);
760 if (!$goback && !file_exists($mapfile)) {
761 $error_html = sprintf(" "._("%s: file not found"), INTERWIKI_MAP_FILE);
765 if (!empty($error_html))
766 trigger_error(_("Default InterWiki map file not loaded.")
767 . $error_html, E_USER_NOTICE);
771 // if loading from virgin setup do echo, otherwise trigger_error E_USER_NOTICE
772 echo sprintf(_("Loading InterWikiMap from external file %s."), $mapfile),"<br />";
774 $fd = fopen ($mapfile, "rb");
775 $data = fread ($fd, filesize($mapfile));
777 $content = $content . "\n<verbatim>\n$data</verbatim>\n";
781 function ParseSerializedPage($text, $default_pagename, $user)
783 if (!preg_match('/^a:\d+:{[si]:\d+/', $text))
786 $pagehash = unserialize($text);
788 // Split up pagehash into four parts:
791 // page-level meta-data
792 // revision-level meta-data
794 if (!defined('FLAG_PAGE_LOCKED'))
795 define('FLAG_PAGE_LOCKED', 1);
796 $pageinfo = array('pagedata' => array(),
797 'versiondata' => array());
799 $pagedata = &$pageinfo['pagedata'];
800 $versiondata = &$pageinfo['versiondata'];
803 if (empty($pagehash['pagename']))
804 $pagehash['pagename'] = $default_pagename;
805 if (empty($pagehash['author'])) {
806 $pagehash['author'] = $user->getId();
809 foreach ($pagehash as $key => $value) {
814 $pageinfo[$key] = $value;
817 $pageinfo[$key] = join("\n", $value);
820 if (($value & FLAG_PAGE_LOCKED) != 0)
821 $pagedata['locked'] = 'yes';
825 $pagedata[$key] = $value;
829 $pagedata['perm'] = ParseMimeifiedPerm($value);
832 $versiondata['mtime'] = $value;
837 $versiondata[$key] = $value;
844 function SortByPageVersion ($a, $b) {
845 return $a['version'] - $b['version'];
848 function LoadFile (&$request, $filename, $text = false, $mtime = false)
850 if (!is_string($text)) {
852 $stat = stat($filename);
854 $text = implode("", file($filename));
857 if (! $request->getArg('start_debug'))
858 @set_time_limit(30); // Reset watchdog.
860 // FIXME: basename("filewithnoslashes") seems to return garbage sometimes.
861 $basename = basename("/dummy/" . $filename);
864 $mtime = time(); // Last resort.
866 $default_pagename = rawurldecode($basename);
868 if ( ($parts = ParseMimeifiedPages($text)) ) {
869 usort($parts, 'SortByPageVersion');
870 foreach ($parts as $pageinfo)
871 SavePage($request, $pageinfo, sprintf(_("MIME file %s"),
872 $filename), $basename);
874 else if ( ($pageinfo = ParseSerializedPage($text, $default_pagename,
875 $request->getUser())) ) {
876 SavePage($request, $pageinfo, sprintf(_("Serialized file %s"),
877 $filename), $basename);
880 $user = $request->getUser();
882 // Assume plain text file.
883 $pageinfo = array('pagename' => $default_pagename,
884 'pagedata' => array(),
886 => array('author' => $user->getId()),
887 'content' => preg_replace('/[ \t\r]*\n/', "\n",
890 SavePage($request, $pageinfo, sprintf(_("plain file %s"), $filename),
895 function LoadZip (&$request, $zipfile, $files = false, $exclude = false) {
896 $zip = new ZipReader($zipfile);
897 while (list ($fn, $data, $attrib) = $zip->readFile()) {
898 // FIXME: basename("filewithnoslashes") seems to return
899 // garbage sometimes.
900 $fn = basename("/dummy/" . $fn);
901 if ( ($files && !in_array($fn, $files))
902 || ($exclude && in_array($fn, $exclude)) ) {
903 PrintXML(HTML::dt(WikiLink($fn)),
904 HTML::dd(_("Skipping")));
909 LoadFile($request, $fn, $data, $attrib['mtime']);
913 function LoadDir (&$request, $dirname, $files = false, $exclude = false) {
914 $fileset = new LimitedFileSet($dirname, $files, $exclude);
916 if (!$files and ($skiplist = $fileset->getSkippedFiles())) {
917 PrintXML(HTML::dt(HTML::strong(_("Skipping"))));
919 foreach ($skiplist as $file)
920 $list->pushContent(HTML::li(WikiLink($file)));
921 PrintXML(HTML::dd($list));
924 // Defer HomePage loading until the end. If anything goes wrong
925 // the pages can still be loaded again.
926 $files = $fileset->getFiles();
927 if (in_array(HOME_PAGE, $files)) {
928 $files = array_diff($files, array(HOME_PAGE));
929 $files[] = HOME_PAGE;
931 foreach ($files as $file) {
932 if (substr($file,-1,1) != '~') // refuse to load backup files
933 LoadFile($request, "$dirname/$file");
937 class LimitedFileSet extends FileSet {
938 function LimitedFileSet($dirname, $_include, $exclude) {
939 $this->_includefiles = $_include;
940 $this->_exclude = $exclude;
941 $this->_skiplist = array();
942 parent::FileSet($dirname);
945 function _filenameSelector($fn) {
946 $incl = &$this->_includefiles;
947 $excl = &$this->_exclude;
949 if ( ($incl && !in_array($fn, $incl))
950 || ($excl && in_array($fn, $excl)) ) {
951 $this->_skiplist[] = $fn;
958 function getSkippedFiles () {
959 return $this->_skiplist;
964 function IsZipFile ($filename_or_fd)
966 // See if it looks like zip file
967 if (is_string($filename_or_fd))
969 $fd = fopen($filename_or_fd, "rb");
970 $magic = fread($fd, 4);
975 $fpos = ftell($filename_or_fd);
976 $magic = fread($filename_or_fd, 4);
977 fseek($filename_or_fd, $fpos);
980 return $magic == ZIP_LOCHEAD_MAGIC || $magic == ZIP_CENTHEAD_MAGIC;
984 function LoadAny (&$request, $file_or_dir, $files = false, $exclude = false)
986 // Try urlencoded filename for accented characters.
987 if (!file_exists($file_or_dir)) {
988 // Make sure there are slashes first to avoid confusing phps
989 // with broken dirname or basename functions.
990 // FIXME: windows uses \ and :
991 if (is_integer(strpos($file_or_dir, "/"))) {
992 $file_or_dir = FindFile($file_or_dir);
994 if (!file_exists($file_or_dir))
995 $file_or_dir = dirname($file_or_dir) . "/"
996 . urlencode(basename($file_or_dir));
998 // This is probably just a file.
999 $file_or_dir = urlencode($file_or_dir);
1003 $type = filetype($file_or_dir);
1004 if ($type == 'link') {
1005 // For symbolic links, use stat() to determine
1006 // the type of the underlying file.
1007 list(,,$mode) = stat($file_or_dir);
1008 $type = ($mode >> 12) & 017;
1011 elseif ($type == 004)
1016 $request->finish(fmt("Unable to load: %s", $file_or_dir));
1018 else if ($type == 'dir') {
1019 LoadDir($request, $file_or_dir, $files, $exclude);
1021 else if ($type != 'file' && !preg_match('/^(http|ftp):/', $file_or_dir))
1023 $request->finish(fmt("Bad file type: %s", $type));
1025 else if (IsZipFile($file_or_dir)) {
1026 LoadZip($request, $file_or_dir, $files, $exclude);
1028 else /* if (!$files || in_array(basename($file_or_dir), $files)) */
1030 LoadFile($request, $file_or_dir);
1034 function LoadFileOrDir (&$request)
1036 $source = $request->getArg('source');
1037 $finder = new FileFinder;
1038 $source = $finder->slashifyPath($source);
1039 $page = rawurldecode(basename($source));
1040 StartLoadDump($request, fmt("Loading '%s'",
1041 HTML(dirname($source),
1042 dirname($source) ? "/" : "",
1043 WikiLink($page,'auto'))));
1045 LoadAny($request, $source);
1047 EndLoadDump($request);
1050 function SetupWiki (&$request)
1052 global $GenericPages, $LANG;
1055 //FIXME: This is a hack (err, "interim solution")
1056 // This is a bogo-bogo-login: Login without
1057 // saving login information in session state.
1058 // This avoids logging in the unsuspecting
1059 // visitor as "The PhpWiki programming team".
1061 // This really needs to be cleaned up...
1062 // (I'm working on it.)
1063 $real_user = $request->_user;
1064 if (ENABLE_USER_NEW)
1065 $request->_user = new _BogoUser(_("The PhpWiki programming team"));
1068 $request->_user = new WikiUser($request, _("The PhpWiki programming team"),
1071 StartLoadDump($request, _("Loading up virgin wiki"));
1074 $pgsrc = FindLocalizedFile(WIKI_PGSRC);
1075 $default_pgsrc = FindFile(DEFAULT_WIKI_PGSRC);
1077 $request->setArg('overwrite',true);
1078 if ($default_pgsrc != $pgsrc) {
1079 LoadAny($request, $default_pgsrc, $GenericPages);
1081 $request->setArg('overwrite',false);
1082 LoadAny($request, $pgsrc);
1084 // Ensure that all mandatory pages are loaded
1085 $finder = new FileFinder;
1086 foreach (array_merge(explode(':','OldTextFormattingRules:TextFormattingRules:PhpWikiAdministration'),
1087 $GLOBALS['AllActionPages'],
1088 array(constant('HOME_PAGE'))) as $f) {
1089 $page = gettext($f);
1090 if (isSubPage($page))
1091 $page = urlencode($page);
1092 if (! $request->_dbi->isWikiPage(urldecode($page)) ) {
1093 // translated version provided?
1094 if ($lf = FindLocalizedFile($pgsrc . $finder->_pathsep . $page, 1))
1095 LoadAny($request, $lf);
1096 else { // load english version of required action page
1097 LoadAny($request, FindFile(DEFAULT_WIKI_PGSRC . $finder->_pathsep . urlencode($f)));
1101 if (!$request->_dbi->isWikiPage(urldecode($page))) {
1102 trigger_error(sprintf("Mandatory file %s couldn't be loaded!", $page),
1108 EndLoadDump($request);
1111 function LoadPostFile (&$request)
1113 $upload = $request->getUploadedFile('file');
1116 $request->finish(_("No uploaded file to upload?")); // FIXME: more concise message
1119 // Dump http headers.
1120 StartLoadDump($request, sprintf(_("Uploading %s"), $upload->getName()));
1123 $fd = $upload->open();
1125 LoadZip($request, $fd, false, array(_("RecentChanges")));
1127 LoadFile($request, $upload->getName(), $upload->getContents());
1130 EndLoadDump($request);
1134 $Log: not supported by cvs2svn $
1135 Revision 1.116 2004/07/01 09:05:41 rurban
1136 support pages and exclude arguments for all 4 dump methods
1138 Revision 1.115 2004/07/01 08:51:22 rurban
1139 dumphtml: added exclude, print pagename before processing
1141 Revision 1.114 2004/06/28 12:51:41 rurban
1142 improved dumphtml and virgin setup
1144 Revision 1.113 2004/06/27 10:26:02 rurban
1145 oci8 patch by Philippe Vanhaesendonck + some ADODB notes+fixes
1147 Revision 1.112 2004/06/25 14:29:20 rurban
1148 WikiGroup refactoring:
1149 global group attached to user, code for not_current user.
1150 improved helpers for special groups (avoid double invocations)
1151 new experimental config option ENABLE_XHTML_XML (fails with IE, and document.write())
1152 fixed a XHTML validation error on userprefs.tmpl
1154 Revision 1.111 2004/06/21 16:38:55 rurban
1155 fixed the StartLoadDump html argument hack.
1157 Revision 1.110 2004/06/21 16:22:30 rurban
1158 add DEFAULT_DUMP_DIR and HTML_DUMP_DIR constants, for easier cmdline dumps,
1159 fixed dumping buttons locally (images/buttons/),
1160 support pages arg for dumphtml,
1161 optional directory arg for dumpserial + dumphtml,
1162 fix a AllPages warning,
1163 show dump warnings/errors on DEBUG,
1164 don't warn just ignore on wikilens pagelist columns, if not loaded.
1165 RateIt pagelist column is called "rating", not "ratingwidget" (Dan?)
1167 Revision 1.109 2004/06/17 11:31:05 rurban
1168 jump back to label after dump/upgrade
1170 Revision 1.108 2004/06/16 12:43:01 rurban
1171 4.0.6 cannot use this errorhandler (not found)
1173 Revision 1.107 2004/06/14 11:31:37 rurban
1174 renamed global $Theme to $WikiTheme (gforge nameclash)
1175 inherit PageList default options from PageList
1176 default sortby=pagename
1177 use options in PageList_Selectable (limit, sortby, ...)
1178 added action revert, with button at action=diff
1179 added option regex to WikiAdminSearchReplace
1181 Revision 1.106 2004/06/13 13:54:25 rurban
1182 Catch fatals on the four dump calls (as file and zip, as html and mimified)
1183 FoafViewer: Check against external requirements, instead of fatal.
1184 Change output for xhtmldumps: using file:// urls to the local fs.
1185 Catch SOAP fatal by checking for GOOGLE_LICENSE_KEY
1186 Import GOOGLE_LICENSE_KEY and FORTUNE_DIR from config.ini.
1188 Revision 1.105 2004/06/08 19:48:16 rurban
1189 fixed foreign setup: no ugly skipped msg for the GenericPages, load english actionpages if translated not found
1191 Revision 1.104 2004/06/08 13:51:57 rurban
1194 Revision 1.103 2004/06/08 10:54:46 rurban
1195 better acl dump representation, read back acl and owner
1197 Revision 1.102 2004/06/06 16:58:51 rurban
1198 added more required ActionPages for foreign languages
1199 install now english ActionPages if no localized are found. (again)
1200 fixed default anon user level to be 0, instead of -1
1201 (wrong "required administrator to view this page"...)
1203 Revision 1.101 2004/06/04 20:32:53 rurban
1204 Several locale related improvements suggested by Pierrick Meignen
1205 LDAP fix by John Cole
1206 reanable admin check without ENABLE_PAGEPERM in the admin plugins
1208 Revision 1.100 2004/05/02 21:26:38 rurban
1209 limit user session data (HomePageHandle and auth_dbi have to invalidated anyway)
1210 because they will not survive db sessions, if too large.
1211 extended action=upgrade
1212 some WikiTranslation button work
1213 revert WIKIAUTH_UNOBTAINABLE (need it for main.php)
1214 some temp. session debug statements
1216 Revision 1.99 2004/05/02 15:10:07 rurban
1217 new finally reliable way to detect if /index.php is called directly
1218 and if to include lib/main.php
1219 new global AllActionPages
1220 SetupWiki now loads all mandatory pages: HOME_PAGE, action pages, and warns if not.
1221 WikiTranslation what=buttons for Carsten to create the missing MacOSX buttons
1222 PageGroupTestOne => subpages
1223 renamed PhpWikiRss to PhpWikiRecentChanges
1224 more docs, default configs, ...
1226 Revision 1.98 2004/04/29 23:25:12 rurban
1227 re-ordered locale init (as in 1.3.9)
1228 fixed loadfile with subpages, and merge/restore anyway
1229 (sf.net bug #844188)
1231 Revision 1.96 2004/04/19 23:13:03 zorloc
1232 Connect the rest of PhpWiki to the IniConfig system. Also the keyword regular expression is not a config setting
1234 Revision 1.95 2004/04/18 01:11:52 rurban
1235 more numeric pagename fixes.
1236 fixed action=upload with merge conflict warnings.
1237 charset changed from constant to global (dynamic utf-8 switching)
1239 Revision 1.94 2004/03/14 16:36:37 rurban
1240 dont load backup files
1242 Revision 1.93 2004/02/26 03:22:05 rurban
1243 also copy css and images with XHTML Dump
1245 Revision 1.92 2004/02/26 02:25:54 rurban
1246 fix empty and #-anchored links in XHTML Dumps
1248 Revision 1.91 2004/02/24 17:19:37 rurban
1249 debugging helpers only
1251 Revision 1.90 2004/02/24 17:09:24 rurban
1252 fixed \r\r\n with dumping on windows
1254 Revision 1.88 2004/02/22 23:20:31 rurban
1255 fixed DumpHtmlToDir,
1256 enhanced sortby handling in PageList
1257 new button_heading th style (enabled),
1258 added sortby and limit support to the db backends and plugins
1259 for paging support (<<prev, next>> links on long lists)
1261 Revision 1.87 2004/01/26 09:17:49 rurban
1262 * changed stored pref representation as before.
1263 the array of objects is 1) bigger and 2)
1264 less portable. If we would import packed pref
1265 objects and the object definition was changed, PHP would fail.
1266 This doesn't happen with an simple array of non-default values.
1267 * use $prefs->retrieve and $prefs->store methods, where retrieve
1268 understands the interim format of array of objects also.
1269 * simplified $prefs->get() and fixed $prefs->set()
1270 * added $user->_userid and class '_WikiUser' portability functions
1271 * fixed $user object ->_level upgrading, mostly using sessions.
1272 this fixes yesterdays problems with loosing authorization level.
1273 * fixed WikiUserNew::checkPass to return the _level
1274 * fixed WikiUserNew::isSignedIn
1275 * added explodePageList to class PageList, support sortby arg
1276 * fixed UserPreferences for WikiUserNew
1277 * fixed WikiPlugin for empty defaults array
1278 * UnfoldSubpages: added pagename arg, renamed pages arg,
1279 removed sort arg, support sortby arg
1281 Revision 1.86 2003/12/02 16:18:26 carstenklapp
1282 Minor enhancement: Provide more meaningful filenames for WikiDB zip
1285 Revision 1.85 2003/11/30 18:18:13 carstenklapp
1286 Minor code optimization: use include_once instead of require_once
1287 inside functions that might not always called.
1289 Revision 1.84 2003/11/26 20:47:47 carstenklapp
1290 Redo bugfix: My last refactoring broke merge-edit & overwrite
1291 functionality again, should be fixed now. Sorry.
1293 Revision 1.83 2003/11/20 22:18:54 carstenklapp
1294 New feature: h1 during merge-edit displays WikiLink to original page.
1295 Internal changes: Replaced some hackish url-generation code in
1296 function SavePage (for pgsrc merge-edit) with appropriate Button()
1299 Revision 1.82 2003/11/18 19:48:01 carstenklapp
1300 Fixed missing gettext _() for button name.
1302 Revision 1.81 2003/11/18 18:28:35 carstenklapp
1303 Bugfix: In the Load File function of PhpWikiAdministration: When doing
1304 a "Merge Edit" or "Restore Anyway", page names containing accented
1305 letters (such as locale/de/pgsrc/G%E4steBuch) would produce a file not
1306 found error (Use FilenameForPage funtion to urlencode page names).
1308 Revision 1.80 2003/03/07 02:46:57 dairiki
1309 Omit checks for safe_mode before set_time_limit(). Just prefix the
1310 set_time_limit() calls with @ so that they fail silently if not
1313 Revision 1.79 2003/02/26 01:56:05 dairiki
1314 Only zip pages with legal pagenames.
1316 Revision 1.78 2003/02/24 02:05:43 dairiki
1317 Fix "n bytes written" message when dumping HTML.
1319 Revision 1.77 2003/02/21 04:12:05 dairiki
1320 Minor fixes for new cached markup.
1322 Revision 1.76 2003/02/16 19:47:17 dairiki
1323 Update WikiDB timestamp when editing or deleting pages.
1325 Revision 1.75 2003/02/15 03:04:30 dairiki
1326 Fix for WikiUser constructor API change.
1328 Revision 1.74 2003/02/15 02:18:04 dairiki
1329 When default language was English (at least), pgsrc was being
1332 LimitedFileSet: Fix typo/bug. ($include was being ignored.)
1334 SetupWiki(): Fix bugs in loading of $GenericPages.
1336 Revision 1.73 2003/01/28 21:09:17 zorloc
1337 The get_cfg_var() function should only be used when one is
1338 interested in the value from php.ini or similar. Use ini_get()
1339 instead to get the effective value of a configuration variable.
1342 Revision 1.72 2003/01/03 22:25:53 carstenklapp
1343 Cosmetic fix to "Merge Edit" & "Overwrite" buttons. Added "The PhpWiki
1344 programming team" as author when loading from pgsrc. Source
1347 Revision 1.71 2003/01/03 02:48:05 carstenklapp
1348 function SavePage: Added loadfile options for overwriting or merge &
1349 compare a loaded pgsrc file with an existing page.
1351 function LoadAny: Added a general error message when unable to load a
1352 file instead of defaulting to "Bad file type".
1360 // c-basic-offset: 4
1361 // c-hanging-comment-ender-p: nil
1362 // indent-tabs-mode: nil