]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/plugin/PageDump.php
Remove safe_wordwrap, use standard PHP wordwrap
[SourceForge/phpwiki.git] / lib / plugin / PageDump.php
1 <?php // -*-php-*-
2 // rcs_id('$Id$');
3 /*
4  * Copyright (C) 2003 $ThePhpWikiProgrammingTeam
5  *
6  * This file is part of PhpWiki.
7  *
8  * PhpWiki is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * PhpWiki is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with PhpWiki; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
22
23 /**
24  * PhpWikiPlugin for PhpWiki developers to generate single page dumps
25  * for checking into cvs, or for users or the admin to produce a
26  * downloadable page dump of a single page.
27  *
28  * This plugin will also be useful to (semi-)automatically sync pages
29  * directly between two wikis. First the LoadFile function of
30  * PhpWikiAdministration needs to be updated to handle URLs again, and
31  * add loading capability from InterWiki addresses.
32  *
33  * Multiple revisions in one file handled by format=backup
34  *
35  * TODO: What about comments/summary field? quoted-printable?
36  *
37  * Usage:
38  *  Direct URL access:
39  *   http://...phpwiki/PageDump?page=HomePage?format=forcvs
40  *   http://...phpwiki/index.php?PageDump&page=HomePage
41  *   http://...phpwiki/index.php?PageDump&page=HomePage&download=1
42  *  Static:
43  *   <<PageDump page=HomePage>>
44  *  Dynamic form (put both on the page):
45  *   <<PageDump>>
46  *   <?plugin-form PageDump?>
47  *  Typical usage: as actionbar button
48  */
49
50 class WikiPlugin_PageDump
51 extends WikiPlugin
52 {
53     var $MessageId;
54
55     function getName() {
56         return _("PageDump");
57     }
58     function getDescription() {
59         return _("View a single page dump online.");
60     }
61
62     function getDefaultArguments() {
63         return array('s'    => false,
64                      'page' => '[pagename]',
65                      //'encoding' => 'binary', // 'binary', 'quoted-printable'
66                      'format' => false, // 'normal', 'forcvs', 'backup'
67                      // display within WikiPage or give a downloadable
68                      // raw pgsrc?
69                      'download' => false);
70     }
71
72     function run($dbi, $argstr, &$request, $basepage) {
73         extract($this->getArgs($argstr, $request));
74         // allow plugin-form
75         if (!empty($s))
76             $page = $s;
77         if (!$page)
78             return '';
79         if (! $dbi->isWikiPage($page) )
80             return fmt("Page %s not found.",
81                        WikiLink($page, 'unknown'));
82
83         // Check if user is allowed to get the Page.
84         if (!mayAccessPage ('view', $page)) {
85                 return $this->error(sprintf(_("Illegal access to page %s: no read access"),
86                                         $page));
87         }
88
89         $p = $dbi->getPage($page);
90         include_once("lib/loadsave.php");
91         $mailified = MailifyPage($p, ($format == 'backup') ? 99 : 1);
92
93         // fixup_headers massages the page dump headers depending on
94         // the 'format' argument, 'normal'(default) or 'forcvs'.
95         //
96         // Normal: Don't add X-Rcs-Id, add unique Message-Id, don't
97         // strip any fields from Content-Type.
98         //
99         // ForCVS: Add empty X-Rcs-Id, strip attributes from
100         // Content-Type field: "author", "version", "lastmodified",
101         // "author_id", "hits".
102
103         $this->pagename = $page;
104         $this->generateMessageId($mailified);
105         if ($format == 'forcvs')
106             $this->fixup_headers_forcvs($mailified);
107         else // backup or normal
108             $this->fixup_headers($mailified);
109
110         if ($download) {
111             // TODO: we need a way to hook into the generated headers, to override
112             // Content-Type, Set-Cookie, Cache-control, ...
113             $request->discardOutput(); // Hijack the http request from PhpWiki.
114             ob_end_clean();            // clean up after hijacking $request
115             //ob_end_flush(); //debugging
116             $filename = FilenameForPage($page);
117             Header("Content-disposition: attachment; filename=\""
118                    . $filename . "\"");
119             // Read charset from generated page itself.
120             // Inconsequential at the moment, since loadsave.php
121             // always generates headers.
122             $charset = $p->get('charset');
123             if (!$charset) $charset = $GLOBALS['charset'];
124             // We generate 3 Content-Type headers! first in loadsave,
125             // then here and the mimified string $mailified also has it!
126             // This one is correct and overwrites the others.
127             Header("Content-Type: application/octet-stream; name=\""
128                    . $filename . "\"; charset=\"" . $charset
129                    . "\"");
130             $request->checkValidators();
131             // let $request provide last modified & etag
132             Header("Content-Id: <" . $this->MessageId . ">");
133             // be nice to http keepalive~s
134             Header("Content-Length: " . strlen($mailified));
135
136             // Here comes our prepared mime file
137             echo $mailified;
138             exit; // noreturn! php exits.
139             return;
140         }
141         // We are displaing inline preview in a WikiPage, so wrap the
142         // text if it is too long--unless quoted-printable (TODO).
143         $mailified = wordwrap($mailified, 70);
144
145         $dlcvs = Button(array(//'page' => $page,
146                               'action' => $this->getName(),
147                               'format'=> 'forcvs',
148                               'download'=> true),
149                         _("Download for CVS"),
150                         $page);
151         $dl = Button(array(//'page' => $page,
152                            'action' => $this->getName(),
153                            'download'=> true),
154                      _("Download for backup"),
155                      $page);
156         $dlall = Button(array(//'page' => $page,
157                            'action' => $this->getName(),
158                            'format'=> 'backup',
159                            'download'=> true),
160                      _("Download all revisions for backup"),
161                      $page);
162
163         $h2 = HTML::h2(fmt("Preview: Page dump of %s",
164                            WikiLink($page, 'auto')));
165         global $WikiTheme;
166         if (!$Sep = $WikiTheme->getButtonSeparator())
167             $Sep = " ";
168
169         if ($format == 'forcvs') {
170             $desc = _("(formatted for PhpWiki developers as pgsrc template, not for backing up)");
171             $altpreviewbuttons = HTML(
172                                       Button(array('action' => $this->getName()),
173                                              _("Preview as normal format"),
174                                              $page),
175                                       $Sep,
176                                       Button(array(
177                                                    'action' => $this->getName(),
178                                                    'format'=> 'backup'),
179                                              _("Preview as backup format"),
180                                              $page));
181         }
182         elseif ($format == 'backup') {
183             $desc = _("(formatted for backing up: all revisions)"); // all revisions
184             $altpreviewbuttons = HTML(
185                                       Button(array('action' => $this->getName(),
186                                                    'format'=> 'forcvs'),
187                                              _("Preview as developer format"),
188                                              $page),
189                                       $Sep,
190                                       Button(array(
191                                                    'action' => $this->getName(),
192                                                    'format'=> ''),
193                                              _("Preview as normal format"),
194                                              $page));
195         } else {
196             $desc = _("(normal formatting: latest revision only)");
197             $altpreviewbuttons = HTML(
198                                       Button(array('action' => $this->getName(),
199                                                    'format'=> 'forcvs'),
200                                              _("Preview as developer format"),
201                                              $page),
202                                       $Sep,
203                                       Button(array(
204                                                    'action' => $this->getName(),
205                                                    'format'=> 'backup'),
206                                              _("Preview as backup format"),
207                                              $page));
208         }
209         $warning = HTML(
210 _("Please use one of the downloadable versions rather than copying and pasting from the above preview.")
211 . " " .
212 _("The wordwrap of the preview doesn't take nested markup or list indentation into consideration!")
213 . " ",
214 HTML::em(
215 _("PhpWiki developers should manually inspect the downloaded file for nested markup before rewrapping with emacs and checking into CVS.")
216          )
217                         );
218
219         return HTML($h2, HTML::em($desc),
220                     HTML::pre($mailified),
221                     $altpreviewbuttons,
222                     HTML::div(array('class' => 'errors'),
223                               HTML::strong(_("Warning:")),
224                               " ", $warning),
225                     $dl, $Sep, $dlall, $Sep, $dlcvs
226                     );
227     }
228
229     // function handle_plugin_args_cruft(&$argstr, &$args) {
230     // }
231
232     function generateMessageId($mailified) {
233         $array = explode("\n", $mailified);
234         // Extract lastmodifed from mailified document for Content-Id
235         // and/or Message-Id header, NOT from DB (page could have been
236         // edited by someone else since we started).
237         $m1 = preg_grep("/^\s+lastmodified\=(.*);/", $array);
238         $m1 = array_values($m1); //reset resulting keys
239         unset($array);
240         $m2 = preg_split("/(^\s+lastmodified\=)|(;)/", $m1[0], 2,
241                          PREG_SPLIT_NO_EMPTY);
242
243         // insert message id into actual message when appropriate, NOT
244         // into http header should be part of fixup_headers, in the
245         // format:
246         // <abbrphpwikiversion.mtimeepochTZ%InterWikiLinktothispage@hostname>
247         // Hopefully this provides a unique enough identifier without
248         // using md5. Even though this particular wiki may not
249         // actually be part of InterWiki, including this info provides
250         // the wiki name and name of the page which is being
251         // represented as a text message.
252         $this->MessageId = implode('', explode('.', PHPWIKI_VERSION))
253             . "-" . $m2[0] . date("O")
254             //. "-". rawurlencode(WIKI_NAME.":" . $request->getURLtoSelf())
255             . "-". rawurlencode(WIKI_NAME.":" . $this->pagename)
256             . "@". rawurlencode(SERVER_NAME);
257     }
258
259     function fixup_headers(&$mailified) {
260         $return = explode("\n", $mailified);
261
262         // Leave message intact for backing up, just add Message-Id header before transmitting.
263         $item_to_insert = "Message-Id: <" . $this->MessageId .">";
264         $insert_into_key_position = 2;
265         $returnval_ignored = array_splice($return,
266                                           $insert_into_key_position,
267                                           0, $item_to_insert);
268
269         $mailified = implode("\n", array_values($return));
270     }
271
272     function fixup_headers_forcvs(&$mailified) {
273         $array = explode("\n", $mailified);
274
275         // Massage headers to prepare for developer checkin to CVS.
276         $item_to_insert = "X-Rcs-Id: \$Id\$";
277         $insert_into_key_position = 2;
278         $returnval_ignored = array_splice($array,
279                                           $insert_into_key_position,
280                                           0, $item_to_insert);
281
282         $item_to_insert = "  pgsrc_version=\"2 \$Revision\$\";";
283         $insert_into_key_position = 5;
284         $returnval_ignored = array_splice($array,
285                                           $insert_into_key_position,
286                                           0, $item_to_insert);
287         /*
288             Strip out all this junk:
289             author=MeMe;
290             version=74;
291             lastmodified=1041561552;
292             author_id=127.0.0.1;
293             hits=146;
294         */
295         $killme = array("author", "version", "lastmodified",
296                         "author_id", "hits", "owner", "acl");
297         // UltraNasty, fixme:
298         foreach ($killme as $pattern) {
299             $array = preg_replace("/^\s\s$pattern\=.*;/",
300                                   /*$replacement =*/"zzzjunk", $array);
301         }
302         // remove deleted values from array
303         for ($i = 0; $i < count($array); $i++ ) {
304             if(trim($array[$i]) != "zzzjunk") { //nasty, fixme
305             //trigger_error("'$array[$i]'");//debugging
306                 $return[] = $array[$i];
307             }
308         }
309
310         $mailified = implode("\n", $return);
311     }
312 };
313
314 // For emacs users
315 // Local Variables:
316 // mode: php
317 // tab-width: 8
318 // c-basic-offset: 4
319 // c-hanging-comment-ender-p: nil
320 // indent-tabs-mode: nil
321 // End:
322 ?>