]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/upgrade.php
Honor predefined ->_rows: hack to (re-)allow non-pagenames, used by WikiAdminUtils
[SourceForge/phpwiki.git] / lib / upgrade.php
1 <?php //-*-php-*-
2 rcs_id('$Id: upgrade.php,v 1.57 2007-01-04 16:43:09 rurban Exp $');
3 /*
4  Copyright 2004,2005,2006,2007 $ThePhpWikiProgrammingTeam
5
6  This file is part of PhpWiki.
7
8  PhpWiki is free software; you can redistribute it and/or modify
9  it under the terms of the GNU General Public License as published by
10  the Free Software Foundation; either version 2 of the License, or
11  (at your option) any later version.
12
13  PhpWiki is distributed in the hope that it will be useful,
14  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  GNU General Public License for more details.
17
18  You should have received a copy of the GNU General Public License
19  along with PhpWiki; if not, write to the Free Software
20  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
22
23 /**
24  * Upgrade the WikiDB and config settings after installing a new 
25  * PhpWiki upgrade.
26  * Status: experimental, no queries for verification yet, 
27  *         no merge conflict resolution (patch?), just overwrite.
28  *
29  * Installation on an existing PhpWiki database needs some 
30  * additional worksteps. Each step will require multiple pages.
31  *
32  * This is the plan:
33  *  1. Check for new or changed database schema and update it 
34  *     according to some predefined upgrade tables. (medium, complete)
35  *  2. Check for new or changed (localized) pgsrc/ pages and ask 
36  *     for upgrading these. Check timestamps, upgrade silently or 
37  *     show diffs if existing. Overwrite or merge (easy, complete)
38  *  3. Check for new or changed or deprecated index.php/config.ini settings
39  *     and help in upgrading these. (hard)
40  *  3a Convert old-style index.php into config/config.ini. (easy)
41  *  4. Check for changed plugin invocation arguments. (hard)
42  *  5. Check for changed theme variables. (hard)
43  *  6. Convert the single-request upgrade to a class-based multi-page 
44  *     version. (hard)
45
46  * Done: overwrite=1 link on edit conflicts at first occurence "Overwrite all".
47  *
48  * @author: Reini Urban
49  */
50 require_once("lib/loadsave.php");
51
52 class Upgrade {
53
54     function Upgrade (&$request) {
55         $this->request =& $request;
56         $this->dbi =& $request->_dbi; // no reference for dbadmin ? 
57     }
58
59     /**
60      * TODO: check for the pgsrc_version number, not the revision mtime only
61      */
62     function doPgsrcUpdate($pagename,$path,$filename) {
63         $page = $this->dbi->getPage($pagename);
64         if ($page->exists()) {
65             // check mtime: update automatically if pgsrc is newer
66             $rev = $page->getCurrentRevision();
67             $page_mtime = $rev->get('mtime');
68             $data  = implode("", file($path."/".$filename));
69             if (($parts = ParseMimeifiedPages($data))) {
70                 usort($parts, 'SortByPageVersion');
71                 reset($parts);
72                 $pageinfo = $parts[0];
73                 $stat  = stat($path."/".$filename);
74                 $new_mtime = @$pageinfo['versiondata']['mtime'];
75                 if (!$new_mtime)
76                     $new_mtime = @$pageinfo['versiondata']['lastmodified'];
77                 if (!$new_mtime)
78                     $new_mtime = @$pageinfo['pagedata']['date'];
79                 if (!$new_mtime)
80                     $new_mtime = $stat[9];
81                 if ($new_mtime > $page_mtime) {
82                     echo "$path/$pagename: ",_("newer than the existing page."),
83                         _(" replace "),"($new_mtime &gt; $page_mtime)","<br />\n";
84                     LoadAny($this->request, $path."/".$filename);
85                     echo "<br />\n";
86                 } else {
87                     echo "$path/$pagename: ",_("older than the existing page."),
88                         _(" skipped"),".<br />\n";
89                 }
90             } else {
91                 echo "$path/$pagename: ",("unknown format."),
92                     _(" skipped"),".<br />\n";
93             }
94         } else {
95             echo sprintf(_("%s does not exist"),$pagename),"<br />\n";
96             LoadAny($this->request, $path."/".$filename);
97             echo "<br />\n";
98         }
99     }
100
101     /** 
102      *  If a matching pgsrc => pluginname exists
103      *  Need the english filename (required precondition: urlencode == urldecode).
104      *  Returns the plugin name.
105      */ 
106     function isActionPage($filename) {
107         static $special = array("DebugInfo"     => "_BackendInfo",
108                                 "PhpWikiRecentChanges" => "RssFeed",
109                                 "ProjectSummary"        => "RssFeed",
110                                 "RecentReleases"        => "RssFeed",
111                                 "InterWikiMap"      => "InterWikiMap",
112                                 );
113         $base = preg_replace("/\..{1,4}$/","",basename($filename));
114         if (isset($special[$base])) return $special[$base];
115         if (FindFile("lib/plugin/".$base.".php",true)) return $base;
116         else return false;
117     }
118
119     function CheckActionPageUpdate() {
120         echo "<h3>",sprintf(_("check for necessary %s updates"),
121                             _("ActionPage")),"</h3>\n";
122         // 1.3.13 before we pull in all missing pages, we rename existing ones
123         $this->_rename_page_helper(_("_AuthInfo"), _("DebugAuthInfo"));
124         // this is in some templates. so we keep the old name
125         //$this->_rename_page_helper($this->dbi, _("DebugInfo"), _("DebugBackendInfo")); 
126         $this->_rename_page_helper(_("_GroupInfo"), _("GroupAuthInfo")); //never officially existed
127         $this->_rename_page_helper("InterWikiKarte", "InterWikiListe"); // german only
128  
129         $path = FindFile('pgsrc');
130         $pgsrc = new fileSet($path);
131         // most actionpages have the same name as the plugin
132         $loc_path = FindLocalizedFile('pgsrc');
133         foreach ($pgsrc->getFiles() as $filename) {
134             if (substr($filename,-1,1) == '~') continue;
135             if (substr($filename,-5,5) == '.orig') continue;
136             $pagename = urldecode($filename);
137             if ($this->isActionPage($filename)) {
138                 $translation = gettext($pagename);
139                 if ($translation == $pagename)
140                     $this->doPgsrcUpdate($pagename, $path, $filename);
141                 elseif (FindLocalizedFile('pgsrc/'.urlencode($translation),1))
142                     $this->doPgsrcUpdate($translation, $loc_path, urlencode($translation));
143                 else
144                     $this->doPgsrcUpdate($pagename, $path, $filename);
145             }
146         }
147     }
148
149     // see loadsave.php for saving new pages.
150     function CheckPgsrcUpdate() {
151         echo "<h3>",sprintf(_("check for necessary %s updates"),
152                             "pgsrc"),"</h3>\n";
153         if ($this->dbi->get_db_version() < 1030.12200612) {
154             echo "<h4>",_("rename to Help: pages"),"</h4>\n";
155         }
156         $path = FindLocalizedFile(WIKI_PGSRC);
157         $pgsrc = new fileSet($path);
158         // fixme: verification, ...
159         $isHomePage = false;
160         foreach ($pgsrc->getFiles() as $filename) {
161             if (substr($filename,-1,1) == '~') continue;
162             if (substr($filename,-5,5) == '.orig') continue;
163             $pagename = urldecode($filename);
164             // don't ever update the HomePage
165             if (defined(HOME_PAGE))
166                 if ($pagename == HOME_PAGE) $isHomePage = true;
167                 else
168                     if ($pagename == _("HomePage")) $isHomePage = true;
169             if ($pagename == "HomePage") $isHomePage = true;
170             if ($isHomePage) {
171                 echo "$path/$pagename: ",_("always skip the HomePage."),
172                     _(" skipped"),".<br />\n";
173                 $isHomePage = false;
174                 continue;
175             }
176             if (!$this->isActionPage($filename)) {
177                 // There're a lot of now unneeded pages around. 
178                 // At first rename the BlaPlugin pages to Help/<pagename> and then to the update.
179                 if ($this->dbi->get_db_version() < 1030.12200612) {
180                     $this->_rename_to_help_page($pagename);    
181                 }
182                 $this->doPgsrcUpdate($pagename,$path,$filename);
183             }
184         }
185         return;
186     }
187
188     function _rename_page_helper($oldname, $pagename) {
189         echo sprintf(_("rename %s to %s"), $oldname, $pagename)," ...";
190         if ($this->dbi->isWikiPage($oldname) and !$this->dbi->isWikiPage($pagename)) {
191             if ($this->dbi->_backend->rename_page($oldname, $pagename))
192                 echo _("OK")," <br />\n";
193             else
194                 echo " <b><font color=\"red\">", _("FAILED"), "</font></b>",
195                     " <br />\n";
196         } else {
197             echo _(" skipped")," <br />\n";
198         }
199     }
200
201     function _rename_to_help_page($pagename) {
202         $newprefix = _("Help") . "/";
203         if (substr($pagename,0,strlen($newprefix)) != $newprefix) return;       
204         $oldname = substr($pagename,strlen($newprefix));
205         $this->_rename_page_helper($oldname, $pagename);
206     }
207
208     /**
209      * TODO: Search table definition in appropriate schema
210      *       and create it.
211      * Supported: mysql and generic SQL, for ADODB and PearDB.
212      */
213     function installTable($table, $backend_type) {
214         global $DBParams;
215         if (!$this->dbi->_backend->isSQL()) return;
216         echo _("MISSING")," ... \n";
217         $backend = &$this->dbi->_backend->_dbh;
218         /*
219           $schema = findFile("schemas/${backend_type}.sql");
220           if (!$schema) {
221           echo "  ",_("FAILED"),": ",sprintf(_("no schema %s found"),
222           "schemas/${backend_type}.sql")," ... <br />\n";
223           return false;
224           }
225         */
226         extract($this->dbi->_backend->_table_names);
227         $prefix = isset($DBParams['prefix']) ? $DBParams['prefix'] : '';
228         switch ($table) {
229         case 'session':
230             assert($session_tbl);
231             if ($backend_type == 'mysql') {
232                 $this->dbi->genericSqlQuery("
233 CREATE TABLE $session_tbl (
234         sess_id         CHAR(32) NOT NULL DEFAULT '',
235         sess_data       BLOB NOT NULL,
236         sess_date       INT UNSIGNED NOT NULL,
237         sess_ip         CHAR(15) NOT NULL,
238         PRIMARY KEY (sess_id),
239         INDEX (sess_date)
240 )");
241             } else {
242                 $this->dbi->genericSqlQuery("
243 CREATE TABLE $session_tbl (
244         sess_id         CHAR(32) NOT NULL DEFAULT '',
245         sess_data       ".($backend_type == 'pgsql'?'TEXT':'BLOB')." NOT NULL,
246         sess_date       INT,
247         sess_ip         CHAR(15) NOT NULL
248 )");
249                 $this->dbi->genericSqlQuery("CREATE UNIQUE INDEX sess_id ON $session_tbl (sess_id)");
250             }
251             $this->dbi->genericSqlQuery("CREATE INDEX sess_date on session (sess_date)");
252             echo "  ",_("CREATED");
253             break;
254         case 'pref':
255             $pref_tbl = $prefix.'pref';
256             if ($backend_type == 'mysql') {
257                 $this->dbi->genericSqlQuery("
258 CREATE TABLE $pref_tbl (
259         userid  CHAR(48) BINARY NOT NULL UNIQUE,
260         prefs   TEXT NULL DEFAULT '',
261         PRIMARY KEY (userid)
262 )");
263             } else {
264                 $this->dbi->genericSqlQuery("
265 CREATE TABLE $pref_tbl (
266         userid  CHAR(48) NOT NULL,
267         prefs   TEXT NULL DEFAULT ''
268 )");
269                 $this->dbi->genericSqlQuery("CREATE UNIQUE INDEX userid ON $pref_tbl (userid)");
270             }
271             echo "  ",_("CREATED");
272             break;
273         case 'member':
274             $member_tbl = $prefix.'member';
275             if ($backend_type == 'mysql') {
276                 $this->dbi->genericSqlQuery("
277 CREATE TABLE $member_tbl (
278         userid    CHAR(48) BINARY NOT NULL,
279         groupname CHAR(48) BINARY NOT NULL DEFAULT 'users',
280         INDEX (userid),
281         INDEX (groupname)
282 )");
283             } else {
284                 $this->dbi->genericSqlQuery("
285 CREATE TABLE $member_tbl (
286         userid    CHAR(48) NOT NULL,
287         groupname CHAR(48) NOT NULL DEFAULT 'users'
288 )");
289                 $this->dbi->genericSqlQuery("CREATE INDEX userid ON $member_tbl (userid)");
290                 $this->dbi->genericSqlQuery("CREATE INDEX groupname ON $member_tbl (groupname)");
291             }
292             echo "  ",_("CREATED");
293             break;
294         case 'rating':
295             $rating_tbl = $prefix.'rating';
296             if ($backend_type == 'mysql') {
297                 $this->dbi->genericSqlQuery("
298 CREATE TABLE $rating_tbl (
299         dimension INT(4) NOT NULL,
300         raterpage INT(11) NOT NULL,
301         rateepage INT(11) NOT NULL,
302         ratingvalue FLOAT NOT NULL,
303         rateeversion INT(11) NOT NULL,
304         tstamp TIMESTAMP(14) NOT NULL,
305         PRIMARY KEY (dimension, raterpage, rateepage)
306 )");
307             } else {
308                 $this->dbi->genericSqlQuery("
309 CREATE TABLE $rating_tbl (
310         dimension INT(4) NOT NULL,
311         raterpage INT(11) NOT NULL,
312         rateepage INT(11) NOT NULL,
313         ratingvalue FLOAT NOT NULL,
314         rateeversion INT(11) NOT NULL,
315         tstamp TIMESTAMP(14) NOT NULL
316 )");
317                 $this->dbi->genericSqlQuery("CREATE UNIQUE INDEX rating"
318                                       ." ON $rating_tbl (dimension, raterpage, rateepage)");
319             }
320             echo "  ",_("CREATED");
321             break;
322         case 'accesslog':
323             $log_tbl = $prefix.'accesslog';
324             // fields according to http://www.outoforder.cc/projects/apache/mod_log_sql/docs-2.0/#id2756178
325             /*
326               A User Agent agent        varchar(255)    Mozilla/4.0 (compat; MSIE 6.0; Windows)
327               a CGi request arguments   request_args    varchar(255)    user=Smith&cart=1231&item=532
328               b Bytes transfered        bytes_sent      int unsigned    32561
329               c???      Text of cookie  cookie  varchar(255)    Apache=sdyn.fooonline.net 1300102700823
330               f Local filename requested        request_file    varchar(255)    /var/www/html/books-cycroad.html
331               H HTTP request_protocol   request_protocol        varchar(10)     HTTP/1.1
332               h Name of remote host     remote_host     varchar(50)     blah.foobar.com
333               I Request ID (from modd_unique_id)        id      char(19)        POlFcUBRH30AAALdBG8
334               l Ident user info remote_logname  varcgar(50)     bobby
335               M Machine ID???   machine_id      varchar(25)     web01
336               m HTTP request method     request_method  varchar(10)     GET
337               P httpd cchild PID        child_pid       smallint unsigned       3215
338               p http port       server_port     smallint unsigned       80
339               R Referer referer varchar(255)    http://www.biglinks4u.com/linkpage.html
340               r Request in full form    request_line    varchar(255)    GET /books-cycroad.html HTTP/1.1
341               S Time of request in UNIX time_t format   time_stamp      int unsigned    1005598029
342               T Seconds to service request      request_duration        smallint unsigned       2
343               t Time of request in human format request_time    char(28)        [02/Dec/2001:15:01:26 -0800]
344               U Request in simple form  request_uri     varchar(255)    /books-cycroad.html
345               u User info from HTTP auth        remote_user     varchar(50)     bobby
346               v Virtual host servicing the request      virtual_host    varchar(255)
347             */
348             $this->dbi->genericSqlQuery("
349 CREATE TABLE $log_tbl (
350         time_stamp    int unsigned,
351         remote_host   varchar(100),
352         remote_user   varchar(50),
353         request_method varchar(10),
354         request_line  varchar(255),
355         request_args  varchar(255),
356         request_uri   varchar(255),
357         request_time  char(28),
358         status        smallint unsigned,
359         bytes_sent    smallint unsigned,
360         referer       varchar(255), 
361         agent         varchar(255),
362         request_duration float
363 )");
364             $this->dbi->genericSqlQuery("CREATE INDEX log_time ON $log_tbl (time_stamp)");
365             $this->dbi->genericSqlQuery("CREATE INDEX log_host ON $log_tbl (remote_host)");
366             echo "  ",_("CREATED");
367             break;
368         }
369         echo "<br />\n";
370     }
371
372     /**
373      * Update from ~1.3.4 to current.
374      * tables: Only session, user, pref and member
375      * jeffs-hacks database api (around 1.3.2) later:
376      *   people should export/import their pages if using that old versions.
377      */
378     function CheckDatabaseUpdate() {
379         global $DBAuthParams;
380
381         echo "<h3>",sprintf(_("check for necessary %s updates"),
382                             _("database")),
383             " - ", DATABASE_TYPE,"</h3>\n";
384         $dbadmin = $this->request->getArg('dbadmin');
385         if ($this->dbi->_backend->isSQL()) {
386             $this->_db_init();
387             if (isset($dbadmin['cancel'])) {
388                 echo _("CANCEL")," <br />\n";
389                 return;
390             }
391         }
392
393         if ($this->dbi->_backend->isSQL()) {
394             $backend_type = $this->dbi->_backend->backendType();
395             echo "<h4>",_("Backend type: "),$backend_type,"</h4>\n";
396             $prefix = isset($DBParams['prefix']) ? $DBParams['prefix'] : '';
397             $tables = $this->dbi->_backend->listOfTables();
398             foreach (explode(':','session:pref:member') as $table) {
399                 echo sprintf(_("check for table %s"), $table)," ...";
400                 if (!in_array($prefix.$table, $tables)) {
401                     $this->installTable($table, $backend_type);
402                 } else {
403                     echo _("OK")," <br />\n";
404                 }
405             }
406         }
407
408         if (phpwiki_version() >= 1030.12200612 and $this->dbi->get_db_version() < 1030.13) {
409             //if ($this->dbi->_backend->isSQL() and preg_match("/(pgsql|postgres)/", $backend_type))
410             //  $this->_upgrade_psql_tsearch2();
411             $this->_upgrade_relation_links();
412         }
413
414         if (!$this->dbi->_backend->isSQL()) return;
415
416         if (ACCESS_LOG_SQL) {
417             $table = "accesslog";
418             echo sprintf(_("check for table %s"), $table)," ...";
419             if (!in_array($prefix.$table, $tables)) {
420                 $this->installTable($table, $backend_type);
421             } else {
422                 echo _("OK")," <br />\n";
423             }
424         }
425         if ((class_exists("RatingsUserFactory") or $this->dbi->isWikiPage(_("RateIt")))) {
426             $table = "rating";
427             echo sprintf(_("check for table %s"), $table)," ...";
428             if (!in_array($prefix.$table, $tables)) {
429                 $this->installTable($table, $backend_type);
430             } else {
431                 echo _("OK")," <br />\n";
432             }
433         }
434         $backend = &$this->dbi->_backend->_dbh;
435         extract($this->dbi->_backend->_table_names);
436
437         // 1.3.8 added session.sess_ip
438         if (phpwiki_version() >= 1030.08 and USE_DB_SESSION and isset($this->request->_dbsession)) {
439             echo _("check for new session.sess_ip column")," ... ";
440             $database = $this->dbi->_backend->database();
441             assert(!empty($DBParams['db_session_table']));
442             $session_tbl = $prefix . $DBParams['db_session_table'];
443             $sess_fields = $this->dbi->_backend->listOfFields($database, $session_tbl);
444             if (!$sess_fields) {
445                 echo _("SKIP");
446             } elseif (!strstr(strtolower(join(':', $sess_fields)), "sess_ip")) {
447                 // TODO: postgres test (should be able to add columns at the end, but not in between)
448                 echo "<b>",_("ADDING"),"</b>"," ... ";          
449                 $this->dbi->genericSqlQuery("ALTER TABLE $session_tbl ADD sess_ip CHAR(15) NOT NULL");
450                 $this->dbi->genericSqlQuery("CREATE INDEX sess_date ON $session_tbl (sess_date)");
451             } else {
452                 echo _("OK");
453             }
454             echo "<br />\n";
455             if (substr($backend_type,0,5) == 'mysql') {
456                 // upgrade to 4.1.8 destroyed my session table: 
457                 // sess_id => varchar(10), sess_data => varchar(5). For others obviously also.
458                 echo _("check for mysql session.sess_id sanity")," ... ";
459                 $result = $this->dbi->genericSqlQuery("DESCRIBE $session_tbl");
460                 if (DATABASE_TYPE == 'SQL') {
461                     $iter = new WikiDB_backend_PearDB_generic_iter($backend, $result);
462                 } elseif (DATABASE_TYPE == 'ADODB') {
463                     $iter = new WikiDB_backend_ADODB_generic_iter($backend, $result, 
464                                                                   array("Field", "Type", "Null", "Key", "Default", "Extra"));
465                 } elseif (DATABASE_TYPE == 'PDO') {
466                     $iter = new WikiDB_backend_PDO_generic_iter($backend, $result);
467                 }
468                 while ($col = $iter->next()) {
469                     if ($col["Field"] == 'sess_id' and !strstr(strtolower($col["Type"]), 'char(32)')) {
470                         $this->dbi->genericSqlQuery("ALTER TABLE $session_tbl CHANGE sess_id"
471                                               ." sess_id CHAR(32) NOT NULL");
472                         echo "sess_id ", $col["Type"], " ", _("fixed"), " =&gt; CHAR(32) ";
473                     }
474                     if ($col["Field"] == 'sess_ip' and !strstr(strtolower($col["Type"]), 'char(15)')) {
475                         $this->dbi->genericSqlQuery("ALTER TABLE $session_tbl CHANGE sess_ip"
476                                               ." sess_ip CHAR(15) NOT NULL");
477                         echo "sess_ip ", $col["Type"], " ", _("fixed"), " =&gt; CHAR(15) ";
478                     }
479                 }
480                 echo _("OK"), "<br />\n";
481             }
482         }
483
484         /* TODO:
485            ALTER TABLE link ADD relation INT DEFAULT 0;
486            CREATE INDEX linkrelation ON link (relation);
487         */
488
489         // mysql >= 4.0.4 requires LOCK TABLE privileges
490         if (substr($backend_type,0,5) == 'mysql') {
491             echo _("check for mysql LOCK TABLE privilege")," ...";
492             $mysql_version = $this->dbi->_backend->_serverinfo['version'];
493             if ($mysql_version > 400.40) {
494                 if (!empty($this->dbi->_backend->_parsedDSN))
495                     $parseDSN = $this->dbi->_backend->_parsedDSN;
496                 elseif (function_exists('parseDSN')) // ADODB or PDO
497                     $parseDSN = parseDSN($DBParams['dsn']);
498                 else                         // pear
499                     $parseDSN = DB::parseDSN($DBParams['dsn']);
500                 $username = $this->dbi->_backend->qstr($parseDSN['username']);
501                 // on db level
502                 $query = "SELECT lock_tables_priv FROM mysql.db WHERE user='$username'";
503                 //mysql_select_db("mysql", $this->dbi->_backend->connection());
504                 $db_fields = $this->dbi->_backend->listOfFields("mysql", "db");
505                 if (!strstr(strtolower(join(':', $db_fields)), "lock_tables_priv")) {
506                     echo join(':', $db_fields);
507                     die("lock_tables_priv missing. The DB Admin must run mysql_fix_privilege_tables");
508                 }
509                 $row = $this->dbi->_backend->getRow($query);
510                 if (isset($row[0]) and $row[0] == 'N') {
511                     $this->dbi->genericSqlQuery("UPDATE mysql.db SET lock_tables_priv='Y'"
512                                           ." WHERE mysql.user='$username'");
513                     $this->dbi->genericSqlQuery("FLUSH PRIVILEGES");
514                     echo "mysql.db user='$username'", _("fixed"), "<br />\n";
515                 } elseif (!$row) {
516                     // or on user level
517                     $query = "SELECT lock_tables_priv FROM mysql.user WHERE user='$username'";
518                     $row = $this->dbi->_backend->getRow($query);
519                     if ($row and $row[0] == 'N') {
520                         $this->dbi->genericSqlQuery("UPDATE mysql.user SET lock_tables_priv='Y'"
521                                               ." WHERE mysql.user='$username'");
522                         $this->dbi->genericSqlQuery("FLUSH PRIVILEGES");
523                         echo "mysql.user user='$username'", _("fixed"), "<br />\n";
524                     } elseif (!$row) {
525                         echo " <b><font color=\"red\">", _("FAILED"), "</font></b>: ",
526                             "Neither mysql.db nor mysql.user has a user='$username'"
527                             ." or the lock_tables_priv field",
528                             "<br />\n";
529                     } else {
530                         echo _("OK"), "<br />\n";
531                     }
532                 } else {
533                     echo _("OK"), "<br />\n";
534                 }
535                 //mysql_select_db($this->dbi->_backend->database(), $this->dbi->_backend->connection());
536             } else {
537                 echo sprintf(_("version <em>%s</em> not affected"), $mysql_version),"<br />\n";
538             }
539         }
540
541         // 1.3.10 mysql requires page.id auto_increment
542         // mysql, mysqli or mysqlt
543         if (phpwiki_version() >= 1030.099 and substr($backend_type,0,5) == 'mysql' 
544             and DATABASE_TYPE != 'PDO') 
545         {
546             echo _("check for mysql page.id auto_increment flag")," ...";
547             assert(!empty($page_tbl));
548             $database = $this->dbi->_backend->database();
549             $fields = mysql_list_fields($database, $page_tbl, $this->dbi->_backend->connection());
550             $columns = mysql_num_fields($fields); 
551             for ($i = 0; $i < $columns; $i++) {
552                 if (mysql_field_name($fields, $i) == 'id') {
553                     $flags = mysql_field_flags($fields, $i);
554                     //DONE: something was wrong with ADODB here.
555                     if (!strstr(strtolower($flags), "auto_increment")) {
556                         echo "<b>",_("ADDING"),"</b>"," ... ";
557                         // MODIFY col_def valid since mysql 3.22.16,
558                         // older mysql's need CHANGE old_col col_def
559                         $this->dbi->genericSqlQuery("ALTER TABLE $page_tbl CHANGE id"
560                                                     ." id INT NOT NULL AUTO_INCREMENT");
561                         $fields = mysql_list_fields($database, $page_tbl);
562                         if (!strstr(strtolower(mysql_field_flags($fields, $i)), "auto_increment"))
563                             echo " <b><font color=\"red\">", _("FAILED"), "</font></b><br />\n";
564                         else     
565                             echo _("OK"), "<br />\n";
566                     } else {
567                         echo _("OK"), "<br />\n";
568                     }
569                     break;
570                 }
571             }
572             mysql_free_result($fields);
573         }
574
575         // Check for mysql 4.1.x/5.0.0a binary search problem.
576         //   http://bugs.mysql.com/bug.php?id=4398
577         // "select * from page where LOWER(pagename) like '%search%'" does not apply LOWER!
578         // Confirmed for 4.1.0alpha,4.1.3-beta,5.0.0a; not yet tested for 4.1.2alpha,
579         // On windows only, though utf8 would be useful elsewhere also.
580         // Illegal mix of collations (latin1_bin,IMPLICIT) and 
581         // (utf8_general_ci, COERCIBLE) for operation '='])
582         if (isWindows() and substr($backend_type,0,5) == 'mysql') {
583             echo _("check for mysql 4.1.x/5.0.0 binary search on windows problem")," ...";
584             $mysql_version = $this->dbi->_backend->_serverinfo['version'];
585             if ($mysql_version < 401.0) { 
586                 echo sprintf(_("version <em>%s</em>"), $mysql_version)," ",
587                     _("not affected"),"<br />\n";
588             } elseif ($mysql_version >= 401.6) { // FIXME: since which version?
589                 $row = $this->dbi->_backend->getRow("SHOW CREATE TABLE $page_tbl");
590                 $result = join(" ", $row);
591                 if (strstr(strtolower($result), "character set") 
592                     and strstr(strtolower($result), "collate")) 
593                     {
594                         echo _("OK"), "<br />\n";
595                     } else {
596                     //SET CHARACTER SET latin1
597                     $charset = CHARSET;
598                     if ($charset == 'iso-8859-1') $charset = 'latin1';
599                     $this->dbi->genericSqlQuery("ALTER TABLE $page_tbl CHANGE pagename "
600                                           ."pagename VARCHAR(100) "
601                                           ."CHARACTER SET '$charset' COLLATE '$charset"."_bin' NOT NULL");
602                     echo sprintf(_("version <em>%s</em>"), $mysql_version), 
603                         " <b>",_("FIXED"),"</b>",
604                         "<br />\n";
605                 }
606             } elseif (DATABASE_TYPE != 'PDO') {
607                 // check if already fixed
608                 extract($this->dbi->_backend->_table_names);
609                 assert(!empty($page_tbl));
610                 $database = $this->dbi->_backend->database();
611                 $fields = mysql_list_fields($database, $page_tbl, $this->dbi->_backend->connection());
612                 $columns = mysql_num_fields($fields); 
613                 for ($i = 0; $i < $columns; $i++) {
614                     if (mysql_field_name($fields, $i) == 'pagename') {
615                         $flags = mysql_field_flags($fields, $i);
616                         // I think it was fixed with 4.1.6, but I tested it only with 4.1.8
617                         if ($mysql_version > 401.0 and $mysql_version < 401.6) {
618                             // remove the binary flag
619                             if (strstr(strtolower($flags), "binary")) {
620                                 // FIXME: on duplicate pagenames this will fail!
621                                 $this->dbi->genericSqlQuery("ALTER TABLE $page_tbl CHANGE pagename"
622                                                       ." pagename VARCHAR(100) NOT NULL");
623                                 echo sprintf(_("version <em>%s</em>"), $mysql_version), 
624                                     "<b>",_("FIXED"),"</b>"
625                                     ,"<br />\n";        
626                             }
627                         }
628                         break;
629                     }
630                 }
631             }
632         }
633         if (ACCESS_LOG_SQL & 2) {
634             echo _("check for ACCESS_LOG_SQL passwords in POST requests")," ...";
635             // Don't display passwords in POST requests (up to 2005-02-04 12:03:20)
636             $res = $this->dbi->genericSqlIter("SELECT time_stamp, remote_host, " .
637                                         "request_args FROM ${prefix}accesslog WHERE request_args LIKE " .
638                                         "'%s:6:\"passwd\"%' AND request_args NOT LIKE '%s:6:\"passwd\";" .
639                                         "s:15:\"<not displayed>\"%'");
640             $count = 0;
641             while ($row = $res->next()) {
642                 $args = preg_replace("/(s:6:\"passwd\";s:15:\").*(\")/", 
643                                      "$1<not displayed>$2", $row["request_args"]);
644                 $ts = $row["time_stamp"];
645                 $rh = $row["remote_host"];
646                 $this->dbi->genericSqlQuery("UPDATE ${prefix}accesslog SET " .
647                                       "request_args='$args' WHERE time_stamp=$ts AND " .
648                                       "remote_host='$rh'");
649                 $count++;
650             }
651             if ($count > 0)
652                 echo "<b>",_("FIXED"),"</b>", "<br />\n";
653             else 
654                 echo _("OK"),"<br />\n";
655
656             if (phpwiki_version() >= 1030.13) {
657                 echo _("check for ACCESS_LOG_SQL remote_host varchar(50)")," ...";
658                 $database = $this->dbi->_backend->database();
659                 $accesslog_tbl = $prefix . 'accesslog';
660                 $fields = $this->dbi->_backend->listOfFields($database, $accesslog_tbl);
661                 if (!$fields) {
662                     echo _("SKIP");
663                 } elseif (strstr(strtolower(join(':', $sess_fields)), "remote_host")) {
664                     // TODO: how to check size, already done?
665                     echo "<b>",_("FIXING"),"remote_host</b>"," ... ";
666                     $this->dbi->genericSqlQuery("ALTER TABLE $accesslog_tbl CHANGE remote_host VARCHAR(100)");
667                 } else {
668                     echo _("FAIL");
669                 }
670                 echo "<br />\n";
671             }
672         }
673         $this->_upgrade_cached_html();
674
675         if (phpwiki_version() >= 1030.122006 and $this->dbi->get_db_version() < 1030.13) {
676             $this->dbi->set_db_version(1030.13);
677         }
678
679         return;
680     }
681
682     /**
683      * Filter SQL missing permissions errors.
684      *
685      * A wrong DBADMIN user will not be able to connect
686      * @see _is_false_error, ErrorManager
687      * @access private
688      */
689     function _dbpermission_filter($err) {
690         if  ( $err->isWarning() ) {
691             global $ErrorManager;
692             $this->error_caught = 1;
693             $ErrorManager->_postponed_errors[] = $err;
694             return true;
695         }
696         return false;
697     }
698
699     function _try_dbadmin_user ($user, $passwd) {
700         global $DBParams, $DBAuthParams;
701         $AdminParams = $DBParams;
702         if (DATABASE_TYPE == 'SQL')
703             $dsn = DB::parseDSN($AdminParams['dsn']);
704         else {
705             $dsn = parseDSN($AdminParams['dsn']);
706         }
707         $AdminParams['dsn'] = sprintf("%s://%s:%s@%s/%s",
708                                       $dsn['phptype'],
709                                       $user,
710                                       $passwd,
711                                       $dsn['hostspec'],
712                                       $dsn['database']);
713         $AdminParams['_tryroot_from_upgrade'] = 1;
714         // add error handler to warn about missing permissions for DBADMIN_USER
715         global $ErrorManager;
716         $ErrorManager->pushErrorHandler(new WikiMethodCb($this, '_dbpermission_filter'));
717         $this->error_caught = 0;
718         $this->dbi = WikiDB::open($AdminParams);
719         if (!$this->error_caught) return true; 
720         // FAILED: redo our connection with the wikiuser
721         $this->dbi = WikiDB::open($DBParams);
722         $ErrorManager->flushPostponedErrors();
723         $ErrorManager->popErrorHandler();
724         return false;
725     }
726
727     function _db_init () {
728         if (!$this->dbi->_backend->isSQL()) return;
729
730         /* SQLite never needs admin params */
731         $backend_type = $this->dbi->_backend->backendType();
732         if (substr($backend_type,0,6)=="sqlite") {
733             return;
734         }
735         $dbadmin_user = 'root';
736         if ($dbadmin = $this->request->getArg('dbadmin')) {
737             $dbadmin_user = $dbadmin['user'];
738             if (isset($dbadmin['cancel'])) {
739                 return;
740             } elseif (!empty($dbadmin_user)) {
741                 if ($this->_try_dbadmin_user($dbadmin['user'], $dbadmin['passwd']))
742                     return;
743             }
744         } elseif (DBADMIN_USER) {
745             if ($this->_try_dbadmin_user(DBADMIN_USER, DBADMIN_PASSWD))
746                 return true;
747         }
748         // Check if the privileges are enough. Need CREATE and ALTER perms. 
749         // And on windows: SELECT FROM mysql, possibly: UPDATE mysql.
750         $form = HTML::form(array("method" => "post", 
751                                  "action" => $this->request->getPostURL(),
752                                  "accept-charset"=>$GLOBALS['charset']),
753                            HTML::p(_("Upgrade requires database privileges to CREATE and ALTER the phpwiki database."),
754                                    HTML::br(),
755                                    _("And on windows at least the privilege to SELECT FROM mysql, and possibly UPDATE mysql")),
756                            HiddenInputs(array('action' => 'upgrade',
757                                               'overwrite' => $this->request->getArg('overwrite'))),
758                            HTML::table(array("cellspacing"=>4),
759                                        HTML::tr(HTML::td(array('align'=>'right'),
760                                                          _("DB admin user:")),
761                                                 HTML::td(HTML::input(array('name'=>"dbadmin[user]",
762                                                                            'size'=>12,
763                                                                            'maxlength'=>256,
764                                                                            'value'=>$dbadmin_user)))),
765                                        HTML::tr(HTML::td(array('align'=>'right'),
766                                                          _("DB admin password:")),
767                                                 HTML::td(HTML::input(array('name'=>"dbadmin[passwd]",
768                                                                            'type'=>'password',
769                                                                            'size'=>12,
770                                                                            'maxlength'=>256)))),
771                                        HTML::tr(HTML::td(array('align'=>'center', 'colspan' => 2),
772                                                          Button("submit:", _("Submit"), 'wikiaction'), 
773                                                          HTML::raw('&nbsp;'),
774                                                          Button("submit:dbadmin[cancel]", _("Cancel"), 
775                                                                 'button')))));
776         $form->printXml();
777         echo "</div><!-- content -->\n";
778         echo asXML(Template("bottom"));
779         echo "</body></html>\n";
780         $this->request->finish();
781         exit();
782     }
783
784     /**
785      * if page.cached_html does not exists:
786      *   put _cached_html from pagedata into a new seperate blob, 
787      *   not into the huge serialized string.
788      *
789      * It is only rarelely needed: for current page only, if-not-modified,
790      * but was extracetd for every simple page iteration.
791      */
792     function _upgrade_cached_html ( $verbose=true ) {
793         global $DBParams;
794         if (!$this->dbi->_backend->isSQL()) return;
795         $count = 0;
796         if (phpwiki_version() >= 1030.10) {
797             if ($verbose)
798                 echo _("check for extra page.cached_html column")," ... ";
799             $database = $this->dbi->_backend->database();
800             extract($this->dbi->_backend->_table_names);
801             $fields = $this->dbi->_backend->listOfFields($database, $page_tbl);
802             if (!$fields) {
803                 echo _("SKIP"), "<br />\n";
804                 return 0;
805             }
806             if (!strstr(strtolower(join(':', $fields)), "cached_html")) {
807                 if ($verbose)
808                     echo "<b>",_("ADDING"),"</b>"," ... ";
809                 $backend_type = $this->dbi->_backend->backendType();
810                 if (substr($backend_type,0,5) == 'mysql')
811                     $this->dbi->genericSqlQuery("ALTER TABLE $page_tbl ADD cached_html MEDIUMBLOB");
812                 else
813                     $this->dbi->genericSqlQuery("ALTER TABLE $page_tbl ADD cached_html BLOB");
814                 if ($verbose)
815                     echo "<b>",_("CONVERTING"),"</b>"," ... ";
816                 $count = _convert_cached_html();
817                 if ($verbose)
818                     echo $count, " ", _("OK"), "<br />\n";
819             } else {
820                 if ($verbose)
821                     echo _("OK"), "<br />\n";
822             }
823         }
824         return $count;
825     }
826
827     /** 
828      * move _cached_html for all pages from pagedata into a new seperate blob.
829      * decoupled from action=upgrade, so that it can be used by a WikiAdminUtils button also.
830      */
831     function _convert_cached_html () {
832         global $DBParams;
833         if (!$this->dbi->_backend->isSQL()) return;
834         //if (!in_array(DATABASE_TYPE, array('SQL','ADODB'))) return;
835
836         $pages = $this->dbi->getAllPages();
837         $cache =& $this->dbi->_cache;
838         $count = 0;
839         extract($this->dbi->_backend->_table_names);
840         while ($page = $pages->next()) {
841             $pagename = $page->getName();
842             $data = $this->dbi->_backend->get_pagedata($pagename);
843             if (!empty($data['_cached_html'])) {
844                 $cached_html = $data['_cached_html'];
845                 $data['_cached_html'] = '';
846                 $cache->update_pagedata($pagename, $data);
847                 // store as blob, not serialized
848                 $this->dbi->genericSqlQuery("UPDATE $page_tbl SET cached_html=? WHERE pagename=?",
849                                       array($cached_html, $pagename));
850                 $count++;
851             }
852         }
853         return $count;
854     }
855
856     /**
857      * upgrade to 1.3.13 link structure.
858      */
859     function _upgrade_relation_links ( $verbose=true ) {
860         if (phpwiki_version() >= 1030.12200610) {
861             echo _("Rebuild entire database to upgrade relation links")," ... ";
862             if (DATABASE_TYPE == 'dba') {
863                 echo "<b>",_("CONVERTING")," dba linktable</b>"," ... ";
864             }
865             longer_timeout(240);
866             $this->dbi->_backend->rebuild();
867             echo _("OK"), "<br />\n";
868         }
869     }
870
871     function CheckPluginUpdate () {
872         echo "<h3>",sprintf(_("check for necessary %s updates"),
873                             _("plugin argument")),"</h3>\n";
874         $process = array(array('msg' => _("change RandomPage pages => numpages"),
875                                'match' => "/(<\?\s*plugin\s+ RandomPage\s+)pages/",
876                                'replace' => "\\1numpages")
877                          );
878         $allpages = $this->dbi->getAllPages(false);
879         while ($page = $allpages->next()) {
880             $current = $page->getCurrentRevision();
881             $pagetext = $current->getPackedContent();
882             foreach ($process as $p) {
883                 if (preg_match($p['match'], $pagetext)) {
884                     echo $page->getName()," ",$p['msg']," ... ";
885                     if ($newtext = preg_replace($p['match'], $p['replace'], $pagetext)) {
886                         $meta = $current->_data;
887                         $meta['summary'] = "upgrade: ".$p['msg'];
888                         $page->save($newtext, $current->getVersion() + 1, $meta);
889                         echo _("OK"), "<br />\n";
890                     } else {
891                         echo " <b><font color=\"red\">", _("FAILED"), "</font></b><br />\n";
892                     }
893                 }
894             }
895         }
896     }
897
898     function fixConfigIni($match, $new) {
899         $file = FindFile("config/config.ini");
900         $found = false;
901         if (is_writable($file)) {
902             $in = fopen($file,"rb");
903             $out = fopen($tmp = tempnam(FindFile("uploads"),"cfg"),"wb");
904             if (isWindows())
905                 $tmp = str_replace("/","\\",$tmp);
906             while ($s = fgets($in)) {
907                 if (preg_match($match, $s)) {
908                     $s = $new . (isWindows() ? "\r\n" : "\n");
909                     $found = true;
910                 }
911                 fputs($out, $s);
912             }
913             fclose($in);
914             fclose($out);
915             if (!$found) {
916                 echo " <b><font color=\"red\">",_("FAILED"),"</font></b>: ",
917                     sprintf(_("%s not found"), $match);
918                 unlink($out);
919             } else {
920                 @unlink("$file.bak");
921                 @rename($file,"$file.bak");
922                 if (rename($tmp, $file))
923                     echo " <b>",_("FIXED"),"</b>";
924                 else {
925                     echo " <b>",_("FAILED"),"</b>: ";
926                     sprintf(_("couldn't move %s to %s"), $tmp, $file);
927                     return false;
928                 }
929             }
930             return $found;
931         } else {
932             echo " <b><font color=\"red\">",_("FAILED"),"</font></b>: ",
933                 sprintf(_("%s is not writable"), $file);
934             return false;
935         }
936     }
937
938     function CheckConfigUpdate () {
939         echo "<h3>",sprintf(_("check for necessary %s updates"),
940                             "config.ini"),"</h3>\n";
941         echo _("check for old CACHE_CONTROL = NONE")," ... ";
942         if (defined('CACHE_CONTROL') and CACHE_CONTROL == '') {
943             echo "<br />&nbsp;&nbsp;",
944                 _("CACHE_CONTROL is set to 'NONE', and must be changed to 'NO_CACHE'"),
945                 " ...";
946             fixConfigIni("/^\s*CACHE_CONTROL\s*=\s*NONE/","CACHE_CONTROL = NO_CACHE");
947         } else {
948             echo _("OK");
949         }
950         echo "<br />\n";
951         echo _("check for GROUP_METHOD = NONE")," ... ";
952         if (defined('GROUP_METHOD') and GROUP_METHOD == '') {
953             echo "<br />&nbsp;&nbsp;",
954                 _("GROUP_METHOD is set to NONE, and must be changed to \"NONE\""),
955                 " ...";
956             fixConfigIni("/^\s*GROUP_METHOD\s*=\s*NONE/","GROUP_METHOD = \"NONE\"");
957         } else {
958             echo _("OK");
959         }
960         echo "<br />\n";
961     }
962
963 } // class Upgrade
964
965 /**
966  * TODO:
967  *
968  * Upgrade: Base class for multipage worksteps
969  * identify, validate, display options, next step
970  */
971 /*
972 */
973
974 // TODO: At which step are we? 
975 // validate and do it again or go on with next step.
976
977 /** entry function from lib/main.php
978  */
979 function DoUpgrade($request) {
980
981     if (!$request->_user->isAdmin()) {
982         $request->_notAuthorized(WIKIAUTH_ADMIN);
983         $request->finish(
984                          HTML::div(array('class' => 'disabled-plugin'),
985                                    fmt("Upgrade disabled: user != isAdmin")));
986         return;
987     }
988
989     StartLoadDump($request, _("Upgrading this PhpWiki"));
990     $upgrade = new Upgrade($request);
991     //CheckOldIndexUpdate($request); // to upgrade from < 1.3.10
992     if (!$request->getArg('nosql'))
993         $upgrade->CheckDatabaseUpdate($request);   // first check cached_html and friends
994     if (!$request->getArg('nopgsrc')) {
995         $upgrade->CheckActionPageUpdate($request);
996         $upgrade->CheckPgsrcUpdate($request);
997     }
998     //CheckThemeUpdate($request);
999     if (!$request->getArg('noplugin'))
1000         $upgrade->CheckPluginUpdate($request);
1001     if (!$request->getArg('noconfig'))
1002         $upgrade->CheckConfigUpdate($request);
1003     EndLoadDump($request);
1004 }
1005
1006
1007 /*
1008  $Log: not supported by cvs2svn $
1009  Revision 1.56  2007/01/03 21:25:34  rurban
1010  rename InterWikiKarte to InterWikiListe. Support nosql, nopgsrc, noplugin, noconfig args.
1011
1012  Revision 1.55  2007/01/02 13:24:01  rurban
1013  1.3.13 support: _rename_page_helper, _rename_to_help_page, _upgrade_relation_links, check for ACCESS_LOG_SQL remote_host varchar(50), _upgrade_psql_tsearch2
1014
1015  Revision 1.54  2006/12/03 17:07:29  rurban
1016  #1535843 by matt brown: Upgrade Wizard Password fixes are not portable
1017
1018  Revision 1.53  2006/12/03 17:03:18  rurban
1019  #1535851 by matt brown
1020
1021  Revision 1.52  2006/12/03 17:01:18  rurban
1022  #1535839 by matt brown
1023
1024  Revision 1.51  2006/08/07 21:05:30  rurban
1025  patch #1535837  (debian)
1026
1027  Revision 1.50  2006/06/18 11:04:09  rurban
1028  keep overwrite arg
1029
1030  Revision 1.49  2006/05/18 06:03:39  rurban
1031  use $dbh->_backend->isSQL
1032
1033  Revision 1.48  2005/11/14 22:32:38  rurban
1034  remove user, SKIP on !session
1035
1036  Revision 1.47  2005/02/27 19:13:27  rurban
1037  latin1 mysql fix
1038
1039  Revision 1.46  2005/02/12 17:22:18  rurban
1040  locale update: missing . : fixed. unified strings
1041  proper linebreaks
1042
1043  Revision 1.45  2005/02/10 19:01:19  rurban
1044  add PDO support
1045
1046  Revision 1.44  2005/02/07 15:40:42  rurban
1047  use defined CHARSET for db. more comments
1048
1049  Revision 1.43  2005/02/04 11:44:07  rurban
1050  check passwd in access_log
1051
1052  Revision 1.42  2005/02/02 19:38:13  rurban
1053  prefer utf8 pagenames for collate issues
1054
1055  Revision 1.41  2005/01/31 12:15:29  rurban
1056  print OK
1057
1058  Revision 1.40  2005/01/30 23:22:17  rurban
1059  clarify messages
1060
1061  Revision 1.39  2005/01/30 23:09:17  rurban
1062  sanify session fields
1063
1064  Revision 1.38  2005/01/25 07:57:02  rurban
1065  add dbadmin form, add mysql LOCK TABLES check, add plugin args updater (not yet activated)
1066
1067  Revision 1.37  2005/01/20 10:19:08  rurban
1068  add InterWikiMap to special pages
1069
1070  Revision 1.36  2004/12/20 12:56:11  rurban
1071  patch #1088128 by Kai Krakow. avoid chicken & egg problem
1072
1073  Revision 1.35  2004/12/13 14:35:41  rurban
1074  verbose arg
1075
1076  Revision 1.34  2004/12/11 09:39:28  rurban
1077  needed init for ref
1078
1079  Revision 1.33  2004/12/10 22:33:39  rurban
1080  add WikiAdminUtils method for convert-cached-html
1081  missed some vars.
1082
1083  Revision 1.32  2004/12/10 22:15:00  rurban
1084  fix $page->get('_cached_html)
1085  refactor upgrade db helper _convert_cached_html() to be able to call them from WikiAdminUtils also.
1086  support 2nd genericSqlQuery param (bind huge arg)
1087
1088  Revision 1.31  2004/12/10 02:45:26  rurban
1089  SQL optimization:
1090    put _cached_html from pagedata into a new seperate blob, not huge serialized string.
1091    it is only rarelely needed: for current page only, if-not-modified
1092    but was extracted for every simple page iteration.
1093
1094  Revision 1.30  2004/11/29 17:58:57  rurban
1095  just aesthetics
1096
1097  Revision 1.29  2004/11/29 16:08:31  rurban
1098  added missing nl
1099
1100  Revision 1.28  2004/11/16 16:25:14  rurban
1101  fix accesslog tablename, print CREATED only if really done
1102
1103  Revision 1.27  2004/11/07 16:02:52  rurban
1104  new sql access log (for spam prevention), and restructured access log class
1105  dbh->quote (generic)
1106  pear_db: mysql specific parts seperated (using replace)
1107
1108  Revision 1.26  2004/10/14 19:19:34  rurban
1109  loadsave: check if the dumped file will be accessible from outside.
1110  and some other minor fixes. (cvsclient native not yet ready)
1111
1112  Revision 1.25  2004/09/06 08:28:00  rurban
1113  rename genericQuery to genericSqlQuery
1114
1115  Revision 1.24  2004/07/05 13:56:22  rurban
1116  sqlite autoincrement fix
1117
1118  Revision 1.23  2004/07/04 10:28:06  rurban
1119  DBADMIN_USER fix
1120
1121  Revision 1.22  2004/07/03 17:21:28  rurban
1122  updated docs: submitted new mysql bugreport (#1491 did not fix it)
1123
1124  Revision 1.21  2004/07/03 16:51:05  rurban
1125  optional DBADMIN_USER:DBADMIN_PASSWD for action=upgrade (if no ALTER permission)
1126  added atomic mysql REPLACE for PearDB as in ADODB
1127  fixed _lock_tables typo links => link
1128  fixes unserialize ADODB bug in line 180
1129
1130  Revision 1.20  2004/07/03 14:48:18  rurban
1131  Tested new mysql 4.1.3-beta: binary search bug as fixed.
1132  => fixed action=upgrade,
1133  => version check in PearDB also (as in ADODB)
1134
1135  Revision 1.19  2004/06/19 12:19:09  rurban
1136  slightly improved docs
1137
1138  Revision 1.18  2004/06/19 11:47:17  rurban
1139  added CheckConfigUpdate: CACHE_CONTROL = NONE => NO_CACHE
1140
1141  Revision 1.17  2004/06/17 11:31:50  rurban
1142  check necessary localized actionpages
1143
1144  Revision 1.16  2004/06/16 10:38:58  rurban
1145  Disallow refernces in calls if the declaration is a reference
1146  ("allow_call_time_pass_reference clean").
1147    PhpWiki is now allow_call_time_pass_reference = Off clean,
1148    but several external libraries may not.
1149    In detail these libs look to be affected (not tested):
1150    * Pear_DB odbc
1151    * adodb oracle
1152
1153  Revision 1.15  2004/06/07 19:50:40  rurban
1154  add owner field to mimified dump
1155
1156  Revision 1.14  2004/06/07 18:38:18  rurban
1157  added mysql 4.1.x search fix
1158
1159  Revision 1.13  2004/06/04 20:32:53  rurban
1160  Several locale related improvements suggested by Pierrick Meignen
1161  LDAP fix by John Cole
1162  reanable admin check without ENABLE_PAGEPERM in the admin plugins
1163
1164  Revision 1.12  2004/05/18 13:59:15  rurban
1165  rename simpleQuery to genericSqlQuery
1166
1167  Revision 1.11  2004/05/15 13:06:17  rurban
1168  skip the HomePage, at first upgrade the ActionPages, then the database, then the rest
1169
1170  Revision 1.10  2004/05/15 01:19:41  rurban
1171  upgrade prefix fix by Kai Krakow
1172
1173  Revision 1.9  2004/05/14 11:33:03  rurban
1174  version updated to 1.3.11pre
1175  upgrade stability fix
1176
1177  Revision 1.8  2004/05/12 10:49:55  rurban
1178  require_once fix for those libs which are loaded before FileFinder and
1179    its automatic include_path fix, and where require_once doesn't grok
1180    dirname(__FILE__) != './lib'
1181  upgrade fix with PearDB
1182  navbar.tmpl: remove spaces for IE &nbsp; button alignment
1183
1184  Revision 1.7  2004/05/06 17:30:38  rurban
1185  CategoryGroup: oops, dos2unix eol
1186  improved phpwiki_version:
1187    pre -= .0001 (1.3.10pre: 1030.099)
1188    -p1 += .001 (1.3.9-p1: 1030.091)
1189  improved InstallTable for mysql and generic SQL versions and all newer tables so far.
1190  abstracted more ADODB/PearDB methods for action=upgrade stuff:
1191    backend->backendType(), backend->database(),
1192    backend->listOfFields(),
1193    backend->listOfTables(),
1194
1195  Revision 1.6  2004/05/03 15:05:36  rurban
1196  + table messages
1197
1198  Revision 1.4  2004/05/02 21:26:38  rurban
1199  limit user session data (HomePageHandle and auth_dbi have to invalidated anyway)
1200    because they will not survive db sessions, if too large.
1201  extended action=upgrade
1202  some WikiTranslation button work
1203  revert WIKIAUTH_UNOBTAINABLE (need it for main.php)
1204  some temp. session debug statements
1205
1206  Revision 1.3  2004/04/29 22:33:30  rurban
1207  fixed sf.net bug #943366 (Kai Krakow)
1208    couldn't load localized url-undecoded pagenames
1209
1210  Revision 1.2  2004/03/12 15:48:07  rurban
1211  fixed explodePageList: wrong sortby argument order in UnfoldSubpages
1212  simplified lib/stdlib.php:explodePageList
1213
1214  */
1215
1216 // For emacs users
1217 // Local Variables:
1218 // mode: php
1219 // tab-width: 8
1220 // c-basic-offset: 4
1221 // c-hanging-comment-ender-p: nil
1222 // indent-tabs-mode: nil
1223 // End:
1224 ?>