]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/plugin/PageDump.php
use the page charset instead if the global one on download
[SourceForge/phpwiki.git] / lib / plugin / PageDump.php
1 <?php // -*-php-*-
2 rcs_id('$Id: PageDump.php,v 1.17 2004-09-16 07:49:01 rurban Exp $');
3 /**
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.
7  * 
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.
12
13  * Multiple revisions in one file handled by format=backup
14  *
15  * TODO: What about comments/summary field? quoted-printable?
16  *
17  * Usage:
18  *  Direct URL access:
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
22  *  Static:
23  *   <?plugin PageDump page=HomePage?>
24  *  Dynamic form (put both on the page):
25  *   <?plugin PageDump?>
26  *   <?plugin-form PageDump?>
27  *  Typical usage: as actionbar button
28  */
29
30 class WikiPlugin_PageDump
31 extends WikiPlugin
32 {
33     var $MessageId;
34
35     function getName() {
36         return _("PageDump");
37     }
38     function getDescription() {
39         return _("View a single page dump online.");
40     }
41
42     function getVersion() {
43         return preg_replace("/[Revision: $]/", '',
44                             "\$Revision: 1.17 $");
45     }
46
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
53                      // raw pgsrc?
54                      'download' => false);
55     }
56
57     function run($dbi, $argstr, &$request, $basepage) {
58         extract($this->getArgs($argstr, $request));
59         // allow plugin-form
60         if (!empty($s))
61             $page = $s;
62         if (!$page)
63             return '';
64         if (! $dbi->isWikiPage($page) )
65             return fmt("Page %s not found.",
66                        WikiLink($page, 'unknown'));
67
68         $p = $dbi->getPage($page);
69         include_once("lib/loadsave.php");
70         $mailified = MailifyPage($p, ($format == 'backup') ? 99 : 1);
71
72         // fixup_headers massages the page dump headers depending on
73         // the 'format' argument, 'normal'(default) or 'forcvs'.
74         //
75         // Normal: Don't add X-Rcs-Id, add unique Message-Id, don't
76         // strip any fields from Content-Type.
77         //
78         // ForCVS: Add empty X-Rcs-Id, strip attributes from
79         // Content-Type field: "author", "version", "lastmodified",
80         // "author_id", "hits".
81
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);
88
89         if ($download) {
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             Header("Content-disposition: attachment; filename=\""
96                    . FilenameForPage($page) . "\"");
97             // Read charset from generated page itself.
98             // Inconsequential at the moment, since loadsave.php
99             // always generates headers
100             $charset = $p->get('charset');
101             if (!$charset) $charset = $GLOBALS['charset'];
102             // We generate 3 Content-Type headers! first in loadsave,
103             // then here and the mimified string $mailified also has it!
104             Header("Content-Type: text/plain; name=\""
105                    . FilenameForPage($page) . "\"; charset=\"" . $charset
106                    . "\"");
107             $request->checkValidators();
108             // let $request provide last modifed & etag
109             Header("Content-Id: <" . $this->MessageId . ">");
110             // be nice to http keepalive~s
111             // FIXME: he length is wrong BTW. must strip the header.
112             Header("Content-Length: " . strlen($mailified)); 
113
114             // Here comes our prepared mime file
115             echo $mailified;
116             exit; // noreturn! php exits.
117             return;
118         }
119         // We are displaing inline preview in a WikiPage, so wrap the
120         // text if it is too long--unless quoted-printable (TODO).
121         $mailified = safe_wordwrap($mailified, 70);
122
123         $dlcvs = Button(array(//'page' => $page,
124                               'action' => $this->getName(),
125                               'format'=> 'forcvs',
126                               'download'=> true),
127                         _("Download for CVS"),
128                         $page);
129         $dl = Button(array(//'page' => $page,
130                            'action' => $this->getName(),
131                            'download'=> true),
132                      _("Download for backup"),
133                      $page);
134         $dlall = Button(array(//'page' => $page,
135                            'action' => $this->getName(),
136                            'format'=> 'backup',
137                            'download'=> true),
138                      _("Download all revisions for backup"),
139                      $page);
140
141         $h2 = HTML::h2(fmt("Preview: Page dump of %s",
142                            WikiLink($page, 'auto')));
143         global $WikiTheme;
144         if (!$Sep = $WikiTheme->getButtonSeparator())
145             $Sep = " ";
146
147         if ($format == 'forcvs') {
148             $desc = _("(formatted for PhpWiki developers as pgsrc template, not for backing up)");
149             $altpreviewbuttons = HTML(
150                                       Button(array('action' => $this->getName()),
151                                              _("Preview as normal format"),
152                                              $page),
153                                       $Sep,
154                                       Button(array(
155                                                    'action' => $this->getName(),
156                                                    'format'=> 'backup'),
157                                              _("Preview as backup format"),
158                                              $page));
159         }
160         elseif ($format == 'backup') {
161             $desc = _("(formatted for backing up: all revisions)"); // all revisions
162             $altpreviewbuttons = HTML(
163                                       Button(array('action' => $this->getName(),
164                                                    'format'=> 'forcvs'),
165                                              _("Preview as developer format"),
166                                              $page),
167                                       $Sep,
168                                       Button(array(
169                                                    'action' => $this->getName(),
170                                                    'format'=> ''),
171                                              _("Preview as normal format"),
172                                              $page));
173         } else {
174             $desc = _("(normal formatting: latest revision only)");
175             $altpreviewbuttons = HTML(
176                                       Button(array('action' => $this->getName(),
177                                                    'format'=> 'forcvs'),
178                                              _("Preview as developer format"),
179                                              $page),
180                                       $Sep,
181                                       Button(array(
182                                                    'action' => $this->getName(),
183                                                    'format'=> 'backup'),
184                                              _("Preview as backup format"),
185                                              $page));
186         }
187         $warning = HTML(
188 _("Please use one of the downloadable versions rather than copying and pasting from the above preview.")
189 . " " .
190 _("The wordwrap of the preview doesn't take nested markup or list indentation into consideration!")
191 . " ",
192 HTML::em(
193 _("PhpWiki developers should manually inspect the downloaded file for nested markup before rewrapping with emacs and checking into CVS.")
194          )
195                         );
196
197         return HTML($h2, HTML::em($desc),
198                     HTML::pre($mailified),
199                     $altpreviewbuttons,
200                     HTML::div(array('class' => 'errors'),
201                               HTML::strong(_("Warning:")),
202                               " ", $warning),
203                     $dl, $Sep, $dlall, $Sep, $dlcvs
204                     );
205     }
206
207     // function handle_plugin_args_cruft(&$argstr, &$args) {
208     // }
209
210     function generateMessageId($mailified) {
211         $array = explode("\n", $mailified);
212         // Extract lastmodifed from mailified document for Content-Id
213         // and/or Message-Id header, NOT from DB (page could have been
214         // edited by someone else since we started).
215         $m1 = preg_grep("/^\s+lastmodified\=(.*);/", $array);
216         $m1 = array_values($m1); //reset resulting keys
217         unset($array);
218         $m2 = preg_split("/(^\s+lastmodified\=)|(;)/", $m1[0], 2,
219                          PREG_SPLIT_NO_EMPTY);
220
221         // insert message id into actual message when appropriate, NOT
222         // into http header should be part of fixup_headers, in the
223         // format:
224         // <abbrphpwikiversion.mtimeepochTZ%InterWikiLinktothispage@hostname>
225         // Hopefully this provides a unique enough identifier without
226         // using md5. Even though this particular wiki may not
227         // actually be part of InterWiki, including this info provides
228         // the wiki name and name of the page which is being
229         // represented as a text message.
230         $this->MessageId = implode('', explode('.', PHPWIKI_VERSION))
231             . "-" . $m2[0] . date("O")
232             //. "-". rawurlencode(WIKI_NAME.":" . $request->getURLtoSelf())
233             . "-". rawurlencode(WIKI_NAME.":" . $this->pagename)
234             . "@". rawurlencode(SERVER_NAME);
235     }
236
237     function fixup_headers(&$mailified) {
238         $return = explode("\n", $mailified);
239
240         // Leave message intact for backing up, just add Message-Id header before transmitting.
241         $item_to_insert = "Message-Id: <" . $this->MessageId .">";
242         $insert_into_key_position = 2;
243         $returnval_ignored = array_splice($return,
244                                           $insert_into_key_position,
245                                           0, $item_to_insert);
246
247         $mailified = implode("\n", array_values($return));
248     }
249
250     function fixup_headers_forcvs(&$mailified) {
251         $array = explode("\n", $mailified);
252
253         // Massage headers to prepare for developer checkin to CVS.
254         $item_to_insert = "X-Rcs-Id: \$Id\$";
255         $insert_into_key_position = 2;
256         $returnval_ignored = array_splice($array,
257                                           $insert_into_key_position,
258                                           0, $item_to_insert);
259
260         $item_to_insert = "  pgsrc_version=\"2 \$Revision\$\";";
261         $insert_into_key_position = 5;
262         $returnval_ignored = array_splice($array,
263                                           $insert_into_key_position,
264                                           0, $item_to_insert);
265         /*
266             Strip out all this junk:
267             author=MeMe;
268             version=74;
269             lastmodified=1041561552;
270             author_id=127.0.0.1;
271             hits=146;
272         */
273         $killme = array("author", "version", "lastmodified",
274                         "author_id", "hits", "owner", "acl");
275         // UltraNasty, fixme:
276         foreach ($killme as $pattern) {
277             $array = preg_replace("/^\s\s$pattern\=.*;/",
278                                   /*$replacement =*/"zzzjunk", $array);
279         }
280         // remove deleted values from array
281         for ($i = 0; $i < count($array); $i++ ) {
282             if(trim($array[$i]) != "zzzjunk") { //nasty, fixme
283             //trigger_error("'$array[$i]'");//debugging
284                 $return[] =$array[$i];
285             }
286         }
287
288         $mailified = implode("\n", $return);
289     }
290 };
291
292 // $Log: not supported by cvs2svn $
293 // Revision 1.16  2004/07/01 06:31:23  rurban
294 // doc upcase only
295 //
296 // Revision 1.15  2004/06/29 10:09:06  rurban
297 // better desc
298 //
299 // Revision 1.14  2004/06/29 10:07:40  rurban
300 // added dump of all revisions by format=backup (screen and download)
301 //
302 // Revision 1.13  2004/06/17 10:39:18  rurban
303 // fix reverse translation of possible actionpage
304 //
305 // Revision 1.12  2004/06/16 13:32:43  rurban
306 // fix urlencoding of pagename in PageDump buttons
307 //
308 // Revision 1.11  2004/06/14 11:31:39  rurban
309 // renamed global $Theme to $WikiTheme (gforge nameclash)
310 // inherit PageList default options from PageList
311 //   default sortby=pagename
312 // use options in PageList_Selectable (limit, sortby, ...)
313 // added action revert, with button at action=diff
314 // added option regex to WikiAdminSearchReplace
315 //
316 // Revision 1.10  2004/06/07 22:28:05  rurban
317 // add acl field to mimified dump
318 //
319 // Revision 1.9  2004/06/07 19:50:41  rurban
320 // add owner field to mimified dump
321 //
322 // Revision 1.8  2004/05/25 12:43:29  rurban
323 // ViewSource link, better actionpage usage
324 //
325 // Revision 1.7  2004/05/04 17:21:06  rurban
326 // revert previous patch
327 //
328 // Revision 1.6  2004/05/03 20:44:55  rurban
329 // fixed gettext strings
330 // new SqlResult plugin
331 // _WikiTranslation: fixed init_locale
332 //
333 // Revision 1.5  2004/05/03 17:42:44  rurban
334 // fix cvs tags: "$tag$" => "$tag: $"
335 //
336 // Revision 1.4  2004/04/18 01:11:52  rurban
337 // more numeric pagename fixes.
338 // fixed action=upload with merge conflict warnings.
339 // charset changed from constant to global (dynamic utf-8 switching)
340 //
341 // Revision 1.3  2004/02/17 12:11:36  rurban
342 // 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, ...)
343 //
344 // Revision 1.2  2003/12/12 01:08:30  carstenklapp
345 // QuickFix for invalid Message-Id header format.
346 //
347 // Revision 1.1  2003/12/12 00:52:55  carstenklapp
348 // New feature: Plugin to download page dumps of individual pages. In the
349 // future this could be used as a rudimentary way to sync pages between
350 // wikis.
351 // Internal changes: enhanced and renamed from the experimental
352 // _MailifyPage plugin.
353 //
354 // Revision 1.3  2003/11/16 00:11:25  carstenklapp
355 // Fixed previous Log comment interfering with PHP (sorry).
356 // Improved error handling.
357 //
358 // Revision 1.2  2003/11/15 23:37:51  carstenklapp
359 // Enhanced plugin to allow invocation with \<\?plugin-form PageDump\?\>.
360 //
361 // Revision 1.1  2003/02/20 18:03:04  carstenklapp
362 // New experimental WikiPlugin for internal use only by PhpWiki developers.
363 //
364
365 // For emacs users
366 // Local Variables:
367 // mode: php
368 // tab-width: 8
369 // c-basic-offset: 4
370 // c-hanging-comment-ender-p: nil
371 // indent-tabs-mode: nil
372 // End:
373 ?>