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