4 * PhpWikiPlugin for PhpWiki developers to generate single page dumps
5 * for checking into cvs, or for users or the admin to produce a
6 * downloadable page dump of a single page.
8 * This plugin will also be useful to (semi-)automatically sync pages
9 * directly between two wikis. First the LoadFile function of
10 * PhpWikiAdministration needs to be updated to handle URLs again, and
11 * add loading capability from InterWiki addresses.
13 * Multiple revisions in one file handled by format=backup
15 * TODO: What about comments/summary field? quoted-printable?
19 * http://...phpwiki/PageDump?page=HomePage?format=forcvs
20 * http://...phpwiki/index.php?PageDump&page=HomePage
21 * http://...phpwiki/index.php?PageDump&page=HomePage&download=1
23 * <?plugin PageDump page=HomePage?>
24 * Dynamic form (put both on the page):
26 * <?plugin-form PageDump?>
27 * Typical usage: as actionbar button
30 class WikiPlugin_PageDump
38 function getDescription() {
39 return _("View a single page dump online.");
42 function getVersion() {
43 return preg_replace("/[Revision: $]/", '',
44 "\$Revision: 1.20 $");
47 function getDefaultArguments() {
48 return array('s' => false,
49 'page' => '[pagename]',
50 //'encoding' => 'binary', // 'binary', 'quoted-printable'
51 'format' => false, // 'normal', 'forcvs', 'backup'
52 // display within WikiPage or give a downloadable
57 function run($dbi, $argstr, &$request, $basepage) {
58 extract($this->getArgs($argstr, $request));
64 if (! $dbi->isWikiPage($page) )
65 return fmt("Page %s not found.",
66 WikiLink($page, 'unknown'));
68 $p = $dbi->getPage($page);
69 include_once("lib/loadsave.php");
70 $mailified = MailifyPage($p, ($format == 'backup') ? 99 : 1);
72 // fixup_headers massages the page dump headers depending on
73 // the 'format' argument, 'normal'(default) or 'forcvs'.
75 // Normal: Don't add X-Rcs-Id, add unique Message-Id, don't
76 // strip any fields from Content-Type.
78 // ForCVS: Add empty X-Rcs-Id, strip attributes from
79 // Content-Type field: "author", "version", "lastmodified",
80 // "author_id", "hits".
82 $this->pagename = $page;
83 $this->generateMessageId($mailified);
84 if ($format == 'forcvs')
85 $this->fixup_headers_forcvs($mailified);
86 else // backup or normal
87 $this->fixup_headers($mailified);
90 // TODO: we need a way to hook into the generated headers, to override
91 // Content-Type, Set-Cookie, Cache-control, ...
92 $request->discardOutput(); // Hijack the http request from PhpWiki.
93 ob_end_clean(); // clean up after hijacking $request
94 //ob_end_flush(); //debugging
95 $filename = FilenameForPage($page);
96 Header("Content-disposition: attachment; filename=\""
98 // Read charset from generated page itself.
99 // Inconsequential at the moment, since loadsave.php
100 // always generates headers.
101 $charset = $p->get('charset');
102 if (!$charset) $charset = $GLOBALS['charset'];
103 // We generate 3 Content-Type headers! first in loadsave,
104 // then here and the mimified string $mailified also has it!
105 // This one is correct and overwrites the others.
106 Header("Content-Type: application/octet-stream; name=\""
107 . $filename . "\"; charset=\"" . $charset
109 $request->checkValidators();
110 // let $request provide last modified & etag
111 Header("Content-Id: <" . $this->MessageId . ">");
112 // be nice to http keepalive~s
113 Header("Content-Length: " . strlen($mailified));
115 // Here comes our prepared mime file
117 exit; // noreturn! php exits.
120 // We are displaing inline preview in a WikiPage, so wrap the
121 // text if it is too long--unless quoted-printable (TODO).
122 $mailified = safe_wordwrap($mailified, 70);
124 $dlcvs = Button(array(//'page' => $page,
125 'action' => $this->getName(),
128 _("Download for CVS"),
130 $dl = Button(array(//'page' => $page,
131 'action' => $this->getName(),
133 _("Download for backup"),
135 $dlall = Button(array(//'page' => $page,
136 'action' => $this->getName(),
139 _("Download all revisions for backup"),
142 $h2 = HTML::h2(fmt("Preview: Page dump of %s",
143 WikiLink($page, 'auto')));
145 if (!$Sep = $WikiTheme->getButtonSeparator())
148 if ($format == 'forcvs') {
149 $desc = _("(formatted for PhpWiki developers as pgsrc template, not for backing up)");
150 $altpreviewbuttons = HTML(
151 Button(array('action' => $this->getName()),
152 _("Preview as normal format"),
156 'action' => $this->getName(),
157 'format'=> 'backup'),
158 _("Preview as backup format"),
161 elseif ($format == 'backup') {
162 $desc = _("(formatted for backing up: all revisions)"); // all revisions
163 $altpreviewbuttons = HTML(
164 Button(array('action' => $this->getName(),
165 'format'=> 'forcvs'),
166 _("Preview as developer format"),
170 'action' => $this->getName(),
172 _("Preview as normal format"),
175 $desc = _("(normal formatting: latest revision only)");
176 $altpreviewbuttons = HTML(
177 Button(array('action' => $this->getName(),
178 'format'=> 'forcvs'),
179 _("Preview as developer format"),
183 'action' => $this->getName(),
184 'format'=> 'backup'),
185 _("Preview as backup format"),
189 _("Please use one of the downloadable versions rather than copying and pasting from the above preview.")
191 _("The wordwrap of the preview doesn't take nested markup or list indentation into consideration!")
194 _("PhpWiki developers should manually inspect the downloaded file for nested markup before rewrapping with emacs and checking into CVS.")
198 return HTML($h2, HTML::em($desc),
199 HTML::pre($mailified),
201 HTML::div(array('class' => 'errors'),
202 HTML::strong(_("Warning:")),
204 $dl, $Sep, $dlall, $Sep, $dlcvs
208 // function handle_plugin_args_cruft(&$argstr, &$args) {
211 function generateMessageId($mailified) {
212 $array = explode("\n", $mailified);
213 // Extract lastmodifed from mailified document for Content-Id
214 // and/or Message-Id header, NOT from DB (page could have been
215 // edited by someone else since we started).
216 $m1 = preg_grep("/^\s+lastmodified\=(.*);/", $array);
217 $m1 = array_values($m1); //reset resulting keys
219 $m2 = preg_split("/(^\s+lastmodified\=)|(;)/", $m1[0], 2,
220 PREG_SPLIT_NO_EMPTY);
222 // insert message id into actual message when appropriate, NOT
223 // into http header should be part of fixup_headers, in the
225 // <abbrphpwikiversion.mtimeepochTZ%InterWikiLinktothispage@hostname>
226 // Hopefully this provides a unique enough identifier without
227 // using md5. Even though this particular wiki may not
228 // actually be part of InterWiki, including this info provides
229 // the wiki name and name of the page which is being
230 // represented as a text message.
231 $this->MessageId = implode('', explode('.', PHPWIKI_VERSION))
232 . "-" . $m2[0] . date("O")
233 //. "-". rawurlencode(WIKI_NAME.":" . $request->getURLtoSelf())
234 . "-". rawurlencode(WIKI_NAME.":" . $this->pagename)
235 . "@". rawurlencode(SERVER_NAME);
238 function fixup_headers(&$mailified) {
239 $return = explode("\n", $mailified);
241 // Leave message intact for backing up, just add Message-Id header before transmitting.
242 $item_to_insert = "Message-Id: <" . $this->MessageId .">";
243 $insert_into_key_position = 2;
244 $returnval_ignored = array_splice($return,
245 $insert_into_key_position,
248 $mailified = implode("\n", array_values($return));
251 function fixup_headers_forcvs(&$mailified) {
252 $array = explode("\n", $mailified);
254 // Massage headers to prepare for developer checkin to CVS.
255 $item_to_insert = "X-Rcs-Id: \$Id\$";
256 $insert_into_key_position = 2;
257 $returnval_ignored = array_splice($array,
258 $insert_into_key_position,
261 $item_to_insert = " pgsrc_version=\"2 \$Revision\$\";";
262 $insert_into_key_position = 5;
263 $returnval_ignored = array_splice($array,
264 $insert_into_key_position,
267 Strip out all this junk:
270 lastmodified=1041561552;
274 $killme = array("author", "version", "lastmodified",
275 "author_id", "hits", "owner", "acl");
276 // UltraNasty, fixme:
277 foreach ($killme as $pattern) {
278 $array = preg_replace("/^\s\s$pattern\=.*;/",
279 /*$replacement =*/"zzzjunk", $array);
281 // remove deleted values from array
282 for ($i = 0; $i < count($array); $i++ ) {
283 if(trim($array[$i]) != "zzzjunk") { //nasty, fixme
284 //trigger_error("'$array[$i]'");//debugging
285 $return[] = $array[$i];
289 $mailified = implode("\n", $return);
293 // $Log: not supported by cvs2svn $
294 // Revision 1.19 2007/01/03 21:23:40 rurban
295 // fix Content-Type header to application/octet-stream to avoid pesty .txt suffixes on windows clients
297 // Revision 1.18 2004/10/14 19:19:34 rurban
298 // loadsave: check if the dumped file will be accessible from outside.
299 // and some other minor fixes. (cvsclient native not yet ready)
301 // Revision 1.17 2004/09/16 07:49:01 rurban
302 // use the page charset instead if the global one on download
303 // (need to clarify header order, since we print the same header type 3 times!)
304 // wordwrap workaround (security concern)
306 // Revision 1.16 2004/07/01 06:31:23 rurban
309 // Revision 1.15 2004/06/29 10:09:06 rurban
312 // Revision 1.14 2004/06/29 10:07:40 rurban
313 // added dump of all revisions by format=backup (screen and download)
315 // Revision 1.13 2004/06/17 10:39:18 rurban
316 // fix reverse translation of possible actionpage
318 // Revision 1.12 2004/06/16 13:32:43 rurban
319 // fix urlencoding of pagename in PageDump buttons
321 // Revision 1.11 2004/06/14 11:31:39 rurban
322 // renamed global $Theme to $WikiTheme (gforge nameclash)
323 // inherit PageList default options from PageList
324 // default sortby=pagename
325 // use options in PageList_Selectable (limit, sortby, ...)
326 // added action revert, with button at action=diff
327 // added option regex to WikiAdminSearchReplace
329 // Revision 1.10 2004/06/07 22:28:05 rurban
330 // add acl field to mimified dump
332 // Revision 1.9 2004/06/07 19:50:41 rurban
333 // add owner field to mimified dump
335 // Revision 1.8 2004/05/25 12:43:29 rurban
336 // ViewSource link, better actionpage usage
338 // Revision 1.7 2004/05/04 17:21:06 rurban
339 // revert previous patch
341 // Revision 1.6 2004/05/03 20:44:55 rurban
342 // fixed gettext strings
343 // new SqlResult plugin
344 // _WikiTranslation: fixed init_locale
346 // Revision 1.5 2004/05/03 17:42:44 rurban
347 // fix cvs tags: "$tag$" => "$tag: $"
349 // Revision 1.4 2004/04/18 01:11:52 rurban
350 // more numeric pagename fixes.
351 // fixed action=upload with merge conflict warnings.
352 // charset changed from constant to global (dynamic utf-8 switching)
354 // Revision 1.3 2004/02/17 12:11:36 rurban
355 // added missing 4th basepage arg at plugin->run() to almost all plugins. This caused no harm so far, because it was silently dropped on normal usage. However on plugin internal ->run invocations it failed. (InterWikiSearch, IncludeSiteMap, ...)
357 // Revision 1.2 2003/12/12 01:08:30 carstenklapp
358 // QuickFix for invalid Message-Id header format.
360 // Revision 1.1 2003/12/12 00:52:55 carstenklapp
361 // New feature: Plugin to download page dumps of individual pages. In the
362 // future this could be used as a rudimentary way to sync pages between
364 // Internal changes: enhanced and renamed from the experimental
365 // _MailifyPage plugin.
367 // Revision 1.3 2003/11/16 00:11:25 carstenklapp
368 // Fixed previous Log comment interfering with PHP (sorry).
369 // Improved error handling.
371 // Revision 1.2 2003/11/15 23:37:51 carstenklapp
372 // Enhanced plugin to allow invocation with \<\?plugin-form PageDump\?\>.
374 // Revision 1.1 2003/02/20 18:03:04 carstenklapp
375 // New experimental WikiPlugin for internal use only by PhpWiki developers.
383 // c-hanging-comment-ender-p: nil
384 // indent-tabs-mode: nil