From: <car...@us...> - 2025-02-15 05:18:11
|
Revision: 11109 http://sourceforge.net/p/phpwiki/code/11109 Author: carstenklapp Date: 2025-02-15 05:18:08 +0000 (Sat, 15 Feb 2025) Log Message: ----------- Bugfixes and improvements to loading/saving pages as files. see ReleaseNotes. Modified Paths: -------------- trunk/lib/PagePerm.php trunk/lib/loadsave.php trunk/lib/main.php trunk/lib/mimelib.php trunk/lib/plugin/PageDump.php trunk/lib/plugin/WikiForm.php trunk/pgsrc/ReleaseNotes Modified: trunk/lib/PagePerm.php =================================================================== --- trunk/lib/PagePerm.php 2025-02-14 23:22:37 UTC (rev 11108) +++ trunk/lib/PagePerm.php 2025-02-15 05:18:08 UTC (rev 11109) @@ -194,6 +194,7 @@ case 'ziphtml': case 'dumphtml': case 'dumpserial': + case 'dumpsvn': return 'dump'; // invent a new access-perm massedit? or switch back to change, or keep it at edit? Modified: trunk/lib/loadsave.php =================================================================== --- trunk/lib/loadsave.php 2025-02-14 23:22:37 UTC (rev 11108) +++ trunk/lib/loadsave.php 2025-02-15 05:18:08 UTC (rev 11109) @@ -120,7 +120,7 @@ /** * For reference see: - * http://www.nacs.uci.edu/indiv/ehood/MIME/2045/rfc2045.html + * https://www.ietf.org/rfc/rfc2045.txt * http://www.faqs.org/rfcs/rfc2045.html * (RFC 1521 has been superceeded by RFC 2045 & others). * @@ -128,9 +128,10 @@ /* * @param WikiDB_Page $page * @param int $nversions + * @param bool $forsvn * @return string */ -function MailifyPage($page, $nversions = 1) +function MailifyPage($page, $nversions = 1, $forsvn = false) { $current = $page->getCurrentRevision(false); $head = ''; @@ -146,7 +147,20 @@ // requires a destination field. $head .= "To: $from (PhpWiki)\r\n"; } - $head .= "Date: " . Rfc2822DateTime($current->get('mtime')) . "\r\n"; + if (!$forsvn) { + // normal dumps use date modified + $head .= "Date: " . Rfc2822DateTime($current->get('mtime')) . "\r\n"; + } else { + $use_page_creationdate_instead = 0; + if ($use_page_creationdate_instead) { + $head .= "Date: " . Rfc2822DateTime($page->get('date')) . "\r\n"; + // for special pagedumps where you don't want to change the date + // in dumped pgsrc, use creation date + } else { + // normal dumps use date modified + $head .= "Date: " . Rfc2822DateTime($current->get('mtime')) . "\r\n"; + } + } $head .= sprintf( "Mime-Version: 1.0 (Produced by PhpWiki %s)\r\n", PHPWIKI_VERSION @@ -155,7 +169,7 @@ $iter = $page->getAllRevisions(); $parts = array(); while ($revision = $iter->next()) { - $parts[] = MimeifyPageRevision($page, $revision); + $parts[] = MimeifyPageRevision($page, $revision, $forsvn); if ($nversions > 0 && count($parts) >= $nversions) { break; } @@ -192,7 +206,7 @@ global $request; $action = $request->getArg('action'); } - if ($action != 'dumpserial') { // zip, ziphtml, dumphtml + if (($action != 'dumpserial') && ($action != 'dumpsvn')) { // zip, ziphtml, dumphtml // For every %2F we will need to mkdir -p dirname($pagename) $enc = preg_replace('/%2F/', '/', $enc); } @@ -296,7 +310,7 @@ $ErrorManager->popErrorHandler(); - header('Content-Transfer-Encoding: binary'); + header("Content-Type: application/octet-stream"); header('Content-Disposition: attachment; filename="'.$zipname.'"'); header('Content-Length: '.filesize($tmpfilename)); @@ -308,6 +322,129 @@ /** * @param WikiRequest $request */ +function DumpSVNToDir(&$request) //this is mostly a copy of DumpToDir +{ + $directory = $request->getArg('directory'); + if (empty($directory)) { + $directory = DEFAULT_DUMP_DIR; + } + if (empty($directory)) { + $html = HTML::p( + array('class' => 'error'), + _("You must specify a directory to dump to") + ); + StartLoadDump($request, _("Dumping Pages for developer"), $html); + EndLoadDump($request); + return; + } + + // see if we can access the directory the user wants us to use + if (!file_exists($directory)) { + if (!mkdir_p($directory, 0755)) { + $html = HTML::p( + array('class' => 'error'), + fmt("Cannot create directory “%s”", $directory) + ); + StartLoadDump($request, _("Dumping Pages for developer"), $html); + EndLoadDump($request); + return; + } else { + $html = HTML::p(fmt( + "Created directory “%s” for the page dump...", + $directory + )); + } + } elseif (!is_writable($directory)) { + $html = HTML::p( + array('class' => 'error'), + fmt( + "Cannot use directory “%s”, it is not writable", + $directory + ) + ); + StartLoadDump($request, _("DumpDumping Pages for SVN"), $html); + EndLoadDump($request); + return; + } else { + $html = HTML::p(fmt("Using directory “%s”", $directory)); + } + + StartLoadDump($request, _("Dumping Pages for SVN"), $html); + + $dbi =& $request->_dbi; + $thispage = $request->getArg('pagename'); // for "Return to ..." + if ($exclude = $request->getArg('exclude')) { // exclude which pagenames + $excludeList = explodePageList($exclude); + } else { + $excludeList = array(); + } + $include_empty = false; + if ($request->getArg('include') == 'empty') { + $include_empty = true; + } + if ($pages = $request->getArg('pages')) { // which pagenames + if ($pages == '[]') { // current page + $pages = $thispage; + } + $page_iter = new WikiDB_Array_PageIterator(explodePageList($pages)); + } else { + $page_iter = $dbi->getAllPages($include_empty, false, false, $excludeList); + } + + $request_args = $request->args; + $timeout = (!$request->getArg('start_debug')) ? 30 : 240; + + while ($page = $page_iter->next()) { + $request->args = $request_args; // some plugins might change them (esp. on POST) + longer_timeout($timeout); // Reset watchdog + + $pagename = $page->getName(); + PrintXML(HTML::br(), $pagename, ' ... '); + flush(); + + if (in_array($pagename, $excludeList)) { + PrintXML(_("Skipped")); + flush(); + continue; + } + //$filename = FilenameForPage($pagename, 'dumpsvn');//why doesn't this workzzz + $filename = rawurlencode($pagename);//this works + $msg = HTML(); + if ($page->getName() != $filename) { + $msg->pushContent( + HTML::small(fmt("saved as %s", $filename)), + " ... " + ); + } + + if ($request->getArg('include') == 'all') { + $data = MailifyPage($page, 0, true); + } else { + $data = MailifyPage($page, 1, true); + } + + if (!($fd = fopen($directory . "/" . $filename, "wb"))) { + $msg->pushContent(HTML::strong(fmt( + "couldn't open file “%s” for writing", + "$directory/$filename" + ))); + $request->finish($msg); + } + + $num = fwrite($fd, $data, strlen($data)); + $msg->pushContent(HTML::small(fmt("%s bytes written", $num))); + PrintXML($msg); + flush(); + assert($num == strlen($data)); + fclose($fd); + } + + EndLoadDump($request); +} + +/** + * @param WikiRequest $request + */ function DumpToDir(&$request) { $directory = $request->getArg('directory'); @@ -393,7 +530,8 @@ flush(); continue; } - $filename = FilenameForPage($pagename); + //$filename = FilenameForPage($pagename, 'dumpserial');//why doesn't this workzzz + $filename = rawurlencode($pagename);//this works $msg = HTML(); if ($page->getName() != $filename) { $msg->pushContent( @@ -913,7 +1051,7 @@ $ErrorManager->popErrorHandler(); - header('Content-Transfer-Encoding: binary'); + header("Content-Type: application/octet-stream"); header('Content-Disposition: attachment; filename="'.$zipname.'"'); header('Content-Length: '.filesize($tmpfilename)); @@ -962,7 +1100,7 @@ //////////////////////////////////////////////////////////////// // -// Functions for restoring. +// Functions for restoring to database. This is really hackish // //////////////////////////////////////////////////////////////// @@ -976,9 +1114,13 @@ PrintXML(HTML::p(HTML::strong(_("Empty pagename!")))); return; } + //print_r($versiondata); + if (empty($versiondata['author'])) { + // $versiondata['author'] = ADMIN_USER; //use admin + } if (empty($versiondata['author_id'])) { - $versiondata['author_id'] = $versiondata['author']; + $versiondata['author_id'] = ADMIN_USER; //use admin } // remove invalid backend specific chars. utf8 issues mostly @@ -993,6 +1135,10 @@ if ($pagename == __("InterWikiMap")) { $content = _tryinsertInterWikiMap($content); } + // echo "<pre>"; + // echo "metadata $pagedata found in pgsrc is:\n"; + // print_r($pagedata); + // echo "</pre>"; $dbi =& $request->_dbi; $page = $dbi->getPage($pagename); @@ -1031,13 +1177,6 @@ return; //early return } } - if (!$skip) { - foreach ($pagedata as $key => $value) { - if (!empty($value)) { - $page->set($key, $value); - } - } - } $mesg = HTML::span(); if ($source) { @@ -1048,9 +1187,8 @@ $current = $page->getCurrentRevision(); } if ($current->getVersion() == 0) { - $versiondata['author'] = ADMIN_USER; - $versiondata['author_id'] = ADMIN_USER; $mesg->pushContent(' - ', _("New page")); + $isnewpage = true; } else { if ((!$current->hasDefaultContents()) && ($current->getPackedContent() != $content) @@ -1061,10 +1199,6 @@ fmt("has edit conflicts - overwriting anyway") ); $skip = false; - if (substr_count($source, 'pgsrc')) { - $versiondata['author'] = ADMIN_USER; - // but leave authorid as userid who loaded the file - } } else { if (isset($edit['keep_old'])) { $mesg->pushContent(' ', fmt("keep old")); @@ -1088,9 +1222,9 @@ } if (!$skip) { + // SAVE THE DATA INTO THE DATABASE // in case of failures print the culprit: PrintXML(HTML::span(WikiLink($pagename))); - flush(); $new = $page->save($content, WIKIDB_FORCE_CREATE, $versiondata); $dbi->touch(); $mesg->pushContent(' ', fmt( @@ -1097,6 +1231,84 @@ "- saved to database as version %d", $new->getVersion() )); + //update metadata of page: locked, date, hits if new page + //$mesg->pushContent(" (also setting metadata)"); + //$mesg->pushContent(" DEBUG: (\$isnewpage=$isnewpage)"); + + // set hits + if ($isnewpage) { //don't load hits from pgsrc if page exists in database, only if new + if (isset($pagedata['hits'])) + { + //sanitize hits. + $pagedata['hits'] = intval($pagedata['hits']); + $mesg->pushContent(", Loading hits=".$pagedata['hits']); + $page->set('hits', $pagedata['hits']); + } + if (isset($pagedata['perm'])) + { + //todo: sanitize acl? + $mesg->pushContent(", Loading acl=".$pagedata['perm']); + $page->set('perm', $pagedata['perm']); + } + } else { + if (isset($pagedata['hits'])) + { + $mesg->pushContent(", Not overwriting hits"); + //unset($pagedata['hits']);//keep hits from loading over existing page + } + if (isset($pagedata['perm'])) + { + $mesg->pushContent(", Not overwriting acl"); + //unset($pagedata['perm']);//keep acl from loading over existing page + } + } + + // set page locked + // how to get current page lock status?? to customise messages more + if ($pagedata['locked']) { + $mesg->pushContent(", LOCKING"); + $page->set('locked', $pagedata['locked']); + } else { + define ('ALLOW_PGSRC_TO_UNLOCK_PAGES' ,0);//debugging and security + if (defined('ALLOW_PGSRC_TO_UNLOCK_PAGES') && ALLOW_PGSRC_TO_UNLOCK_PAGES) { + $mesg->pushContent(", UNLOCKING"); + $page->set('locked', false); + } else { + if ($isnewpage) { + $mesg->pushContent(", unlocked"); + } else { + $mesg->pushContent(", Ignoring UNLOCKED"); + } + //unset($pagedata['locked']);//keep locked from loading over existing page + } + } + + // set page creation date + $page->set('date', $pagedata['date']); +// unset($pagedata['date']); + + // set page owner + if ($pagedata['owner']) { + $page->set('owner', $pagedata['owner']); + } +// unset($pagedata['owner']); + + // if we want to load other fields in the future from + // MIME file, here is where we do it: + // + // comment out the below to discard remaining data from loaded file for security. + // loading remaining data: + // if ($s=sizeof($pagedata)) { + // echo "<pre>$s extra pagedata entries found in loaded file:\n"; + // print_r($pagedata);//this formatting is fine, normal users will never get here + // echo "</pre>"; + // foreach ($pagedata as $key => $value) { + // $mesg->pushContent(", setting '$key'='$value'"); + // $page->set($key, $value); + // } + + // } + $mesg->pushContent(HTML::br()); } if ($needs_merge) { @@ -1195,7 +1407,7 @@ 'method' => 'post'), HiddenInputs($request->getArgs(), false, array('verify')), HiddenInputs(array('verify' => 1)), - Button('submit:verify', _("Yes"), 'button'), + Button('submit:verify', _("Revert"), 'button'), HTML::raw(' '), Button('submit:cancel', _("Cancel"), 'button') ) Modified: trunk/lib/main.php =================================================================== --- trunk/lib/main.php 2025-02-14 23:22:37 UTC (rev 11108) +++ trunk/lib/main.php 2025-02-15 05:18:08 UTC (rev 11109) @@ -595,6 +595,7 @@ 'diff' => _("diff this page"), 'dumphtml' => _("dump HTML pages"), 'dumpserial' => _("dump serial pages"), + 'dumpsvn' => _("dump pages for svn"), 'edit' => _("edit this page"), 'rename' => _("rename this page"), 'revert' => _("revert to a previous version of this page"), @@ -640,6 +641,7 @@ 'diff' => _("Diffing pages"), 'dumphtml' => _("Dumping HTML pages"), 'dumpserial' => _("Dumping serial pages"), + 'dumpsvn' => _("dump pages for svn"), 'edit' => _("Editing pages"), 'revert' => _("Reverting to a previous version of pages"), 'create' => _("Creating pages"), @@ -1385,6 +1387,12 @@ echo "PhpWiki " . PHPWIKI_VERSION . "\n"; } + public function action_dumpsvn() + { + include_once 'lib/loadsave.php'; + DumpSVNToDir($this); + } + public function action_dumpserial() { include_once 'lib/loadsave.php'; Modified: trunk/lib/mimelib.php =================================================================== --- trunk/lib/mimelib.php 2025-02-14 23:22:37 UTC (rev 11108) +++ trunk/lib/mimelib.php 2025-02-15 05:18:08 UTC (rev 11109) @@ -129,10 +129,11 @@ /** * @param WikiDB_Page $page * @param WikiDB_PageRevision $revision + * @param bool $forsvn * @return string */ -function MimeifyPageRevision(&$page, &$revision) +function MimeifyPageRevision(&$page, &$revision, $forsvn) { // $wikidb =& $revision->_wikidb; // $page = $wikidb->getPage($revision->getName()); @@ -153,6 +154,9 @@ if (ENABLE_EXTERNAL_PAGES && $page->get('external')) { $params['flags'] = ($params['flags'] ? $params['flags'] . ',EXTERNAL_PAGE' : 'EXTERNAL_PAGE'); } + if ($params['flags'] == '') { + unset($params['flags']); + } if ($revision->get('author_id')) { $params['author_id'] = $revision->get('author_id'); } @@ -170,7 +174,40 @@ $params['acl'] = $acl->asAclLines(); //TODO: convert to multiple lines? acl-view => groups,...; acl-edit => groups,... } + if ($forsvn) + { + // Strip out all this junk: + // version=74; + // lastmodified=1041561552; + // author_id=127.0.0.1; + // hits=146 + // owner + // acl + // summary + $killme = array( + //"author", + "version", + "lastmodified", + "author_id", + "hits", + "owner", + // "acl", + "summary" + ); + foreach ($killme as $pattern) + { + unset($params[$pattern]); + } + //Add in standard stuff for dumping + $params['author'] = _("The PhpWiki Team"); + } + if (STRICT_MAILABLE_PAGEDUMPS) { + $params['charset'] = 'US-ASCII'; //kind of a kludge to assume without massaging text? + } else { + $params['charset'] = 'UTF-8'; //kind of a kludge to assume without checking? + } + // Non-US-ASCII is not allowed in Mime headers (at least not without // special handling) --- so we urlencode all parameter values. foreach ($params as $key => $val) { Modified: trunk/lib/plugin/PageDump.php =================================================================== --- trunk/lib/plugin/PageDump.php 2025-02-14 23:22:37 UTC (rev 11108) +++ trunk/lib/plugin/PageDump.php 2025-02-15 05:18:08 UTC (rev 11109) @@ -1,6 +1,6 @@ <?php /** - * Copyright © 2003 $ThePhpWikiProgrammingTeam + * Copyright © 2003-2025 $ThePhpWikiProgrammingTeam * * This file is part of PhpWiki. * @@ -94,7 +94,8 @@ $p = $dbi->getPage($page); include_once 'lib/loadsave.php'; - $mailified = MailifyPage($p, ($format == 'backup') ? 99 : 1); + // we let MailifyPage handle forsvn format now instead of trying to strip it out later + $mailified = MailifyPage($p, ($format == 'backup') ? 99 : 1, ($format == 'forsvn')) ; // fixup_headers massages the page dump headers depending on // the 'format' argument, 'normal'(default) or 'forsvn'. @@ -104,9 +105,8 @@ $this->pagename = $page; $this->generateMessageId($mailified); - if ($format == 'forsvn') { - $this->fixup_headers_forsvn($mailified); - } else { // backup or normal + if ($format != 'forsvn') { + // format is backup or normal $this->fixup_headers($mailified); } @@ -114,23 +114,21 @@ // TODO: we need a way to hook into the generated headers, to override // Content-Type, Set-Cookie, Cache-control, ... $request->discardOutput(); // Hijack the http request from PhpWiki. - ob_end_clean(); // clean up after hijacking $request + // clean up after hijacking $request, + // but this causes files not to download right away without clicking resume in browser + //ob_end_clean(); //while (@ob_end_flush()); //debugging - $filename = FilenameForPage($page); + //windows formats filenames on its own even if they are urlencoded + $filename = FilenameForPage($page, $format); + header("Content-Type: application/octet-stream"); header("Content-disposition: attachment; filename=\"" . $filename . "\""); // We generate 3 Content-Type headers! first in loadsave, // then here and the mimified string $mailified also has it! // This one is correct and overwrites the others. - header("Content-Type: application/octet-stream; name=\"" - . $filename . "\"; charset=\"" . 'UTF-8' - . "\""); $request->checkValidators(); - // let $request provide last modified & etag - header("Content-Id: <" . $this->MessageId . ">"); // be nice to http keepalive~s header("Content-Length: " . strlen($mailified)); - // Here comes our prepared mime file echo $mailified; exit(); // noreturn! php exits. @@ -137,7 +135,7 @@ } // We are displaing inline preview in a WikiPage, so wrap the // text if it is too long--unless quoted-printable (TODO). - $mailified = wordwrap($mailified, 70); + $mailified = wordwrap($mailified, 78); $dlsvn = Button( array( //'page' => $page, @@ -144,7 +142,7 @@ 'action' => $this->getName(), 'format' => 'forsvn', 'download' => true), - _("Download for Subversion"), + _("Download as developer format"), $page ); $dl = Button( @@ -173,7 +171,7 @@ } if ($format == 'forsvn') { - $desc = _("(formatted for PhpWiki developers as pgsrc template, not for backing up)"); + $desc = _("(formatted for PhpWiki developers/translators as pgsrc template, not for backing up)"); $altpreviewbuttons = HTML( Button( array('action' => $this->getName()), @@ -302,38 +300,4 @@ $mailified = implode("\n", array_values($return)); } - - public function fixup_headers_forsvn(&$mailified) - { - $array = explode("\n", $mailified); - - // Massage headers to prepare for developer checkin to Subversion. - /* - Strip out all this junk: - author=MeMe; - version=74; - lastmodified=1041561552; - author_id=127.0.0.1; - hits=146; - */ - $killme = array("author", "version", "lastmodified", - "author_id", "hits", "owner", "acl"); - // UltraNasty, fixme: - foreach ($killme as $pattern) { - $array = preg_replace( - "/^\s\s$pattern\=.*;/", - /*$replacement =*/ - "zzzjunk", - $array - ); - } - // remove deleted values from array - for ($i = 0; $i < count($array); $i++) { - if (trim($array[$i]) != "zzzjunk") { //nasty, fixme - $return[] = $array[$i]; - } - } - - $mailified = implode("\n", $return); - } } Modified: trunk/lib/plugin/WikiForm.php =================================================================== --- trunk/lib/plugin/WikiForm.php 2025-02-14 23:22:37 UTC (rev 11108) +++ trunk/lib/plugin/WikiForm.php 2025-02-15 05:18:08 UTC (rev 11109) @@ -91,6 +91,17 @@ } $class = 'wikiadmin'; break; + case 'dumpsvn': + $input['name'] = 'directory'; + $input['required'] = 'required'; + if (!$default) { + $input['value'] = DEFAULT_DUMP_DIR; + } + if (!$buttontext) { + $buttontext = _("Dump Pages for developer"); + } + $class = 'wikiadmin'; + break; case 'dumphtml': $input['name'] = 'directory'; $input['required'] = 'required'; Modified: trunk/pgsrc/ReleaseNotes =================================================================== --- trunk/pgsrc/ReleaseNotes 2025-02-14 23:22:37 UTC (rev 11108) +++ trunk/pgsrc/ReleaseNotes 2025-02-15 05:18:08 UTC (rev 11109) @@ -1,4 +1,4 @@ -Date: Fri, 14 Feb 2025 23:09:08 +0000 +Date: Sat, 15 Feb 2025 04:58:20 +0000 Mime-Version: 1.0 (Produced by PhpWiki 1.6.5) Content-Type: application/x-phpwiki; pagename=ReleaseNotes; @@ -29,11 +29,26 @@ * Added 404 error to ~HttpClient.php which is displayed now in ~PhotoAlbumPlugin. Removed url of defunct example website that broke PhpWikiManual. -* **Important bugfix for PHP8.3 and newer:** Fixed garbled pages due to - depreciated errors. Also try harder to suppress depreciated errors on all - PHP versions. Set ##DEBUG=2## in ##config.ini## to view depreciated errors, - and ##DEBUG=1## for basic errors. Added hooks for css formatting of +* **Important bugfix for PHP8.3 and newer:** Fixed garbled display of pages + due to depreciated errors. Also try harder to suppress depreciated errors on + all PHP versions. Set ##DEBUG=2## in ##config.ini## to view depreciated + errors, and ##DEBUG=1## for basic errors. Added hooks for css formatting of depreciated errors. +* Improvements and bugfixes to loading and saving pages to filedumps: + - fixed download of individual dump files stalling in chrome (must always + click resume). + - fixed page lock status not always loading, especially for virgin wiki. + - Security: Prevented unlocking existing page when loading dumped file + without lock flag. + - Security: Page hits and acl are only loaded when page doesn't exist in + database. To do a complete restore of a page, delete it first. + - Fixed page metadata changes incorrectly being applied to database when + attempting to load a file and it was skipped. + - When a file is loaded the status now reports the loaded page lock status. + - Removed invalid http headers. + - Added dumpsvn action, mainly for developers and translators of pgsrc files. + - Fixed filenames sometimes not being urlencoded properly. + - Improved filtering of unknown page metadata being loaded into database. == 1.6.4 2024-03-13 Marc-Etienne Vargenau, Christof Meerwald == This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |