]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/loadsave.php
more unittest fixes (file backend, metadata RatingsDb)
[SourceForge/phpwiki.git] / lib / loadsave.php
1 <?php //-*-php-*-
2 rcs_id('$Id: loadsave.php,v 1.120 2004-07-08 19:04:42 rurban Exp $');
3
4 /*
5  Copyright 1999, 2000, 2001, 2002 $ThePhpWikiProgrammingTeam
6
7  This file is part of PhpWiki.
8
9  PhpWiki is free software; you can redistribute it and/or modify
10  it under the terms of the GNU General Public License as published by
11  the Free Software Foundation; either version 2 of the License, or
12  (at your option) any later version.
13
14  PhpWiki is distributed in the hope that it will be useful,
15  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  GNU General Public License for more details.
18
19  You should have received a copy of the GNU General Public License
20  along with PhpWiki; if not, write to the Free Software
21  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  */
23
24
25 require_once("lib/ziplib.php");
26 require_once("lib/Template.php");
27
28 /**
29  * ignore fatal errors during dump
30  */
31 function _dump_error_handler(&$error) {
32     if ($error->isFatal()) {
33         $error->errno = E_USER_WARNING;
34         return true;
35     }
36     return true;         // Ignore error
37     /*
38     if (preg_match('/Plugin/', $error->errstr))
39         return true;
40     */
41     // let the message come through: call the remaining handlers:
42     // return false; 
43 }
44
45 function StartLoadDump(&$request, $title, $html = '')
46 {
47     if (isa($request,'MockRequest'))
48         return;
49     // FIXME: This is a hack
50     if ($html)
51         $html->pushContent('%BODY%');
52     $tmpl = Template('html', array('TITLE' => $title,
53                                    'HEADER' => $title,
54                                    'CONTENT' => $html ? $html : '%BODY%'));
55     echo ereg_replace('%BODY%.*', '', $tmpl->getExpansion($html));
56
57     /* Ignore fatals or warnings in any pagedumps (failing plugins). 
58      * WikiFunctionCb() fails with 4.0.6, works ok with 4.1.1 
59      */
60     if (!check_php_version(4,1) or (DEBUG & _DEBUG_VERBOSE)) return;
61     global $ErrorManager;
62     $ErrorManager->pushErrorHandler(new WikiFunctionCb('_dump_error_handler'));
63 }
64
65 function EndLoadDump(&$request)
66 {
67     if (isa($request,'MockRequest'))
68         return;
69     if (check_php_version(4,1)) {
70         global $ErrorManager;
71         $ErrorManager->popErrorHandler();
72     }
73     $action = $request->getArg('action');
74     $label = '';
75     switch ($action) {
76     case 'zip':        $label = _("ZIP files of database"); break;
77     case 'dumpserial': $label = _("Dump to directory"); break;
78     case 'upload':     $label = _("Upload File"); break;
79     case 'loadfile':   $label = _("Load File"); break;
80     case 'upgrade':    $label = _("Upgrade"); break;
81     case 'dumphtml': 
82     case 'ziphtml':    $label = _("Dump pages as XHTML"); break;
83     }
84     if ($label) $label = str_replace(" ","_",$label);
85     if ($action == 'browse') // loading virgin 
86         $pagelink = WikiLink(HOME_PAGE);
87     else
88         $pagelink = WikiLink(new WikiPageName(_("PhpWikiAdministration"),false,$label));
89
90     PrintXML(HTML::p(HTML::strong(_("Complete."))),
91              HTML::p(fmt("Return to %s", $pagelink)));
92     echo "</body></html>\n";
93 }
94
95
96 ////////////////////////////////////////////////////////////////
97 //
98 //  Functions for dumping.
99 //
100 ////////////////////////////////////////////////////////////////
101
102 /**
103  * For reference see:
104  * http://www.nacs.uci.edu/indiv/ehood/MIME/2045/rfc2045.html
105  * http://www.faqs.org/rfcs/rfc2045.html
106  * (RFC 1521 has been superceeded by RFC 2045 & others).
107  *
108  * Also see http://www.faqs.org/rfcs/rfc2822.html
109  */
110 function MailifyPage ($page, $nversions = 1)
111 {
112     $current = $page->getCurrentRevision();
113     $head = '';
114
115     if (STRICT_MAILABLE_PAGEDUMPS) {
116         $from = defined('SERVER_ADMIN') ? SERVER_ADMIN : 'foo@bar';
117         //This is for unix mailbox format: (not RFC (2)822)
118         // $head .= "From $from  " . CTime(time()) . "\r\n";
119         $head .= "Subject: " . rawurlencode($page->getName()) . "\r\n";
120         $head .= "From: $from (PhpWiki)\r\n";
121         // RFC 2822 requires only a Date: and originator (From:)
122         // field, however the obsolete standard RFC 822 also
123         // requires a destination field.
124         $head .= "To: $from (PhpWiki)\r\n";
125     }
126     $head .= "Date: " . Rfc2822DateTime($current->get('mtime')) . "\r\n";
127     $head .= sprintf("Mime-Version: 1.0 (Produced by PhpWiki %s)\r\n",
128                      PHPWIKI_VERSION);
129
130     // This should just be entered by hand (or by script?)
131     // in the actual pgsrc files, since only they should have
132     // RCS ids.
133     //$head .= "X-Rcs-Id: \$Id\$\r\n";
134
135     $iter = $page->getAllRevisions();
136     $parts = array();
137     while ($revision = $iter->next()) {
138         $parts[] = MimeifyPageRevision($revision);
139         if ($nversions > 0 && count($parts) >= $nversions)
140             break;
141     }
142     if (count($parts) > 1)
143         return $head . MimeMultipart($parts);
144     assert($parts);
145     return $head . $parts[0];
146 }
147
148 /***
149  * Compute filename to used for storing contents of a wiki page.
150  *
151  * Basically we do a rawurlencode() which encodes everything except
152  * ASCII alphanumerics and '.', '-', and '_'.
153  *
154  * But we also want to encode leading dots to avoid filenames like
155  * '.', and '..'. (Also, there's no point in generating "hidden" file
156  * names, like '.foo'.)
157  *
158  * @param $pagename string Pagename.
159  * @return string Filename for page.
160  */
161 function FilenameForPage ($pagename)
162 {
163     $enc = rawurlencode($pagename);
164     return preg_replace('/^\./', '%2e', $enc);
165 }
166
167 /**
168  * The main() function which generates a zip archive of a PhpWiki.
169  *
170  * If $include_archive is false, only the current version of each page
171  * is included in the zip file; otherwise all archived versions are
172  * included as well.
173  */
174 function MakeWikiZip (&$request)
175 {
176     if ($request->getArg('include') == 'all') {
177         $zipname         = WIKI_NAME . _("FullDump") . date('Ymd-Hi') . '.zip';
178         $include_archive = true;
179     }
180     else {
181         $zipname         = WIKI_NAME . _("LatestSnapshot") . date('Ymd-Hi') . '.zip';
182         $include_archive = false;
183     }
184
185
186     $zip = new ZipWriter("Created by PhpWiki " . PHPWIKI_VERSION, $zipname);
187
188     /* ignore fatals in plugins */
189     if (check_php_version(4,1)) {
190         global $ErrorManager;
191         $ErrorManager->pushErrorHandler(new WikiFunctionCb('_dump_error_handler'));
192     }
193
194     $dbi = $request->getDbh();
195     $thispage = $request->getArg('pagename'); // for "Return to ..."
196     if ($exclude = $request->getArg('exclude')) {   // exclude which pagenames
197         $excludeList = explodePageList($exclude); 
198     } else {
199         $excludeList = array();
200     }
201     if ($whichpages = $request->getArg('pages')) {  // which pagenames
202         if ($whichpages == '[]') // current page
203             $whichpages = $thispage;
204         $pages = new WikiDB_Array_PageIterator(explodePageList($whichpages));
205     } else {
206         $pages = $dbi->getAllPages();
207     }
208     $request_args = $request->args;
209     
210     while ($page = $pages->next()) {
211         $request->args = $request_args; // some plugins might change them (esp. on POST)
212         if (! $request->getArg('start_debug'))
213             @set_time_limit(30); // Reset watchdog
214         else    
215             @set_time_limit(240);
216
217         $current = $page->getCurrentRevision();
218         if ($current->getVersion() == 0)
219             continue;
220
221         $pagename = $page->getName();
222         $wpn = new WikiPageName($pagename);
223         if (!$wpn->isValid())
224             continue;
225         if (in_array($page->getName(), $excludeList)) {
226             continue;
227         }
228
229         $attrib = array('mtime'    => $current->get('mtime'),
230                         'is_ascii' => 1);
231         if ($page->get('locked'))
232             $attrib['write_protected'] = 1;
233
234         if ($include_archive)
235             $content = MailifyPage($page, 0);
236         else
237             $content = MailifyPage($page);
238
239         $zip->addRegularFile( FilenameForPage($pagename),
240                               $content, $attrib);
241     }
242     $zip->finish();
243     if (check_php_version(4,1)) {
244         $ErrorManager->popErrorHandler();
245     }
246 }
247
248 function DumpToDir (&$request)
249 {
250     $directory = $request->getArg('directory');
251     if (empty($directory))
252         $directory = DEFAULT_DUMP_DIR; // See lib/plugin/WikiForm.php:87
253     if (empty($directory))
254         $request->finish(_("You must specify a directory to dump to"));
255
256     // see if we can access the directory the user wants us to use
257     if (! file_exists($directory)) {
258         if (! mkdir($directory, 0755))
259             $request->finish(fmt("Cannot create directory '%s'", $directory));
260         else
261             $html = HTML::p(fmt("Created directory '%s' for the page dump...",
262                                 $directory));
263     } else {
264         $html = HTML::p(fmt("Using directory '%s'", $directory));
265     }
266
267     StartLoadDump($request, _("Dumping Pages"), $html);
268
269     $dbi = $request->getDbh();
270     $thispage = $request->getArg('pagename'); // for "Return to ..."
271     if ($exclude = $request->getArg('exclude')) {   // exclude which pagenames
272         $excludeList = explodePageList($exclude); 
273     } else {
274         $excludeList = array();
275     }
276     if ($whichpages = $request->getArg('pages')) {  // which pagenames
277         if ($whichpages == '[]') // current page
278             $whichpages = $thispage;
279         $pages = new WikiDB_Array_PageIterator(explodePageList($whichpages));
280     } else {
281         $pages = $dbi->getAllPages();
282     }
283
284     $request_args = $request->args;
285     
286     while ($page = $pages->next()) {
287         $request->args = $request_args; // some plugins might change them (esp. on POST)
288         if (! $request->getArg('start_debug'))
289             @set_time_limit(30); // Reset watchdog.
290         else    
291             @set_time_limit(240);
292
293         $pagename = $page->getName();
294         if (!isa($request,'MockRequest')) {
295             PrintXML(HTML::br(), $pagename, ' ... ');
296             flush();
297         }
298
299         if (in_array($pagename, $excludeList)) {
300             if (!isa($request,'MockRequest')) {
301                 PrintXML(_("Skipped."));
302                 flush();
303             }
304             continue;
305         }
306         $filename = FilenameForPage($pagename);
307         $msg = HTML();
308         if($page->getName() != $filename) {
309             $msg->pushContent(HTML::small(fmt("saved as %s", $filename)),
310                               " ... ");
311         }
312
313         if ($request->getArg('include') == 'all')
314             $data = MailifyPage($page, 0);
315         else
316             $data = MailifyPage($page);
317
318         if ( !($fd = fopen("$directory/$filename", "wb")) ) {
319             $msg->pushContent(HTML::strong(fmt("couldn't open file '%s' for writing",
320                                                "$directory/$filename")));
321             $request->finish($msg);
322         }
323
324         $num = fwrite($fd, $data, strlen($data));
325         $msg->pushContent(HTML::small(fmt("%s bytes written", $num)));
326         if (!isa($request,'MockRequest')) {
327             PrintXML($msg);
328             flush();
329         }
330         assert($num == strlen($data));
331         fclose($fd);
332     }
333
334     EndLoadDump($request);
335 }
336
337 /**
338  * Dump all pages as XHTML to a directory, as pagename.html.
339  * Copies all used css files to the directory, all used images to a 
340  * "images" subdirectory, and all used buttons to a "images/buttons" subdirectory.
341  * The webserver must have write permissions to these directories. 
342  *   chown httpd HTML_DUMP_DIR; chmod u+rwx HTML_DUMP_DIR 
343  * should be enough.
344  *
345  * @param string directory (optional) path to dump to. Default: HTML_DUMP_DIR
346  * @param string pages     (optional) Comma-seperated of glob-style pagenames to dump
347  * @param string exclude   (optional) Comma-seperated of glob-style pagenames to exclude
348  */
349 function DumpHtmlToDir (&$request)
350 {
351     $directory = $request->getArg('directory');
352     if (empty($directory))
353         $directory = HTML_DUMP_DIR; // See lib/plugin/WikiForm.php:87
354     if (empty($directory))
355         $request->finish(_("You must specify a directory to dump to"));
356
357     // see if we can access the directory the user wants us to use
358     if (! file_exists($directory)) {
359         if (! mkdir($directory, 0755))
360             $request->finish(fmt("Cannot create directory '%s'", $directory));
361         else
362             $html = HTML::p(fmt("Created directory '%s' for the page dump...",
363                                 $directory));
364     } else {
365         $html = HTML::p(fmt("Using directory '%s'", $directory));
366     }
367
368     StartLoadDump($request, _("Dumping Pages"), $html);
369     $thispage = $request->getArg('pagename'); // for "Return to ..."
370
371     $dbi = $request->getDbh();
372     if ($exclude = $request->getArg('exclude')) {   // exclude which pagenames
373         $excludeList = explodePageList($exclude); 
374     } else {
375         $excludeList = array();
376     }
377     if ($whichpages = $request->getArg('pages')) {  // which pagenames
378         if ($whichpages == '[]') // current page
379             $whichpages = $thispage;
380         $pages = new WikiDB_Array_PageIterator(explodePageList($whichpages));
381     } else {
382         $pages = $dbi->getAllPages();
383     }
384
385     global $WikiTheme;
386     if (defined('HTML_DUMP_SUFFIX'))
387         $WikiTheme->HTML_DUMP_SUFFIX = HTML_DUMP_SUFFIX;
388     $WikiTheme->DUMP_MODE = 'HTML';
389     $request_args = $request->args;
390     
391     while ($page = $pages->next()) {
392         $request->args = $request_args; // some plugins might change them (esp. on POST)
393         if (! $request->getArg('start_debug'))
394             @set_time_limit(30); // Reset watchdog.
395         else    
396             @set_time_limit(240);
397           
398         $pagename = $page->getName();
399         if (!isa($request,'MockRequest')) {
400             PrintXML(HTML::br(), $pagename, ' ... ');
401             flush();
402         }
403         if (in_array($pagename, $excludeList)) {
404             if (!isa($request,'MockRequest')) {
405                 PrintXML(_("Skipped."));
406                 flush();
407             }
408             continue;
409         }
410
411         $request->setArg('pagename', $pagename); // Template::_basepage fix
412         $filename = FilenameForPage($pagename) . $WikiTheme->HTML_DUMP_SUFFIX;
413         $msg = HTML();
414
415         $revision = $page->getCurrentRevision();
416         $transformedContent = $revision->getTransformedContent();
417         $template = new Template('browse', $request,
418                                  array('revision' => $revision,
419                                        'CONTENT' => $transformedContent));
420
421         $data = GeneratePageasXML($template, $pagename);
422
423         if ( !($fd = fopen("$directory/$filename", "wb")) ) {
424             $msg->pushContent(HTML::strong(fmt("couldn't open file '%s' for writing",
425                                                "$directory/$filename")));
426             $request->finish($msg);
427         }
428         $num = fwrite($fd, $data, strlen($data));
429         if($page->getName() != $filename) {
430             $prefix = '';
431             if (isWindows()) {  
432                 // drive where apache is installed
433                 $prefix = '/' . substr($_SERVER["DOCUMENT_ROOT"],0,2);
434             }
435             $link = LinkURL("file://".$prefix.$directory."/".$filename, 
436                             $filename);
437             $msg->pushContent(HTML::small(_("saved as "), $link, " ... "));
438         }
439         $msg->pushContent(HTML::small(fmt("%s bytes written", $num), "\n"));
440         if (!isa($request,'MockRequest')) {
441             PrintXML($msg);
442             flush();
443         }
444
445         assert($num == strlen($data));
446         fclose($fd);
447     }
448
449     if (!empty($WikiTheme->dumped_images) and is_array($WikiTheme->dumped_images)) {
450         @mkdir("$directory/images");
451         foreach ($WikiTheme->dumped_images as $img_file) {
452             if (($from = $WikiTheme->_findFile($img_file)) and basename($from)) {
453                 $target = "$directory/images/".basename($img_file);
454                 if (copy($WikiTheme->_path . $from, $target)) {
455                     $msg = HTML(HTML::br(), HTML($from), HTML::small(fmt("... copied to %s", $target)));
456                     if (!isa($request,'MockRequest'))
457                         PrintXML($msg);
458                 }
459             } else {
460                 $msg = HTML(HTML::br(), HTML($from), HTML::small(fmt("... not found", $target)));
461                 if (!isa($request,'MockRequest'))
462                     PrintXML($msg);
463             }
464         }
465     }
466     if (!empty($WikiTheme->dumped_buttons) and is_array($WikiTheme->dumped_buttons)) {
467         // Buttons also
468         @mkdir("$directory/images/buttons");
469         foreach ($WikiTheme->dumped_buttons as $text => $img_file) {
470             if (($from = $WikiTheme->_findFile($img_file)) and basename($from)) {
471                 $target = "$directory/images/buttons/".basename($img_file);
472                 if (copy($WikiTheme->_path . $from, $target)) {
473                     $msg = HTML(HTML::br(), HTML($from), HTML::small(fmt("... copied to %s", $target)));
474                     if (!isa($request,'MockRequest'))
475                         PrintXML($msg);
476                 }
477             } else {
478                 $msg = HTML(HTML::br(), HTML($from), HTML::small(fmt("... not found", $target)));
479                 if (!isa($request,'MockRequest'))
480                     PrintXML($msg);
481             }
482         }
483     }
484     if (!empty($WikiTheme->dumped_css) and is_array($WikiTheme->dumped_css)) {
485       foreach ($WikiTheme->dumped_css as $css_file) {
486           if (($from = $WikiTheme->_findFile(basename($css_file))) and basename($from)) {
487               $target = "$directory/" . basename($css_file);
488               if (copy($WikiTheme->_path . $from, $target)) {
489                   $msg = HTML(HTML::br(), HTML($from), HTML::small(fmt("... copied to %s", $target)));
490                   if (!isa($request,'MockRequest'))
491                       PrintXML($msg);
492               }
493           } else {
494               $msg = HTML(HTML::br(), HTML($from), HTML::small(fmt("... not found", $target)));
495               if (!isa($request,'MockRequest'))
496                   PrintXML($msg);
497           }
498       }
499     }
500     $WikiTheme->HTML_DUMP_SUFFIX = '';
501     $WikiTheme->DUMP_MODE = false;
502
503     $request->setArg('pagename',$thispage); // Template::_basepage fix
504     EndLoadDump($request);
505 }
506
507 /* Known problem: any plugins or other code which echo()s text will
508  * lead to a corrupted html zip file which may produce the following
509  * errors upon unzipping:
510  *
511  * warning [wikihtml.zip]:  2401 extra bytes at beginning or within zipfile
512  * file #58:  bad zipfile offset (local header sig):  177561
513  *  (attempting to re-compensate)
514  *
515  * However, the actual wiki page data should be unaffected.
516  */
517 function MakeWikiZipHtml (&$request)
518 {
519     $zipname = "wikihtml.zip";
520     $zip = new ZipWriter("Created by PhpWiki " . PHPWIKI_VERSION, $zipname);
521     $dbi = $request->getDbh();
522     $thispage = $request->getArg('pagename'); // for "Return to ..."
523     if ($exclude = $request->getArg('exclude')) {   // exclude which pagenames
524         $excludeList = explodePageList($exclude); 
525     } else {
526         $excludeList = array();
527     }
528     if ($whichpages = $request->getArg('pages')) {  // which pagenames
529         if ($whichpages == '[]') // current page
530             $whichpages = $thispage;
531         $pages = new WikiDB_Array_PageIterator(explodePageList($whichpages));
532     } else {
533         $pages = $dbi->getAllPages();
534     }
535
536     global $WikiTheme;
537     if (defined('HTML_DUMP_SUFFIX'))
538         $WikiTheme->HTML_DUMP_SUFFIX = HTML_DUMP_SUFFIX;
539
540     /* ignore fatals in plugins */
541     if (check_php_version(4,1)) {
542         global $ErrorManager;
543         $ErrorManager->pushErrorHandler(new WikiFunctionCb('_dump_error_handler'));
544     }
545
546     $request_args = $request->args;
547     
548     while ($page = $pages->next()) {
549         $request->args = $request_args; // some plugins might change them (esp. on POST)
550         if (! $request->getArg('start_debug'))
551             @set_time_limit(30); // Reset watchdog.
552         else    
553             @set_time_limit(240);
554
555         $current = $page->getCurrentRevision();
556         if ($current->getVersion() == 0)
557             continue;
558         $pagename = $page->getName();
559         if (in_array($pagename, $excludeList)) {
560             continue;
561         }
562
563         $attrib = array('mtime'    => $current->get('mtime'),
564                         'is_ascii' => 1);
565         if ($page->get('locked'))
566             $attrib['write_protected'] = 1;
567
568         $request->setArg('pagename',$pagename); // Template::_basepage fix
569         $filename = FilenameForPage($pagename) . $WikiTheme->HTML_DUMP_SUFFIX;
570         $revision = $page->getCurrentRevision();
571
572         $transformedContent = $revision->getTransformedContent();
573
574         $template = new Template('browse', $request,
575                                  array('revision' => $revision,
576                                        'CONTENT' => $transformedContent));
577
578         $data = GeneratePageasXML($template, $pagename);
579
580         $zip->addRegularFile( $filename, $data, $attrib);
581     }
582     // FIXME: Deal with images here.
583     $zip->finish();
584     if (check_php_version(4,1)) {
585         $ErrorManager->popErrorHandler();
586     }
587     $WikiTheme->$HTML_DUMP_SUFFIX = '';
588 }
589
590
591 ////////////////////////////////////////////////////////////////
592 //
593 //  Functions for restoring.
594 //
595 ////////////////////////////////////////////////////////////////
596
597 function SavePage (&$request, $pageinfo, $source, $filename)
598 {
599     $pagedata    = $pageinfo['pagedata'];    // Page level meta-data.
600     $versiondata = $pageinfo['versiondata']; // Revision level meta-data.
601
602     if (empty($pageinfo['pagename'])) {
603         PrintXML(HTML::dt(HTML::strong(_("Empty pagename!"))));
604         return;
605     }
606
607     if (empty($versiondata['author_id']))
608         $versiondata['author_id'] = $versiondata['author'];
609
610     $pagename = $pageinfo['pagename'];
611     $content  = $pageinfo['content'];
612
613     if ($pagename ==_("InterWikiMap"))
614         $content = _tryinsertInterWikiMap($content);
615
616     $dbi = $request->getDbh();
617     $page = $dbi->getPage($pagename);
618
619     // Try to merge if updated pgsrc contents are different. This
620     // whole thing is hackish
621     //
622     // TODO: try merge unless:
623     // if (current contents = default contents && pgsrc_version >=
624     // pgsrc_version) then just upgrade this pgsrc
625     $needs_merge = false;
626     $merging = false;
627     $overwrite = false;
628
629     if ($request->getArg('merge')) {
630         $merging = true;
631     }
632     else if ($request->getArg('overwrite')) {
633         $overwrite = true;
634     }
635
636     $current = $page->getCurrentRevision();
637     if ( $current and (! $current->hasDefaultContents())
638          && ($current->getPackedContent() != $content)
639          && ($merging == true) ) {
640         include_once('lib/editpage.php');
641         $request->setArg('pagename', $pagename);
642         $r = $current->getVersion();
643         $request->setArg('revision', $current->getVersion());
644         $p = new LoadFileConflictPageEditor($request);
645         $p->_content = $content;
646         $p->_currentVersion = $r - 1;
647         $p->editPage($saveFailed = true);
648         return; //early return
649     }
650
651     foreach ($pagedata as $key => $value) {
652         if (!empty($value))
653             $page->set($key, $value);
654     }
655
656     $mesg = HTML::dd();
657     $skip = false;
658     if ($source)
659         $mesg->pushContent(' ', fmt("from %s", $source));
660
661
662     if (!$current) {
663         //FIXME: This should not happen! (empty vdata, corrupt cache or db)
664         $current = $page->getCurrentRevision();
665     }
666     if ($current->getVersion() == 0) {
667         $mesg->pushContent(' - ', _("new page"));
668         $isnew = true;
669     }
670     else {
671         if ( (! $current->hasDefaultContents())
672              && ($current->getPackedContent() != $content) ) {
673             if ($overwrite) {
674                 $mesg->pushContent(' ',
675                                    fmt("has edit conflicts - overwriting anyway"));
676                 $skip = false;
677                 if (substr_count($source, 'pgsrc')) {
678                     $versiondata['author'] = _("The PhpWiki programming team");
679                     // but leave authorid as userid who loaded the file
680                 }
681             }
682             else {
683                 $mesg->pushContent(' ', fmt("has edit conflicts - skipped"));
684                 $needs_merge = true; // hackish
685                 $skip = true;
686             }
687         }
688         else if ($current->getPackedContent() == $content
689                  && $current->get('author') == $versiondata['author']) {
690             // The page metadata is already changed, we don't need a new revision.
691             // This was called previously "is identical to current version %d - skipped"
692             // which is wrong, since the pagedata was stored, not skipped.
693             $mesg->pushContent(' ',
694                                fmt("content is identical to current version %d - no new revision created",
695                                    $current->getVersion()));
696             $skip = true;
697         }
698         $isnew = false;
699     }
700
701     if (! $skip ) {
702         // in case of failures print the culprit:
703         if (!isa($request,'MockRequest')) {
704             PrintXML(HTML::dt(WikiLink($pagename))); flush();
705         }
706         $new = $page->save($content, WIKIDB_FORCE_CREATE, $versiondata);
707         $dbi->touch();
708         $mesg->pushContent(' ', fmt("- saved to database as version %d",
709                                     $new->getVersion()));
710     }
711     if ($needs_merge) {
712         $f = $source;
713         // hackish, $source contains needed path+filename
714         $f = str_replace(sprintf(_("MIME file %s"), ''), '', $f);
715         $f = str_replace(sprintf(_("Serialized file %s"), ''), '', $f);
716         $f = str_replace(sprintf(_("plain file %s"), ''), '', $f);
717         //check if uploaded file? they pass just the content, but the file is gone
718         if (@stat($f)) {
719             global $WikiTheme;
720             $meb = Button(array('action' => 'loadfile',
721                                 'merge'=> true,
722                                 'source'=> $f),
723                           _("Merge Edit"),
724                           _("PhpWikiAdministration"),
725                           'wikiadmin');
726             $owb = Button(array('action' => 'loadfile',
727                                 'overwrite'=> true,
728                                 'source'=> $f),
729                           _("Restore Anyway"),
730                           _("PhpWikiAdministration"),
731                           'wikiunsafe');
732             $mesg->pushContent(' ', $meb, " ", $owb);
733         } else {
734             $mesg->pushContent(HTML::em(_(" Sorry, cannot merge.")));
735         }
736     }
737
738     if (!isa($request,'MockRequest')) {
739       if ($skip)
740         PrintXML(HTML::dt(HTML::em(WikiLink($pagename))), $mesg);
741       else
742         PrintXML($mesg);
743       flush();
744     }
745 }
746
747 // action=revert (by diff)
748 function RevertPage (&$request)
749 {
750     $mesg = HTML::dd();
751     $pagename = $request->getArg('pagename');
752     $version = $request->getArg('version');
753     if (!$version) {
754         PrintXML(HTML::dt(fmt("Revert")," ",WikiLink($pagename)),
755                  HTML::dd(_("missing required version argument")));
756         return;
757     }
758     $dbi = $request->getDbh();
759     $page = $dbi->getPage($pagename);
760     $current = $page->getCurrentRevision();
761     if ($current->getVersion() == 0) {
762         $mesg->pushContent(' ', _("no page content"));
763         PrintXML(HTML::dt(fmt("Revert")," ",WikiLink($pagename)),
764                  $mesg);
765         return;
766     }
767     if ($current->getVersion() == $version) {
768         $mesg->pushContent(' ', _("same version page"));
769         return;
770     }
771     $rev = $page->getRevision($version);
772     $content = $rev->getPackedContent();
773     $versiondata = $rev->_data;
774     $versiondata['summary'] = sprintf(_("revert to version %d"), $version);
775     $new = $page->save($content, $current->getVersion() + 1, $versiondata);
776     $dbi->touch();
777     $mesg->pushContent(' ', fmt("- version %d saved to database as version %d",
778                                 $version, $new->getVersion()));
779     PrintXML(HTML::dt(fmt("Revert")," ",WikiLink($pagename)),
780              $mesg);
781     flush();
782 }
783
784 function _tryinsertInterWikiMap($content) {
785     $goback = false;
786     if (strpos($content, "<verbatim>")) {
787         //$error_html = " The newly loaded pgsrc already contains a verbatim block.";
788         $goback = true;
789     }
790     if (!$goback && !defined('INTERWIKI_MAP_FILE')) {
791         $error_html = sprintf(" "._("%s: not defined"), "INTERWIKI_MAP_FILE");
792         $goback = true;
793     }
794     $mapfile = FindFile(INTERWIKI_MAP_FILE,1);
795     if (!$goback && !file_exists($mapfile)) {
796         $error_html = sprintf(" "._("%s: file not found"), INTERWIKI_MAP_FILE);
797         $goback = true;
798     }
799
800     if (!empty($error_html))
801         trigger_error(_("Default InterWiki map file not loaded.")
802                       . $error_html, E_USER_NOTICE);
803     if ($goback)
804         return $content;
805
806     // if loading from virgin setup do echo, otherwise trigger_error E_USER_NOTICE
807     if (!isa($GLOBALS['request'],'MockRequest'))
808         echo sprintf(_("Loading InterWikiMap from external file %s."), $mapfile),"<br />";
809
810     $fd = fopen ($mapfile, "rb");
811     $data = fread ($fd, filesize($mapfile));
812     fclose ($fd);
813     $content = $content . "\n<verbatim>\n$data</verbatim>\n";
814     return $content;
815 }
816
817 function ParseSerializedPage($text, $default_pagename, $user)
818 {
819     if (!preg_match('/^a:\d+:{[si]:\d+/', $text))
820         return false;
821
822     $pagehash = unserialize($text);
823
824     // Split up pagehash into four parts:
825     //   pagename
826     //   content
827     //   page-level meta-data
828     //   revision-level meta-data
829
830     if (!defined('FLAG_PAGE_LOCKED'))
831         define('FLAG_PAGE_LOCKED', 1);
832     $pageinfo = array('pagedata'    => array(),
833                       'versiondata' => array());
834
835     $pagedata = &$pageinfo['pagedata'];
836     $versiondata = &$pageinfo['versiondata'];
837
838     // Fill in defaults.
839     if (empty($pagehash['pagename']))
840         $pagehash['pagename'] = $default_pagename;
841     if (empty($pagehash['author'])) {
842         $pagehash['author'] = $user->getId();
843     }
844
845     foreach ($pagehash as $key => $value) {
846         switch($key) {
847             case 'pagename':
848             case 'version':
849             case 'hits':
850                 $pageinfo[$key] = $value;
851                 break;
852             case 'content':
853                 $pageinfo[$key] = join("\n", $value);
854                 break;
855             case 'flags':
856                 if (($value & FLAG_PAGE_LOCKED) != 0)
857                     $pagedata['locked'] = 'yes';
858                 break;
859             case 'owner':
860             case 'created':
861                 $pagedata[$key] = $value;
862                 break;
863             case 'acl':
864             case 'perm':
865                 $pagedata['perm'] = ParseMimeifiedPerm($value);
866                 break;
867             case 'lastmodified':
868                 $versiondata['mtime'] = $value;
869                 break;
870             case 'author':
871             case 'author_id':
872             case 'summary':
873                 $versiondata[$key] = $value;
874                 break;
875         }
876     }
877     return $pageinfo;
878 }
879
880 function SortByPageVersion ($a, $b) {
881     return $a['version'] - $b['version'];
882 }
883
884 function LoadFile (&$request, $filename, $text = false, $mtime = false)
885 {
886     if (!is_string($text)) {
887         // Read the file.
888         $stat  = stat($filename);
889         $mtime = $stat[9];
890         $text  = implode("", file($filename));
891     }
892
893         if (! $request->getArg('start_debug'))
894         @set_time_limit(30); // Reset watchdog.
895     else    
896         @set_time_limit(240);
897
898     // FIXME: basename("filewithnoslashes") seems to return garbage sometimes.
899     $basename = basename("/dummy/" . $filename);
900
901     if (!$mtime)
902         $mtime = time();    // Last resort.
903
904     $default_pagename = rawurldecode($basename);
905
906     if ( ($parts = ParseMimeifiedPages($text)) ) {
907         usort($parts, 'SortByPageVersion');
908         foreach ($parts as $pageinfo)
909             SavePage($request, $pageinfo, sprintf(_("MIME file %s"),
910                                                   $filename), $basename);
911     }
912     else if ( ($pageinfo = ParseSerializedPage($text, $default_pagename,
913                                                $request->getUser())) ) {
914         SavePage($request, $pageinfo, sprintf(_("Serialized file %s"),
915                                               $filename), $basename);
916     }
917     else {
918         $user = $request->getUser();
919
920         // Assume plain text file.
921         $pageinfo = array('pagename' => $default_pagename,
922                           'pagedata' => array(),
923                           'versiondata'
924                           => array('author' => $user->getId()),
925                           'content'  => preg_replace('/[ \t\r]*\n/', "\n",
926                                                      chop($text))
927                           );
928         SavePage($request, $pageinfo, sprintf(_("plain file %s"), $filename),
929                  $basename);
930     }
931 }
932
933 function LoadZip (&$request, $zipfile, $files = false, $exclude = false) {
934     $zip = new ZipReader($zipfile);
935     while (list ($fn, $data, $attrib) = $zip->readFile()) {
936         // FIXME: basename("filewithnoslashes") seems to return
937         // garbage sometimes.
938         $fn = basename("/dummy/" . $fn);
939         if ( ($files && !in_array($fn, $files))
940              || ($exclude && in_array($fn, $exclude)) ) {
941             PrintXML(HTML::dt(WikiLink($fn)),
942                      HTML::dd(_("Skipping")));
943             flush();
944             continue;
945         }
946
947         LoadFile($request, $fn, $data, $attrib['mtime']);
948     }
949 }
950
951 function LoadDir (&$request, $dirname, $files = false, $exclude = false) {
952     $fileset = new LimitedFileSet($dirname, $files, $exclude);
953
954     if (!$files and ($skiplist = $fileset->getSkippedFiles())) {
955         PrintXML(HTML::dt(HTML::strong(_("Skipping"))));
956         $list = HTML::ul();
957         foreach ($skiplist as $file)
958             $list->pushContent(HTML::li(WikiLink($file)));
959         PrintXML(HTML::dd($list));
960     }
961
962     // Defer HomePage loading until the end. If anything goes wrong
963     // the pages can still be loaded again.
964     $files = $fileset->getFiles();
965     if (in_array(HOME_PAGE, $files)) {
966         $files = array_diff($files, array(HOME_PAGE));
967         $files[] = HOME_PAGE;
968     }
969     foreach ($files as $file) {
970         if (substr($file,-1,1) != '~') // refuse to load backup files
971             LoadFile($request, "$dirname/$file");
972     }
973 }
974
975 class LimitedFileSet extends FileSet {
976     function LimitedFileSet($dirname, $_include, $exclude) {
977         $this->_includefiles = $_include;
978         $this->_exclude = $exclude;
979         $this->_skiplist = array();
980         parent::FileSet($dirname);
981     }
982
983     function _filenameSelector($fn) {
984         $incl = &$this->_includefiles;
985         $excl = &$this->_exclude;
986
987         if ( ($incl && !in_array($fn, $incl))
988              || ($excl && in_array($fn, $excl)) ) {
989             $this->_skiplist[] = $fn;
990             return false;
991         } else {
992             return true;
993         }
994     }
995
996     function getSkippedFiles () {
997         return $this->_skiplist;
998     }
999 }
1000
1001
1002 function IsZipFile ($filename_or_fd)
1003 {
1004     // See if it looks like zip file
1005     if (is_string($filename_or_fd))
1006     {
1007         $fd    = fopen($filename_or_fd, "rb");
1008         $magic = fread($fd, 4);
1009         fclose($fd);
1010     }
1011     else
1012     {
1013         $fpos  = ftell($filename_or_fd);
1014         $magic = fread($filename_or_fd, 4);
1015         fseek($filename_or_fd, $fpos);
1016     }
1017
1018     return $magic == ZIP_LOCHEAD_MAGIC || $magic == ZIP_CENTHEAD_MAGIC;
1019 }
1020
1021
1022 function LoadAny (&$request, $file_or_dir, $files = false, $exclude = false)
1023 {
1024     // Try urlencoded filename for accented characters.
1025     if (!file_exists($file_or_dir)) {
1026         // Make sure there are slashes first to avoid confusing phps
1027         // with broken dirname or basename functions.
1028         // FIXME: windows uses \ and :
1029         if (is_integer(strpos($file_or_dir, "/"))) {
1030             $file_or_dir = FindFile($file_or_dir);
1031             // Panic
1032             if (!file_exists($file_or_dir))
1033                 $file_or_dir = dirname($file_or_dir) . "/"
1034                     . urlencode(basename($file_or_dir));
1035         } else {
1036             // This is probably just a file.
1037             $file_or_dir = urlencode($file_or_dir);
1038         }
1039     }
1040
1041     $type = filetype($file_or_dir);
1042     if ($type == 'link') {
1043         // For symbolic links, use stat() to determine
1044         // the type of the underlying file.
1045         list(,,$mode) = stat($file_or_dir);
1046         $type = ($mode >> 12) & 017;
1047         if ($type == 010)
1048             $type = 'file';
1049         elseif ($type == 004)
1050             $type = 'dir';
1051     }
1052
1053     if (! $type) {
1054         $request->finish(fmt("Unable to load: %s", $file_or_dir));
1055     }
1056     else if ($type == 'dir') {
1057         LoadDir($request, $file_or_dir, $files, $exclude);
1058     }
1059     else if ($type != 'file' && !preg_match('/^(http|ftp):/', $file_or_dir))
1060     {
1061         $request->finish(fmt("Bad file type: %s", $type));
1062     }
1063     else if (IsZipFile($file_or_dir)) {
1064         LoadZip($request, $file_or_dir, $files, $exclude);
1065     }
1066     else /* if (!$files || in_array(basename($file_or_dir), $files)) */
1067     {
1068         LoadFile($request, $file_or_dir);
1069     }
1070 }
1071
1072 function LoadFileOrDir (&$request)
1073 {
1074     $source = $request->getArg('source');
1075     $finder = new FileFinder;
1076     $source = $finder->slashifyPath($source);
1077     $page = rawurldecode(basename($source));
1078     StartLoadDump($request, fmt("Loading '%s'", 
1079         HTML(dirname($source),
1080              dirname($source) ? "/" : "",
1081              WikiLink($page,'auto'))));
1082     echo "<dl>\n";
1083     LoadAny($request, $source);
1084     echo "</dl>\n";
1085     EndLoadDump($request);
1086 }
1087
1088 function SetupWiki (&$request)
1089 {
1090     global $GenericPages, $LANG;
1091
1092
1093     //FIXME: This is a hack (err, "interim solution")
1094     // This is a bogo-bogo-login:  Login without
1095     // saving login information in session state.
1096     // This avoids logging in the unsuspecting
1097     // visitor as "The PhpWiki programming team".
1098     //
1099     // This really needs to be cleaned up...
1100     // (I'm working on it.)
1101     $real_user = $request->_user;
1102     if (ENABLE_USER_NEW)
1103         $request->_user = new _BogoUser(_("The PhpWiki programming team"));
1104
1105     else
1106         $request->_user = new WikiUser($request, _("The PhpWiki programming team"),
1107                                        WIKIAUTH_BOGO);
1108
1109     StartLoadDump($request, _("Loading up virgin wiki"));
1110     echo "<dl>\n";
1111
1112     $pgsrc = FindLocalizedFile(WIKI_PGSRC);
1113     $default_pgsrc = FindFile(DEFAULT_WIKI_PGSRC);
1114
1115     $request->setArg('overwrite',true);
1116     if ($default_pgsrc != $pgsrc) {
1117         LoadAny($request, $default_pgsrc, $GenericPages);
1118     }
1119     $request->setArg('overwrite',false);
1120     LoadAny($request, $pgsrc);
1121
1122     // Ensure that all mandatory pages are loaded
1123     $finder = new FileFinder;
1124     foreach (array_merge(explode(':','OldTextFormattingRules:TextFormattingRules:PhpWikiAdministration'),
1125                          $GLOBALS['AllActionPages'],
1126                          array(constant('HOME_PAGE'))) as $f) {
1127         $page = gettext($f);
1128         if (isSubPage($page))
1129             $page = urlencode($page);
1130         if (! $request->_dbi->isWikiPage(urldecode($page)) ) {
1131             // translated version provided?
1132             if ($lf = FindLocalizedFile($pgsrc . $finder->_pathsep . $page, 1))
1133                 LoadAny($request, $lf);
1134             else { // load english version of required action page
1135                 LoadAny($request, FindFile(DEFAULT_WIKI_PGSRC . $finder->_pathsep . urlencode($f)));
1136                 $page = $f;
1137             }
1138         }
1139         if (!$request->_dbi->isWikiPage(urldecode($page))) {
1140             trigger_error(sprintf("Mandatory file %s couldn't be loaded!", $page),
1141                           E_USER_WARNING);
1142         }
1143     }
1144
1145     echo "</dl>\n";
1146     EndLoadDump($request);
1147 }
1148
1149 function LoadPostFile (&$request)
1150 {
1151     $upload = $request->getUploadedFile('file');
1152
1153     if (!$upload)
1154         $request->finish(_("No uploaded file to upload?")); // FIXME: more concise message
1155
1156
1157     // Dump http headers.
1158     StartLoadDump($request, sprintf(_("Uploading %s"), $upload->getName()));
1159     echo "<dl>\n";
1160
1161     $fd = $upload->open();
1162     if (IsZipFile($fd))
1163         LoadZip($request, $fd, false, array(_("RecentChanges")));
1164     else
1165         LoadFile($request, $upload->getName(), $upload->getContents());
1166
1167     echo "</dl>\n";
1168     EndLoadDump($request);
1169 }
1170
1171 /**
1172  $Log: not supported by cvs2svn $
1173  Revision 1.119  2004/07/08 15:23:59  rurban
1174  less verbose for tests
1175
1176  Revision 1.118  2004/07/08 13:50:32  rurban
1177  various unit test fixes: print error backtrace on _DEBUG_TRACE; allusers fix; new PHPWIKI_NOMAIN constant for omitting the mainloop
1178
1179  Revision 1.117  2004/07/02 09:55:58  rurban
1180  more stability fixes: new DISABLE_GETIMAGESIZE if your php crashes when loading LinkIcons: failing getimagesize in old phps; blockparser stabilized
1181
1182  Revision 1.116  2004/07/01 09:05:41  rurban
1183  support pages and exclude arguments for all 4 dump methods
1184
1185  Revision 1.115  2004/07/01 08:51:22  rurban
1186  dumphtml: added exclude, print pagename before processing
1187
1188  Revision 1.114  2004/06/28 12:51:41  rurban
1189  improved dumphtml and virgin setup
1190
1191  Revision 1.113  2004/06/27 10:26:02  rurban
1192  oci8 patch by Philippe Vanhaesendonck + some ADODB notes+fixes
1193
1194  Revision 1.112  2004/06/25 14:29:20  rurban
1195  WikiGroup refactoring:
1196    global group attached to user, code for not_current user.
1197    improved helpers for special groups (avoid double invocations)
1198  new experimental config option ENABLE_XHTML_XML (fails with IE, and document.write())
1199  fixed a XHTML validation error on userprefs.tmpl
1200
1201  Revision 1.111  2004/06/21 16:38:55  rurban
1202  fixed the StartLoadDump html argument hack.
1203
1204  Revision 1.110  2004/06/21 16:22:30  rurban
1205  add DEFAULT_DUMP_DIR and HTML_DUMP_DIR constants, for easier cmdline dumps,
1206  fixed dumping buttons locally (images/buttons/),
1207  support pages arg for dumphtml,
1208  optional directory arg for dumpserial + dumphtml,
1209  fix a AllPages warning,
1210  show dump warnings/errors on DEBUG,
1211  don't warn just ignore on wikilens pagelist columns, if not loaded.
1212  RateIt pagelist column is called "rating", not "ratingwidget" (Dan?)
1213
1214  Revision 1.109  2004/06/17 11:31:05  rurban
1215  jump back to label after dump/upgrade
1216
1217  Revision 1.108  2004/06/16 12:43:01  rurban
1218  4.0.6 cannot use this errorhandler (not found)
1219
1220  Revision 1.107  2004/06/14 11:31:37  rurban
1221  renamed global $Theme to $WikiTheme (gforge nameclash)
1222  inherit PageList default options from PageList
1223    default sortby=pagename
1224  use options in PageList_Selectable (limit, sortby, ...)
1225  added action revert, with button at action=diff
1226  added option regex to WikiAdminSearchReplace
1227
1228  Revision 1.106  2004/06/13 13:54:25  rurban
1229  Catch fatals on the four dump calls (as file and zip, as html and mimified)
1230  FoafViewer: Check against external requirements, instead of fatal.
1231  Change output for xhtmldumps: using file:// urls to the local fs.
1232  Catch SOAP fatal by checking for GOOGLE_LICENSE_KEY
1233  Import GOOGLE_LICENSE_KEY and FORTUNE_DIR from config.ini.
1234
1235  Revision 1.105  2004/06/08 19:48:16  rurban
1236  fixed foreign setup: no ugly skipped msg for the GenericPages, load english actionpages if translated not found
1237
1238  Revision 1.104  2004/06/08 13:51:57  rurban
1239  some comments only
1240
1241  Revision 1.103  2004/06/08 10:54:46  rurban
1242  better acl dump representation, read back acl and owner
1243
1244  Revision 1.102  2004/06/06 16:58:51  rurban
1245  added more required ActionPages for foreign languages
1246  install now english ActionPages if no localized are found. (again)
1247  fixed default anon user level to be 0, instead of -1
1248    (wrong "required administrator to view this page"...)
1249
1250  Revision 1.101  2004/06/04 20:32:53  rurban
1251  Several locale related improvements suggested by Pierrick Meignen
1252  LDAP fix by John Cole
1253  reanable admin check without ENABLE_PAGEPERM in the admin plugins
1254
1255  Revision 1.100  2004/05/02 21:26:38  rurban
1256  limit user session data (HomePageHandle and auth_dbi have to invalidated anyway)
1257    because they will not survive db sessions, if too large.
1258  extended action=upgrade
1259  some WikiTranslation button work
1260  revert WIKIAUTH_UNOBTAINABLE (need it for main.php)
1261  some temp. session debug statements
1262
1263  Revision 1.99  2004/05/02 15:10:07  rurban
1264  new finally reliable way to detect if /index.php is called directly
1265    and if to include lib/main.php
1266  new global AllActionPages
1267  SetupWiki now loads all mandatory pages: HOME_PAGE, action pages, and warns if not.
1268  WikiTranslation what=buttons for Carsten to create the missing MacOSX buttons
1269  PageGroupTestOne => subpages
1270  renamed PhpWikiRss to PhpWikiRecentChanges
1271  more docs, default configs, ...
1272
1273  Revision 1.98  2004/04/29 23:25:12  rurban
1274  re-ordered locale init (as in 1.3.9)
1275  fixed loadfile with subpages, and merge/restore anyway
1276    (sf.net bug #844188)
1277
1278  Revision 1.96  2004/04/19 23:13:03  zorloc
1279  Connect the rest of PhpWiki to the IniConfig system.  Also the keyword regular expression is not a config setting
1280
1281  Revision 1.95  2004/04/18 01:11:52  rurban
1282  more numeric pagename fixes.
1283  fixed action=upload with merge conflict warnings.
1284  charset changed from constant to global (dynamic utf-8 switching)
1285
1286  Revision 1.94  2004/03/14 16:36:37  rurban
1287  dont load backup files
1288
1289  Revision 1.93  2004/02/26 03:22:05  rurban
1290  also copy css and images with XHTML Dump
1291
1292  Revision 1.92  2004/02/26 02:25:54  rurban
1293  fix empty and #-anchored links in XHTML Dumps
1294
1295  Revision 1.91  2004/02/24 17:19:37  rurban
1296  debugging helpers only
1297
1298  Revision 1.90  2004/02/24 17:09:24  rurban
1299  fixed \r\r\n with dumping on windows
1300
1301  Revision 1.88  2004/02/22 23:20:31  rurban
1302  fixed DumpHtmlToDir,
1303  enhanced sortby handling in PageList
1304    new button_heading th style (enabled),
1305  added sortby and limit support to the db backends and plugins
1306    for paging support (<<prev, next>> links on long lists)
1307
1308  Revision 1.87  2004/01/26 09:17:49  rurban
1309  * changed stored pref representation as before.
1310    the array of objects is 1) bigger and 2)
1311    less portable. If we would import packed pref
1312    objects and the object definition was changed, PHP would fail.
1313    This doesn't happen with an simple array of non-default values.
1314  * use $prefs->retrieve and $prefs->store methods, where retrieve
1315    understands the interim format of array of objects also.
1316  * simplified $prefs->get() and fixed $prefs->set()
1317  * added $user->_userid and class '_WikiUser' portability functions
1318  * fixed $user object ->_level upgrading, mostly using sessions.
1319    this fixes yesterdays problems with loosing authorization level.
1320  * fixed WikiUserNew::checkPass to return the _level
1321  * fixed WikiUserNew::isSignedIn
1322  * added explodePageList to class PageList, support sortby arg
1323  * fixed UserPreferences for WikiUserNew
1324  * fixed WikiPlugin for empty defaults array
1325  * UnfoldSubpages: added pagename arg, renamed pages arg,
1326    removed sort arg, support sortby arg
1327
1328  Revision 1.86  2003/12/02 16:18:26  carstenklapp
1329  Minor enhancement: Provide more meaningful filenames for WikiDB zip
1330  dumps & snapshots.
1331
1332  Revision 1.85  2003/11/30 18:18:13  carstenklapp
1333  Minor code optimization: use include_once instead of require_once
1334  inside functions that might not always called.
1335
1336  Revision 1.84  2003/11/26 20:47:47  carstenklapp
1337  Redo bugfix: My last refactoring broke merge-edit & overwrite
1338  functionality again, should be fixed now. Sorry.
1339
1340  Revision 1.83  2003/11/20 22:18:54  carstenklapp
1341  New feature: h1 during merge-edit displays WikiLink to original page.
1342  Internal changes: Replaced some hackish url-generation code in
1343  function SavePage (for pgsrc merge-edit) with appropriate Button()
1344  calls.
1345
1346  Revision 1.82  2003/11/18 19:48:01  carstenklapp
1347  Fixed missing gettext _() for button name.
1348
1349  Revision 1.81  2003/11/18 18:28:35  carstenklapp
1350  Bugfix: In the Load File function of PhpWikiAdministration: When doing
1351  a "Merge Edit" or "Restore Anyway", page names containing accented
1352  letters (such as locale/de/pgsrc/G%E4steBuch) would produce a file not
1353  found error (Use FilenameForPage funtion to urlencode page names).
1354
1355  Revision 1.80  2003/03/07 02:46:57  dairiki
1356  Omit checks for safe_mode before set_time_limit().  Just prefix the
1357  set_time_limit() calls with @ so that they fail silently if not
1358  supported.
1359
1360  Revision 1.79  2003/02/26 01:56:05  dairiki
1361  Only zip pages with legal pagenames.
1362
1363  Revision 1.78  2003/02/24 02:05:43  dairiki
1364  Fix "n bytes written" message when dumping HTML.
1365
1366  Revision 1.77  2003/02/21 04:12:05  dairiki
1367  Minor fixes for new cached markup.
1368
1369  Revision 1.76  2003/02/16 19:47:17  dairiki
1370  Update WikiDB timestamp when editing or deleting pages.
1371
1372  Revision 1.75  2003/02/15 03:04:30  dairiki
1373  Fix for WikiUser constructor API change.
1374
1375  Revision 1.74  2003/02/15 02:18:04  dairiki
1376  When default language was English (at least), pgsrc was being
1377  loaded twice.
1378
1379  LimitedFileSet: Fix typo/bug. ($include was being ignored.)
1380
1381  SetupWiki(): Fix bugs in loading of $GenericPages.
1382
1383  Revision 1.73  2003/01/28 21:09:17  zorloc
1384  The get_cfg_var() function should only be used when one is
1385  interested in the value from php.ini or similar. Use ini_get()
1386  instead to get the effective value of a configuration variable.
1387  -- Martin Geisler
1388
1389  Revision 1.72  2003/01/03 22:25:53  carstenklapp
1390  Cosmetic fix to "Merge Edit" & "Overwrite" buttons. Added "The PhpWiki
1391  programming team" as author when loading from pgsrc. Source
1392  reformatting.
1393
1394  Revision 1.71  2003/01/03 02:48:05  carstenklapp
1395  function SavePage: Added loadfile options for overwriting or merge &
1396  compare a loaded pgsrc file with an existing page.
1397
1398  function LoadAny: Added a general error message when unable to load a
1399  file instead of defaulting to "Bad file type".
1400
1401  */
1402
1403 // For emacs users
1404 // Local Variables:
1405 // mode: php
1406 // tab-width: 8
1407 // c-basic-offset: 4
1408 // c-hanging-comment-ender-p: nil
1409 // indent-tabs-mode: nil
1410 // End:
1411 ?>