]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/plugin/PageDump.php
Use WikiPlugin_WikiAdminSelect::getDefaultArguments instead of PageList::supportedArg...
[SourceForge/phpwiki.git] / lib / plugin / PageDump.php
1 <?php // -*-php-*-
2 rcs_id('$Id$');
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$");
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         // Check if user is allowed to get the Page.
69         if (!mayAccessPage ('view', $page)) {
70                 return $this->error(sprintf(_("Illegal access to page %s: no read access"),
71                                         $page));
72         }
73         
74         $p = $dbi->getPage($page);
75         include_once("lib/loadsave.php");
76         $mailified = MailifyPage($p, ($format == 'backup') ? 99 : 1);
77
78         // fixup_headers massages the page dump headers depending on
79         // the 'format' argument, 'normal'(default) or 'forcvs'.
80         //
81         // Normal: Don't add X-Rcs-Id, add unique Message-Id, don't
82         // strip any fields from Content-Type.
83         //
84         // ForCVS: Add empty X-Rcs-Id, strip attributes from
85         // Content-Type field: "author", "version", "lastmodified",
86         // "author_id", "hits".
87
88         $this->pagename = $page;
89         $this->generateMessageId($mailified);
90         if ($format == 'forcvs')
91             $this->fixup_headers_forcvs($mailified);
92         else // backup or normal
93             $this->fixup_headers($mailified);
94
95         if ($download) {
96             // TODO: we need a way to hook into the generated headers, to override 
97             // Content-Type, Set-Cookie, Cache-control, ...
98             $request->discardOutput(); // Hijack the http request from PhpWiki.
99             ob_end_clean();            // clean up after hijacking $request
100             //ob_end_flush(); //debugging
101             $filename = FilenameForPage($page);
102             Header("Content-disposition: attachment; filename=\""
103                    . $filename . "\"");
104             // Read charset from generated page itself.
105             // Inconsequential at the moment, since loadsave.php
106             // always generates headers.
107             $charset = $p->get('charset');
108             if (!$charset) $charset = $GLOBALS['charset'];
109             // We generate 3 Content-Type headers! first in loadsave,
110             // then here and the mimified string $mailified also has it!
111             // This one is correct and overwrites the others.
112             Header("Content-Type: application/octet-stream; name=\""
113                    . $filename . "\"; charset=\"" . $charset
114                    . "\"");
115             $request->checkValidators();
116             // let $request provide last modified & etag
117             Header("Content-Id: <" . $this->MessageId . ">");
118             // be nice to http keepalive~s
119             Header("Content-Length: " . strlen($mailified)); 
120
121             // Here comes our prepared mime file
122             echo $mailified;
123             exit; // noreturn! php exits.
124             return;
125         }
126         // We are displaing inline preview in a WikiPage, so wrap the
127         // text if it is too long--unless quoted-printable (TODO).
128         $mailified = safe_wordwrap($mailified, 70);
129
130         $dlcvs = Button(array(//'page' => $page,
131                               'action' => $this->getName(),
132                               'format'=> 'forcvs',
133                               'download'=> true),
134                         _("Download for CVS"),
135                         $page);
136         $dl = Button(array(//'page' => $page,
137                            'action' => $this->getName(),
138                            'download'=> true),
139                      _("Download for backup"),
140                      $page);
141         $dlall = Button(array(//'page' => $page,
142                            'action' => $this->getName(),
143                            'format'=> 'backup',
144                            'download'=> true),
145                      _("Download all revisions for backup"),
146                      $page);
147
148         $h2 = HTML::h2(fmt("Preview: Page dump of %s",
149                            WikiLink($page, 'auto')));
150         global $WikiTheme;
151         if (!$Sep = $WikiTheme->getButtonSeparator())
152             $Sep = " ";
153
154         if ($format == 'forcvs') {
155             $desc = _("(formatted for PhpWiki developers as pgsrc template, not for backing up)");
156             $altpreviewbuttons = HTML(
157                                       Button(array('action' => $this->getName()),
158                                              _("Preview as normal format"),
159                                              $page),
160                                       $Sep,
161                                       Button(array(
162                                                    'action' => $this->getName(),
163                                                    'format'=> 'backup'),
164                                              _("Preview as backup format"),
165                                              $page));
166         }
167         elseif ($format == 'backup') {
168             $desc = _("(formatted for backing up: all revisions)"); // all revisions
169             $altpreviewbuttons = HTML(
170                                       Button(array('action' => $this->getName(),
171                                                    'format'=> 'forcvs'),
172                                              _("Preview as developer format"),
173                                              $page),
174                                       $Sep,
175                                       Button(array(
176                                                    'action' => $this->getName(),
177                                                    'format'=> ''),
178                                              _("Preview as normal format"),
179                                              $page));
180         } else {
181             $desc = _("(normal formatting: latest revision only)");
182             $altpreviewbuttons = HTML(
183                                       Button(array('action' => $this->getName(),
184                                                    'format'=> 'forcvs'),
185                                              _("Preview as developer format"),
186                                              $page),
187                                       $Sep,
188                                       Button(array(
189                                                    'action' => $this->getName(),
190                                                    'format'=> 'backup'),
191                                              _("Preview as backup format"),
192                                              $page));
193         }
194         $warning = HTML(
195 _("Please use one of the downloadable versions rather than copying and pasting from the above preview.")
196 . " " .
197 _("The wordwrap of the preview doesn't take nested markup or list indentation into consideration!")
198 . " ",
199 HTML::em(
200 _("PhpWiki developers should manually inspect the downloaded file for nested markup before rewrapping with emacs and checking into CVS.")
201          )
202                         );
203
204         return HTML($h2, HTML::em($desc),
205                     HTML::pre($mailified),
206                     $altpreviewbuttons,
207                     HTML::div(array('class' => 'errors'),
208                               HTML::strong(_("Warning:")),
209                               " ", $warning),
210                     $dl, $Sep, $dlall, $Sep, $dlcvs
211                     );
212     }
213
214     // function handle_plugin_args_cruft(&$argstr, &$args) {
215     // }
216
217     function generateMessageId($mailified) {
218         $array = explode("\n", $mailified);
219         // Extract lastmodifed from mailified document for Content-Id
220         // and/or Message-Id header, NOT from DB (page could have been
221         // edited by someone else since we started).
222         $m1 = preg_grep("/^\s+lastmodified\=(.*);/", $array);
223         $m1 = array_values($m1); //reset resulting keys
224         unset($array);
225         $m2 = preg_split("/(^\s+lastmodified\=)|(;)/", $m1[0], 2,
226                          PREG_SPLIT_NO_EMPTY);
227
228         // insert message id into actual message when appropriate, NOT
229         // into http header should be part of fixup_headers, in the
230         // format:
231         // <abbrphpwikiversion.mtimeepochTZ%InterWikiLinktothispage@hostname>
232         // Hopefully this provides a unique enough identifier without
233         // using md5. Even though this particular wiki may not
234         // actually be part of InterWiki, including this info provides
235         // the wiki name and name of the page which is being
236         // represented as a text message.
237         $this->MessageId = implode('', explode('.', PHPWIKI_VERSION))
238             . "-" . $m2[0] . date("O")
239             //. "-". rawurlencode(WIKI_NAME.":" . $request->getURLtoSelf())
240             . "-". rawurlencode(WIKI_NAME.":" . $this->pagename)
241             . "@". rawurlencode(SERVER_NAME);
242     }
243
244     function fixup_headers(&$mailified) {
245         $return = explode("\n", $mailified);
246
247         // Leave message intact for backing up, just add Message-Id header before transmitting.
248         $item_to_insert = "Message-Id: <" . $this->MessageId .">";
249         $insert_into_key_position = 2;
250         $returnval_ignored = array_splice($return,
251                                           $insert_into_key_position,
252                                           0, $item_to_insert);
253
254         $mailified = implode("\n", array_values($return));
255     }
256
257     function fixup_headers_forcvs(&$mailified) {
258         $array = explode("\n", $mailified);
259
260         // Massage headers to prepare for developer checkin to CVS.
261         $item_to_insert = "X-Rcs-Id: \$Id\$";
262         $insert_into_key_position = 2;
263         $returnval_ignored = array_splice($array,
264                                           $insert_into_key_position,
265                                           0, $item_to_insert);
266
267         $item_to_insert = "  pgsrc_version=\"2 \$Revision\$\";";
268         $insert_into_key_position = 5;
269         $returnval_ignored = array_splice($array,
270                                           $insert_into_key_position,
271                                           0, $item_to_insert);
272         /*
273             Strip out all this junk:
274             author=MeMe;
275             version=74;
276             lastmodified=1041561552;
277             author_id=127.0.0.1;
278             hits=146;
279         */
280         $killme = array("author", "version", "lastmodified",
281                         "author_id", "hits", "owner", "acl");
282         // UltraNasty, fixme:
283         foreach ($killme as $pattern) {
284             $array = preg_replace("/^\s\s$pattern\=.*;/",
285                                   /*$replacement =*/"zzzjunk", $array);
286         }
287         // remove deleted values from array
288         for ($i = 0; $i < count($array); $i++ ) {
289             if(trim($array[$i]) != "zzzjunk") { //nasty, fixme
290             //trigger_error("'$array[$i]'");//debugging
291                 $return[] = $array[$i];
292             }
293         }
294
295         $mailified = implode("\n", $return);
296     }
297 };
298
299 // For emacs users
300 // Local Variables:
301 // mode: php
302 // tab-width: 8
303 // c-basic-offset: 4
304 // c-hanging-comment-ender-p: nil
305 // indent-tabs-mode: nil
306 // End:
307 ?>