]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/upgrade.php
sqlite autoincrement fix
[SourceForge/phpwiki.git] / lib / upgrade.php
1 <?php //-*-php-*-
2 rcs_id('$Id: upgrade.php,v 1.24 2004-07-05 13:56:22 rurban Exp $');
3
4 /*
5  Copyright 2004 $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 /**
26  * Upgrade the WikiDB and config settings after installing a new 
27  * PhpWiki upgrade.
28  * Status: experimental, no queries for verification yet, no db update,
29  *         no merge conflict
30  *
31  * Installation on an existing PhpWiki database needs some 
32  * additional worksteps. Each step will require multiple pages.
33  *
34  * This is the plan:
35  *  1. Check for new or changed database schema and update it 
36  *     according to some predefined upgrade tables. (medium)
37  *  2. Check for new or changed (localized) pgsrc/ pages and ask 
38  *     for upgrading these. Check timestamps, upgrade silently or 
39  *     show diffs if existing. Overwrite or merge (easy)
40  *  3. Check for new or changed or deprecated index.php/config.ini settings
41  *     and help in upgrading these. (hard)
42  *  3a Convert old-style index.php into config/config.ini. (easy)
43  *  4. Check for changed plugin invocation arguments. (hard)
44  *  5. Check for changed theme variables. (hard)
45  *  6. Convert the automatic update to a class-based multi-page 
46  *     version. (hard)
47
48  * TODO: overwrite=1 link on edit conflicts at end of page to overwrite all.
49  *
50  * @author: Reini Urban
51  */
52 require_once("lib/loadsave.php");
53 //define('DBADMIN_USER','rurban');
54 //define('DBADMIN_PASSWD','');
55
56 /**
57  * TODO: check for the pgsrc_version number, not the revision mtime only
58  */
59 function doPgsrcUpdate(&$request,$pagename,$path,$filename) {
60     $dbi = $request->getDbh(); 
61     $page = $dbi->getPage($pagename);
62     if ($page->exists()) {
63         // check mtime: update automatically if pgsrc is newer
64         $rev = $page->getCurrentRevision();
65         $page_mtime = $rev->get('mtime');
66         $data  = implode("", file($path."/".$filename));
67         if (($parts = ParseMimeifiedPages($data))) {
68             usort($parts, 'SortByPageVersion');
69             reset($parts);
70             $pageinfo = $parts[0];
71             $stat  = stat($path."/".$filename);
72             $new_mtime = @$pageinfo['versiondata']['mtime'];
73             if (!$new_mtime)
74                 $new_mtime = @$pageinfo['versiondata']['lastmodified'];
75             if (!$new_mtime)
76                 $new_mtime = @$pageinfo['pagedata']['date'];
77             if (!$new_mtime)
78                 $new_mtime = $stat[9];
79             if ($new_mtime > $page_mtime) {
80                 echo "$path/$pagename: ",_("newer than the existing page."),
81                     _(" replace "),"($new_mtime &gt; $page_mtime)","<br />\n";
82                 LoadAny($request,$path."/".$filename);
83                 echo "<br />\n";
84             } else {
85                 echo "$path/$pagename: ",_("older than the existing page."),
86                     _(" skipped"),".<br />\n";
87             }
88         } else {
89             echo "$path/$pagename: ",("unknown format."),
90                     _(" skipped"),".<br />\n";
91         }
92     } else {
93         echo sprintf(_("%s does not exist"),$pagename),"<br />\n";
94         LoadAny($request,$path."/".$filename);
95         echo "<br />\n";
96     }
97 }
98
99 /** need the english filename (required precondition: urlencode == urldecode)
100  *  returns the plugin name.
101  */ 
102 function isActionPage($filename) {
103     static $special = array("DebugInfo"         => "_BackendInfo",
104                             "PhpWikiRecentChanges" => "RssFeed",
105                             "ProjectSummary"    => "RssFeed",
106                             "RecentReleases"    => "RssFeed",
107                             );
108     $base = preg_replace("/\..{1,4}$/","",basename($filename));
109     if (isset($special[$base])) return $special[$base];
110     if (FindFile("lib/plugin/".$base.".php",true)) return $base;
111     else return false;
112 }
113
114 function CheckActionPageUpdate(&$request) {
115     echo "<h3>",_("check for necessary ActionPage updates"),"</h3>\n";
116     $dbi = $request->getDbh(); 
117     $path = FindFile('pgsrc');
118     $pgsrc = new fileSet($path);
119     // most actionpages have the same name as the plugin
120     $loc_path = FindLocalizedFile('pgsrc');
121     foreach ($pgsrc->getFiles() as $filename) {
122         if (substr($filename,-1,1) == '~') continue;
123         $pagename = urldecode($filename);
124         if (isActionPage($filename)) {
125             $translation = gettext($pagename);
126             if ($translation == $pagename)
127                 doPgsrcUpdate($request, $pagename, $path, $filename);
128             elseif (FindLocalizedFile('pgsrc/'.urlencode($translation),1))
129                 doPgsrcUpdate($request, $translation, $loc_path, 
130                               urlencode($translation));
131             else
132                 doPgsrcUpdate($request, $pagename, $path, $filename);
133         }
134     }
135 }
136
137 // see loadsave.php for saving new pages.
138 function CheckPgsrcUpdate(&$request) {
139     echo "<h3>",_("check for necessary pgsrc updates"),"</h3>\n";
140     $dbi = $request->getDbh(); 
141     $path = FindLocalizedFile(WIKI_PGSRC);
142     $pgsrc = new fileSet($path);
143     // fixme: verification, ...
144     $isHomePage = false;
145     foreach ($pgsrc->getFiles() as $filename) {
146         if (substr($filename,-1,1) == '~') continue;
147         $pagename = urldecode($filename);
148         // don't ever update the HomePage
149         if (defined(HOME_PAGE))
150             if ($pagename == HOME_PAGE) $isHomePage = true;
151         else
152             if ($pagename == _("HomePage")) $isHomePage = true;
153         if ($pagename == "HomePage") $isHomePage = true;
154         if ($isHomePage) {
155             echo "$path/$pagename: ",_("always skip the HomePage."),
156                 _(" skipped"),".<br />\n";
157             $isHomePage = false;
158             continue;
159         }
160         if (!isActionPage($filename)) {
161             doPgsrcUpdate($request,$pagename,$path,$filename);
162         }
163     }
164     return;
165 }
166
167 /**
168  * TODO: Search table definition in appropriate schema
169  *       and create it.
170  * Supported: mysql and generic SQL, for ADODB and PearDB.
171  */
172 function installTable(&$dbh, $table, $backend_type) {
173     global $DBParams;
174     if (!in_array($DBParams['dbtype'],array('SQL','ADODB'))) return;
175     echo _("MISSING")," ... \n";
176     $backend = &$dbh->_backend->_dbh;
177     /*
178     $schema = findFile("schemas/${backend_type}.sql");
179     if (!$schema) {
180         echo "  ",_("FAILED"),": ",sprintf(_("no schema %s found"),"schemas/${backend_type}.sql")," ... <br />\n";
181         return false;
182     }
183     */
184     extract($dbh->_backend->_table_names);
185     $prefix = isset($DBParams['prefix']) ? $DBParams['prefix'] : '';
186     switch ($table) {
187     case 'session':
188         assert($session_tbl);
189         if ($backend_type == 'mysql') {
190             $dbh->genericQuery("
191 CREATE TABLE $session_tbl (
192         sess_id         CHAR(32) NOT NULL DEFAULT '',
193         sess_data       BLOB NOT NULL,
194         sess_date       INT UNSIGNED NOT NULL,
195         sess_ip         CHAR(15) NOT NULL,
196         PRIMARY KEY (sess_id),
197         INDEX (sess_date)
198 )");
199         } else {
200             $dbh->genericQuery("
201 CREATE TABLE $session_tbl (
202         sess_id         CHAR(32) NOT NULL DEFAULT '',
203         sess_data       ".($backend_type == 'pgsql'?'TEXT':'BLOB')." NOT NULL,
204         sess_date       INT,
205         sess_ip         CHAR(15) NOT NULL
206 )");
207             $dbh->genericQuery("CREATE UNIQUE INDEX sess_id ON $session_tbl (sess_id)");
208         }
209         $dbh->genericQuery("CREATE INDEX sess_date on session (sess_date)");
210         break;
211     case 'user':
212         $user_tbl = $prefix.'user';
213         if ($backend_type == 'mysql') {
214             $dbh->genericQuery("
215 CREATE TABLE $user_tbl (
216         userid  CHAR(48) BINARY NOT NULL UNIQUE,
217         passwd  CHAR(48) BINARY DEFAULT '',
218         PRIMARY KEY (userid)
219 )");
220         } else {
221             $dbh->genericQuery("
222 CREATE TABLE $user_tbl (
223         userid  CHAR(48) NOT NULL,
224         passwd  CHAR(48) DEFAULT ''
225 )");
226             $dbh->genericQuery("CREATE UNIQUE INDEX userid ON $user_tbl (userid)");
227         }
228         break;
229     case 'pref':
230         $pref_tbl = $prefix.'pref';
231         if ($backend_type == 'mysql') {
232             $dbh->genericQuery("
233 CREATE TABLE $pref_tbl (
234         userid  CHAR(48) BINARY NOT NULL UNIQUE,
235         prefs   TEXT NULL DEFAULT '',
236         PRIMARY KEY (userid)
237 )");
238         } else {
239             $dbh->genericQuery("
240 CREATE TABLE $pref_tbl (
241         userid  CHAR(48) NOT NULL,
242         prefs   TEXT NULL DEFAULT '',
243 )");
244             $dbh->genericQuery("CREATE UNIQUE INDEX userid ON $pref_tbl (userid)");
245         }
246         break;
247     case 'member':
248         $member_tbl = $prefix.'member';
249         if ($backend_type == 'mysql') {
250             $dbh->genericQuery("
251 CREATE TABLE $member_tbl (
252         userid    CHAR(48) BINARY NOT NULL,
253         groupname CHAR(48) BINARY NOT NULL DEFAULT 'users',
254         INDEX (userid),
255         INDEX (groupname)
256 )");
257         } else {
258             $dbh->genericQuery("
259 CREATE TABLE $member_tbl (
260         userid    CHAR(48) NOT NULL,
261         groupname CHAR(48) NOT NULL DEFAULT 'users',
262 )");
263             $dbh->genericQuery("CREATE INDEX userid ON $member_tbl (userid)");
264             $dbh->genericQuery("CREATE INDEX groupname ON $member_tbl (groupname)");
265         }
266         break;
267     case 'rating':
268         $rating_tbl = $prefix.'rating';
269         if ($backend_type == 'mysql') {
270             $dbh->genericQuery("
271 CREATE TABLE $rating_tbl (
272         dimension INT(4) NOT NULL,
273         raterpage INT(11) NOT NULL,
274         rateepage INT(11) NOT NULL,
275         ratingvalue FLOAT NOT NULL,
276         rateeversion INT(11) NOT NULL,
277         tstamp TIMESTAMP(14) NOT NULL,
278         PRIMARY KEY (dimension, raterpage, rateepage)
279 )");
280         } else {
281             $dbh->genericQuery("
282 CREATE TABLE $rating_tbl (
283         dimension INT(4) NOT NULL,
284         raterpage INT(11) NOT NULL,
285         rateepage INT(11) NOT NULL,
286         ratingvalue FLOAT NOT NULL,
287         rateeversion INT(11) NOT NULL,
288         tstamp TIMESTAMP(14) NOT NULL,
289 )");
290             $dbh->genericQuery("CREATE UNIQUE INDEX rating ON $rating_tbl (dimension, raterpage, rateepage)");
291         }
292         break;
293     }
294     echo "  ",_("CREATED"),"<br />\n";
295 }
296
297 /**
298  * currently update only session, user, pref and member
299  * jeffs-hacks database api (around 1.3.2) later
300  *   people should export/import their pages if using that old versions.
301  */
302 function CheckDatabaseUpdate(&$request) {
303     global $DBParams, $DBAuthParams;
304     if (!in_array($DBParams['dbtype'], array('SQL','ADODB'))) return;
305     echo "<h3>",_("check for necessary database updates"),"</h3>\n";
306     if (defined('DBADMIN_USER') and DBADMIN_USER) {
307         // if need to connect as the root user, for alter permissions
308         $AdminParams = $DBParams;
309         if ($DBParams['dbtype'] == 'SQL')
310             $dsn = DB::parseDSN($AdminParams['dsn']);
311         else
312             $dsn = parseDSN($AdminParams['dsn']);
313         $AdminParams['dsn'] = sprintf("%s://%s:%s@%s/%s",
314                                       $dsn['phptype'],
315                                       DBADMIN_USER,
316                                       DBADMIN_PASSWD,
317                                       $dsn['hostspec'],
318                                       $dsn['database']);
319         $dbh = WikiDB::open($AdminParams);
320     } else {
321         $dbh = &$request->_dbi;
322     }
323     $tables = $dbh->_backend->listOfTables();
324     $backend_type = $dbh->_backend->backendType();
325     $prefix = isset($DBParams['prefix']) ? $DBParams['prefix'] : '';
326     extract($dbh->_backend->_table_names);
327     foreach (explode(':','session:user:pref:member') as $table) {
328         echo sprintf(_("check for table %s"), $table)," ...";
329         if (!in_array($prefix.$table, $tables)) {
330             installTable($dbh, $table, $backend_type);
331         } else {
332             echo _("OK")," <br />\n";
333         }
334     }
335     $backend = &$dbh->_backend->_dbh;
336     // 1.3.8 added session.sess_ip
337     if (phpwiki_version() >= 1030.08 and USE_DB_SESSION and isset($request->_dbsession)) {
338         echo _("check for new session.sess_ip column")," ... ";
339         $database = $dbh->_backend->database();
340         assert(!empty($DBParams['db_session_table']));
341         $session_tbl = $prefix . $DBParams['db_session_table'];
342         $sess_fields = $dbh->_backend->listOfFields($database, $session_tbl);
343         if (!strstr(strtolower(join(':', $sess_fields)),"sess_ip")) {
344             echo "<b>",_("ADDING"),"</b>"," ... ";              
345             $dbh->genericQuery("ALTER TABLE $session_tbl ADD sess_ip CHAR(15) NOT NULL");
346         } else {
347             echo _("OK");
348         }
349         echo "<br />\n";
350     }
351     // 1.3.10 mysql requires page.id auto_increment
352     // mysql, mysqli or mysqlt
353     if (phpwiki_version() >= 1030.099 and substr($backend_type,0,5) == 'mysql') {
354         echo _("check for page.id auto_increment flag")," ...";
355         assert(!empty($page_tbl));
356         $database = $dbh->_backend->database();
357         $fields = mysql_list_fields($database, $page_tbl, $dbh->_backend->connection());
358         $columns = mysql_num_fields($fields); 
359         for ($i = 0; $i < $columns; $i++) {
360             if (mysql_field_name($fields, $i) == 'id') {
361                 $flags = mysql_field_flags($fields, $i);
362                 //FIXME: something wrong with ADODB here!
363                 if (!strstr(strtolower($flags),"auto_increment")) {
364                     echo "<b>",_("ADDING"),"</b>"," ... ";              
365                     // MODIFY col_def valid since mysql 3.22.16,
366                     // older mysql's need CHANGE old_col col_def
367                     $dbh->genericQuery("ALTER TABLE $page_tbl CHANGE id id INT NOT NULL AUTO_INCREMENT");
368                     $fields = mysql_list_fields($database, $page_tbl);
369                     if (!strstr(strtolower(mysql_field_flags($fields, $i)),"auto_increment"))
370                         echo " <b><font color=\"red\">",_("FAILED"),"</font></b><br />\n";
371                     else     
372                         echo _("OK"),"<br />\n";
373                 } else {
374                     echo _("OK"),"<br />\n";                            
375                 }
376                 break;
377             }
378         }
379         mysql_free_result($fields);
380     }
381     // check for mysql 4.1.x/5.0.0a binary search bug.
382     //   http://bugs.mysql.com/bug.php?id=4398
383     // "select * from page where LOWER(pagename) like '%search%'" does not apply LOWER!
384     // confirmed for 4.1.0alpha,4.1.3-beta,5.0.0a; not yet tested for 4.1.2alpha,
385     if (substr($backend_type,0,5) == 'mysql') {
386         echo _("check for mysql 4.1.x/5.0.0 binary search problem")," ...";
387         $result = mysql_query("SELECT VERSION()",$dbh->_backend->connection());
388         $row = mysql_fetch_row($result);
389         $mysql_version = $row[0];
390         $arr = explode('.',$mysql_version);
391         $version = (string)(($arr[0] * 100) + $arr[1]) . "." . (integer)$arr[2];
392         if ($version >= 401.0) {
393             $dbh->genericQuery("ALTER TABLE $page_tbl CHANGE pagename pagename VARCHAR(100) NOT NULL;");
394             echo sprintf(_("version <em>%s</em> <b>FIXED</b>"), $mysql_version),"<br />\n";     
395         } else {
396             echo sprintf(_("version <em>%s</em> not affected"), $mysql_version),"<br />\n";
397         }
398     }
399     return;
400 }
401
402 function fixConfigIni($match, $new) {
403     $file = FindFile("config/config.ini");
404     $found = false;
405     if (is_writable($file)) {
406         $in = fopen($file,"rb");
407         $out = fopen($tmp = tempnam(FindFile("uploads"),"cfg"),"wb");
408         if (isWindows())
409             $tmp = str_replace("/","\\",$tmp);
410         while ($s = fgets($in)) {
411             if (preg_match($match, $s)) {
412                 $s = $new . (isWindows() ? "\r\n" : "\n");
413                 $found = true;
414             }
415             fputs($out, $s);
416         }
417         fclose($in);
418         fclose($out);
419         if (!$found) {
420             echo " <b><font color=\"red\">",_("FAILED"),"</font></b>: ",
421                 sprintf(_("%s not found"), $match);
422             unlink($out);
423         } else {
424             @unlink("$file.bak");
425             @rename($file,"$file.bak");
426             if (rename($tmp, $file))
427                 echo " <b>",_("FIXED"),"</b>";
428             else {
429                 echo " <b>",_("FAILED"),"</b>: ";
430                 sprintf(_("couldn't move %s to %s"), $tmp, $file);
431                 return false;
432             }
433         }
434         return $found;
435     } else {
436         echo " <b><font color=\"red\">",_("FAILED"),"</font></b>: ",
437             sprintf(_("%s is not writable"), $file);
438         return false;
439     }
440 }
441
442 function CheckConfigUpdate(&$request) {
443     echo "<h3>",_("check for necessary config updates"),"</h3>\n";
444     echo _("check for old CACHE_CONTROL = NONE")," ... ";
445     if (defined('CACHE_CONTROL') and CACHE_CONTROL == '') {
446         echo "<br />&nbsp;&nbsp;",_("CACHE_CONTROL is set to 'NONE', and must be changed to 'NO_CACHE'")," ...";
447         fixConfigIni("/^\s*CACHE_CONTROL\s*=\s*NONE/","CACHE_CONTROL = NO_CACHE");
448     } else {
449         echo _("OK");
450     }
451     echo "<br />\n";
452 }
453
454 /**
455  * TODO:
456  *
457  * Upgrade: Base class for multipage worksteps
458  * identify, validate, display options, next step
459  */
460 class Upgrade {
461 }
462
463 class Upgrade_CheckPgsrc extends Upgrade {
464 }
465
466 class Upgrade_CheckDatabaseUpdate extends Upgrade {
467 }
468
469 // TODO: At which step are we? 
470 // validate and do it again or go on with next step.
471
472 /** entry function from lib/main.php
473  */
474 function DoUpgrade($request) {
475
476     if (!$request->_user->isAdmin()) {
477         $request->_notAuthorized(WIKIAUTH_ADMIN);
478         $request->finish(
479                          HTML::div(array('class' => 'disabled-plugin'),
480                                    fmt("Upgrade disabled: user != isAdmin")));
481         return;
482     }
483
484     StartLoadDump($request, _("Upgrading this PhpWiki"));
485     CheckActionPageUpdate($request);
486     CheckDatabaseUpdate($request);
487     CheckPgsrcUpdate($request);
488     //CheckThemeUpdate($request);
489     CheckConfigUpdate($request);
490     EndLoadDump($request);
491 }
492
493
494 /**
495  $Log: not supported by cvs2svn $
496  Revision 1.23  2004/07/04 10:28:06  rurban
497  DBADMIN_USER fix
498
499  Revision 1.22  2004/07/03 17:21:28  rurban
500  updated docs: submitted new mysql bugreport (#1491 did not fix it)
501
502  Revision 1.21  2004/07/03 16:51:05  rurban
503  optional DBADMIN_USER:DBADMIN_PASSWD for action=upgrade (if no ALTER permission)
504  added atomic mysql REPLACE for PearDB as in ADODB
505  fixed _lock_tables typo links => link
506  fixes unserialize ADODB bug in line 180
507
508  Revision 1.20  2004/07/03 14:48:18  rurban
509  Tested new mysql 4.1.3-beta: binary search bug as fixed.
510  => fixed action=upgrade,
511  => version check in PearDB also (as in ADODB)
512
513  Revision 1.19  2004/06/19 12:19:09  rurban
514  slightly improved docs
515
516  Revision 1.18  2004/06/19 11:47:17  rurban
517  added CheckConfigUpdate: CACHE_CONTROL = NONE => NO_CACHE
518
519  Revision 1.17  2004/06/17 11:31:50  rurban
520  check necessary localized actionpages
521
522  Revision 1.16  2004/06/16 10:38:58  rurban
523  Disallow refernces in calls if the declaration is a reference
524  ("allow_call_time_pass_reference clean").
525    PhpWiki is now allow_call_time_pass_reference = Off clean,
526    but several external libraries may not.
527    In detail these libs look to be affected (not tested):
528    * Pear_DB odbc
529    * adodb oracle
530
531  Revision 1.15  2004/06/07 19:50:40  rurban
532  add owner field to mimified dump
533
534  Revision 1.14  2004/06/07 18:38:18  rurban
535  added mysql 4.1.x search fix
536
537  Revision 1.13  2004/06/04 20:32:53  rurban
538  Several locale related improvements suggested by Pierrick Meignen
539  LDAP fix by John Cole
540  reanable admin check without ENABLE_PAGEPERM in the admin plugins
541
542  Revision 1.12  2004/05/18 13:59:15  rurban
543  rename simpleQuery to genericQuery
544
545  Revision 1.11  2004/05/15 13:06:17  rurban
546  skip the HomePage, at first upgrade the ActionPages, then the database, then the rest
547
548  Revision 1.10  2004/05/15 01:19:41  rurban
549  upgrade prefix fix by Kai Krakow
550
551  Revision 1.9  2004/05/14 11:33:03  rurban
552  version updated to 1.3.11pre
553  upgrade stability fix
554
555  Revision 1.8  2004/05/12 10:49:55  rurban
556  require_once fix for those libs which are loaded before FileFinder and
557    its automatic include_path fix, and where require_once doesn't grok
558    dirname(__FILE__) != './lib'
559  upgrade fix with PearDB
560  navbar.tmpl: remove spaces for IE &nbsp; button alignment
561
562  Revision 1.7  2004/05/06 17:30:38  rurban
563  CategoryGroup: oops, dos2unix eol
564  improved phpwiki_version:
565    pre -= .0001 (1.3.10pre: 1030.099)
566    -p1 += .001 (1.3.9-p1: 1030.091)
567  improved InstallTable for mysql and generic SQL versions and all newer tables so far.
568  abstracted more ADODB/PearDB methods for action=upgrade stuff:
569    backend->backendType(), backend->database(),
570    backend->listOfFields(),
571    backend->listOfTables(),
572
573  Revision 1.6  2004/05/03 15:05:36  rurban
574  + table messages
575
576  Revision 1.4  2004/05/02 21:26:38  rurban
577  limit user session data (HomePageHandle and auth_dbi have to invalidated anyway)
578    because they will not survive db sessions, if too large.
579  extended action=upgrade
580  some WikiTranslation button work
581  revert WIKIAUTH_UNOBTAINABLE (need it for main.php)
582  some temp. session debug statements
583
584  Revision 1.3  2004/04/29 22:33:30  rurban
585  fixed sf.net bug #943366 (Kai Krakow)
586    couldn't load localized url-undecoded pagenames
587
588  Revision 1.2  2004/03/12 15:48:07  rurban
589  fixed explodePageList: wrong sortby argument order in UnfoldSubpages
590  simplified lib/stdlib.php:explodePageList
591
592  */
593
594 // For emacs users
595 // Local Variables:
596 // mode: php
597 // tab-width: 8
598 // c-basic-offset: 4
599 // c-hanging-comment-ender-p: nil
600 // indent-tabs-mode: nil
601 // End:
602 ?>