2 rcs_id('$Id: upgrade.php,v 1.54 2006-12-03 17:07:29 rurban Exp $');
4 Copyright 2004,2005,2006 $ThePhpWikiProgrammingTeam
6 This file is part of PhpWiki.
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.
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.
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
24 * Upgrade the WikiDB and config settings after installing a new
26 * Status: experimental, no queries for verification yet,
27 * no merge conflict resolution (patch?), just overwrite.
29 * Installation on an existing PhpWiki database needs some
30 * additional worksteps. Each step will require multiple pages.
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
46 * Done: overwrite=1 link on edit conflicts at first occurence "Overwrite all".
48 * @author: Reini Urban
50 require_once("lib/loadsave.php");
53 * TODO: check for the pgsrc_version number, not the revision mtime only
55 function doPgsrcUpdate(&$request,$pagename,$path,$filename) {
56 $dbi = $request->getDbh();
57 $page = $dbi->getPage($pagename);
58 if ($page->exists()) {
59 // check mtime: update automatically if pgsrc is newer
60 $rev = $page->getCurrentRevision();
61 $page_mtime = $rev->get('mtime');
62 $data = implode("", file($path."/".$filename));
63 if (($parts = ParseMimeifiedPages($data))) {
64 usort($parts, 'SortByPageVersion');
66 $pageinfo = $parts[0];
67 $stat = stat($path."/".$filename);
68 $new_mtime = @$pageinfo['versiondata']['mtime'];
70 $new_mtime = @$pageinfo['versiondata']['lastmodified'];
72 $new_mtime = @$pageinfo['pagedata']['date'];
74 $new_mtime = $stat[9];
75 if ($new_mtime > $page_mtime) {
76 echo "$path/$pagename: ",_("newer than the existing page."),
77 _(" replace "),"($new_mtime > $page_mtime)","<br />\n";
78 LoadAny($request,$path."/".$filename);
81 echo "$path/$pagename: ",_("older than the existing page."),
82 _(" skipped"),".<br />\n";
85 echo "$path/$pagename: ",("unknown format."),
86 _(" skipped"),".<br />\n";
89 echo sprintf(_("%s does not exist"),$pagename),"<br />\n";
90 LoadAny($request,$path."/".$filename);
95 /** Need the english filename (required precondition: urlencode == urldecode).
96 * Returns the plugin name.
98 function isActionPage($filename) {
99 static $special = array("DebugInfo" => "_BackendInfo",
100 "PhpWikiRecentChanges" => "RssFeed",
101 "ProjectSummary" => "RssFeed",
102 "RecentReleases" => "RssFeed",
103 "InterWikiMap" => "InterWikiMap",
105 $base = preg_replace("/\..{1,4}$/","",basename($filename));
106 if (isset($special[$base])) return $special[$base];
107 if (FindFile("lib/plugin/".$base.".php",true)) return $base;
111 function CheckActionPageUpdate(&$request) {
112 echo "<h3>",_("check for necessary ActionPage updates"),"</h3>\n";
113 $dbi = $request->getDbh();
114 $path = FindFile('pgsrc');
115 $pgsrc = new fileSet($path);
116 // most actionpages have the same name as the plugin
117 $loc_path = FindLocalizedFile('pgsrc');
118 foreach ($pgsrc->getFiles() as $filename) {
119 if (substr($filename,-1,1) == '~') continue;
120 $pagename = urldecode($filename);
121 if (isActionPage($filename)) {
122 $translation = gettext($pagename);
123 if ($translation == $pagename)
124 doPgsrcUpdate($request, $pagename, $path, $filename);
125 elseif (FindLocalizedFile('pgsrc/'.urlencode($translation),1))
126 doPgsrcUpdate($request, $translation, $loc_path,
127 urlencode($translation));
129 doPgsrcUpdate($request, $pagename, $path, $filename);
134 // see loadsave.php for saving new pages.
135 function CheckPgsrcUpdate(&$request) {
136 echo "<h3>",_("check for necessary pgsrc updates"),"</h3>\n";
137 $dbi = $request->getDbh();
138 $path = FindLocalizedFile(WIKI_PGSRC);
139 $pgsrc = new fileSet($path);
140 // fixme: verification, ...
142 foreach ($pgsrc->getFiles() as $filename) {
143 if (substr($filename,-1,1) == '~') continue;
144 $pagename = urldecode($filename);
145 // don't ever update the HomePage
146 if (defined(HOME_PAGE))
147 if ($pagename == HOME_PAGE) $isHomePage = true;
149 if ($pagename == _("HomePage")) $isHomePage = true;
150 if ($pagename == "HomePage") $isHomePage = true;
152 echo "$path/$pagename: ",_("always skip the HomePage."),
153 _(" skipped"),".<br />\n";
157 if (!isActionPage($filename)) {
158 doPgsrcUpdate($request,$pagename,$path,$filename);
165 * TODO: Search table definition in appropriate schema
167 * Supported: mysql and generic SQL, for ADODB and PearDB.
169 function installTable(&$dbh, $table, $backend_type) {
171 if (!$dbh->_backend->isSQL()) return;
172 echo _("MISSING")," ... \n";
173 $backend = &$dbh->_backend->_dbh;
175 $schema = findFile("schemas/${backend_type}.sql");
177 echo " ",_("FAILED"),": ",sprintf(_("no schema %s found"),
178 "schemas/${backend_type}.sql")," ... <br />\n";
182 extract($dbh->_backend->_table_names);
183 $prefix = isset($DBParams['prefix']) ? $DBParams['prefix'] : '';
186 assert($session_tbl);
187 if ($backend_type == 'mysql') {
188 $dbh->genericSqlQuery("
189 CREATE TABLE $session_tbl (
190 sess_id CHAR(32) NOT NULL DEFAULT '',
191 sess_data BLOB NOT NULL,
192 sess_date INT UNSIGNED NOT NULL,
193 sess_ip CHAR(15) NOT NULL,
194 PRIMARY KEY (sess_id),
198 $dbh->genericSqlQuery("
199 CREATE TABLE $session_tbl (
200 sess_id CHAR(32) NOT NULL DEFAULT '',
201 sess_data ".($backend_type == 'pgsql'?'TEXT':'BLOB')." NOT NULL,
203 sess_ip CHAR(15) NOT NULL
205 $dbh->genericSqlQuery("CREATE UNIQUE INDEX sess_id ON $session_tbl (sess_id)");
207 $dbh->genericSqlQuery("CREATE INDEX sess_date on session (sess_date)");
208 echo " ",_("CREATED");
211 $pref_tbl = $prefix.'pref';
212 if ($backend_type == 'mysql') {
213 $dbh->genericSqlQuery("
214 CREATE TABLE $pref_tbl (
215 userid CHAR(48) BINARY NOT NULL UNIQUE,
216 prefs TEXT NULL DEFAULT '',
220 $dbh->genericSqlQuery("
221 CREATE TABLE $pref_tbl (
222 userid CHAR(48) NOT NULL,
223 prefs TEXT NULL DEFAULT ''
225 $dbh->genericSqlQuery("CREATE UNIQUE INDEX userid ON $pref_tbl (userid)");
227 echo " ",_("CREATED");
230 $member_tbl = $prefix.'member';
231 if ($backend_type == 'mysql') {
232 $dbh->genericSqlQuery("
233 CREATE TABLE $member_tbl (
234 userid CHAR(48) BINARY NOT NULL,
235 groupname CHAR(48) BINARY NOT NULL DEFAULT 'users',
240 $dbh->genericSqlQuery("
241 CREATE TABLE $member_tbl (
242 userid CHAR(48) NOT NULL,
243 groupname CHAR(48) NOT NULL DEFAULT 'users'
245 $dbh->genericSqlQuery("CREATE INDEX userid ON $member_tbl (userid)");
246 $dbh->genericSqlQuery("CREATE INDEX groupname ON $member_tbl (groupname)");
248 echo " ",_("CREATED");
251 $rating_tbl = $prefix.'rating';
252 if ($backend_type == 'mysql') {
253 $dbh->genericSqlQuery("
254 CREATE TABLE $rating_tbl (
255 dimension INT(4) NOT NULL,
256 raterpage INT(11) NOT NULL,
257 rateepage INT(11) NOT NULL,
258 ratingvalue FLOAT NOT NULL,
259 rateeversion INT(11) NOT NULL,
260 tstamp TIMESTAMP(14) NOT NULL,
261 PRIMARY KEY (dimension, raterpage, rateepage)
264 $dbh->genericSqlQuery("
265 CREATE TABLE $rating_tbl (
266 dimension INT(4) NOT NULL,
267 raterpage INT(11) NOT NULL,
268 rateepage INT(11) NOT NULL,
269 ratingvalue FLOAT NOT NULL,
270 rateeversion INT(11) NOT NULL,
271 tstamp TIMESTAMP(14) NOT NULL
273 $dbh->genericSqlQuery("CREATE UNIQUE INDEX rating"
274 ." ON $rating_tbl (dimension, raterpage, rateepage)");
276 echo " ",_("CREATED");
279 $log_tbl = $prefix.'accesslog';
280 // fields according to http://www.outoforder.cc/projects/apache/mod_log_sql/docs-2.0/#id2756178
282 A User Agent agent varchar(255) Mozilla/4.0 (compat; MSIE 6.0; Windows)
283 a CGi request arguments request_args varchar(255) user=Smith&cart=1231&item=532
284 b Bytes transfered bytes_sent int unsigned 32561
285 c??? Text of cookie cookie varchar(255) Apache=sdyn.fooonline.net 1300102700823
286 f Local filename requested request_file varchar(255) /var/www/html/books-cycroad.html
287 H HTTP request_protocol request_protocol varchar(10) HTTP/1.1
288 h Name of remote host remote_host varchar(50) blah.foobar.com
289 I Request ID (from modd_unique_id) id char(19) POlFcUBRH30AAALdBG8
290 l Ident user info remote_logname varcgar(50) bobby
291 M Machine ID??? machine_id varchar(25) web01
292 m HTTP request method request_method varchar(10) GET
293 P httpd cchild PID child_pid smallint unsigned 3215
294 p http port server_port smallint unsigned 80
295 R Referer referer varchar(255) http://www.biglinks4u.com/linkpage.html
296 r Request in full form request_line varchar(255) GET /books-cycroad.html HTTP/1.1
297 S Time of request in UNIX time_t format time_stamp int unsigned 1005598029
298 T Seconds to service request request_duration smallint unsigned 2
299 t Time of request in human format request_time char(28) [02/Dec/2001:15:01:26 -0800]
300 U Request in simple form request_uri varchar(255) /books-cycroad.html
301 u User info from HTTP auth remote_user varchar(50) bobby
302 v Virtual host servicing the request virtual_host varchar(255)
304 $dbh->genericSqlQuery("
305 CREATE TABLE $log_tbl (
306 time_stamp int unsigned,
307 remote_host varchar(100),
308 remote_user varchar(50),
309 request_method varchar(10),
310 request_line varchar(255),
311 request_args varchar(255),
312 request_uri varchar(255),
313 request_time char(28),
314 status smallint unsigned,
315 bytes_sent smallint unsigned,
316 referer varchar(255),
318 request_duration float
320 $dbh->genericSqlQuery("CREATE INDEX log_time ON $log_tbl (time_stamp)");
321 $dbh->genericSqlQuery("CREATE INDEX log_host ON $log_tbl (remote_host)");
322 echo " ",_("CREATED");
329 * Update from ~1.3.4 to current.
330 * Only session, user, pref and member
331 * jeffs-hacks database api (around 1.3.2) later:
332 * people should export/import their pages if using that old versions.
334 function CheckDatabaseUpdate(&$request) {
335 global $DBAuthParams;
336 $dbh = $request->getDbh();
337 if (!$dbh->_backend->isSQL()) return;
338 echo "<h3>",_("check for necessary database updates"), " - ", DATABASE_TYPE, "</h3>\n";
340 $dbadmin = $request->getArg('dbadmin');
341 _upgrade_db_init($dbh);
342 if (isset($dbadmin['cancel'])) {
343 echo _("CANCEL")," <br />\n";
347 $tables = $dbh->_backend->listOfTables();
348 $backend_type = $dbh->_backend->backendType();
349 echo "<h4>",_("Backend type: "),$backend_type,"</h4>\n";
350 $prefix = isset($DBParams['prefix']) ? $DBParams['prefix'] : '';
351 foreach (explode(':','session:pref:member') as $table) {
352 echo sprintf(_("check for table %s"), $table)," ...";
353 if (!in_array($prefix.$table, $tables)) {
354 installTable($dbh, $table, $backend_type);
356 echo _("OK")," <br />\n";
359 if (ACCESS_LOG_SQL) {
360 $table = "accesslog";
361 echo sprintf(_("check for table %s"), $table)," ...";
362 if (!in_array($prefix.$table, $tables)) {
363 installTable($dbh, $table, $backend_type);
365 echo _("OK")," <br />\n";
368 if ((class_exists("RatingsUserFactory") or $dbh->isWikiPage(_("RateIt")))) {
370 echo sprintf(_("check for table %s"), $table)," ...";
371 if (!in_array($prefix.$table, $tables)) {
372 installTable($dbh, $table, $backend_type);
374 echo _("OK")," <br />\n";
377 $backend = &$dbh->_backend->_dbh;
378 extract($dbh->_backend->_table_names);
380 // 1.3.8 added session.sess_ip
381 if (phpwiki_version() >= 1030.08 and USE_DB_SESSION and isset($request->_dbsession)) {
382 echo _("check for new session.sess_ip column")," ... ";
383 $database = $dbh->_backend->database();
384 assert(!empty($DBParams['db_session_table']));
385 $session_tbl = $prefix . $DBParams['db_session_table'];
386 $sess_fields = $dbh->_backend->listOfFields($database, $session_tbl);
389 } elseif (!strstr(strtolower(join(':', $sess_fields)), "sess_ip")) {
390 // TODO: postgres test (should be able to add columns at the end, but not in between)
391 echo "<b>",_("ADDING"),"</b>"," ... ";
392 $dbh->genericSqlQuery("ALTER TABLE $session_tbl ADD sess_ip CHAR(15) NOT NULL");
393 $dbh->genericSqlQuery("CREATE INDEX sess_date ON $session_tbl (sess_date)");
398 if (substr($backend_type,0,5) == 'mysql') {
399 // upgrade to 4.1.8 destroyed my session table:
400 // sess_id => varchar(10), sess_data => varchar(5). For others obviously also.
401 echo _("check for mysql session.sess_id sanity")," ... ";
402 $result = $dbh->genericSqlQuery("DESCRIBE $session_tbl");
403 if (DATABASE_TYPE == 'SQL') {
404 $iter = new WikiDB_backend_PearDB_generic_iter($backend, $result);
405 } elseif (DATABASE_TYPE == 'ADODB') {
406 $iter = new WikiDB_backend_ADODB_generic_iter($backend, $result,
407 array("Field", "Type", "Null", "Key", "Default", "Extra"));
408 } elseif (DATABASE_TYPE == 'PDO') {
409 $iter = new WikiDB_backend_PDO_generic_iter($backend, $result);
411 while ($col = $iter->next()) {
412 if ($col["Field"] == 'sess_id' and !strstr(strtolower($col["Type"]), 'char(32)')) {
413 $dbh->genericSqlQuery("ALTER TABLE $session_tbl CHANGE sess_id"
414 ." sess_id CHAR(32) NOT NULL");
415 echo "sess_id ", $col["Type"], " ", _("fixed"), " => CHAR(32) ";
417 if ($col["Field"] == 'sess_ip' and !strstr(strtolower($col["Type"]), 'char(15)')) {
418 $dbh->genericSqlQuery("ALTER TABLE $session_tbl CHANGE sess_ip"
419 ." sess_ip CHAR(15) NOT NULL");
420 echo "sess_ip ", $col["Type"], " ", _("fixed"), " => CHAR(15) ";
423 echo _("OK"), "<br />\n";
428 ALTER TABLE link ADD relation INT DEFAULT 0;
429 CREATE INDEX linkrelation ON link (relation);
432 // mysql >= 4.0.4 requires LOCK TABLE privileges
433 if (0 and substr($backend_type,0,5) == 'mysql') {
434 echo _("check for mysql LOCK TABLE privilege")," ...";
435 $mysql_version = $dbh->_backend->_serverinfo['version'];
436 if ($mysql_version > 400.40) {
437 if (!empty($dbh->_backend->_parsedDSN))
438 $parseDSN = $dbh->_backend->_parsedDSN;
439 elseif (function_exists('parseDSN')) // ADODB or PDO
440 $parseDSN = parseDSN($DBParams['dsn']);
442 $parseDSN = DB::parseDSN($DBParams['dsn']);
443 $username = $dbh->_backend->qstr($parseDSN['username']);
445 $query = "SELECT lock_tables_priv FROM mysql.db WHERE user='$username'";
446 //mysql_select_db("mysql", $dbh->_backend->connection());
447 $db_fields = $dbh->_backend->listOfFields("mysql", "db");
448 if (!strstr(strtolower(join(':', $db_fields)), "lock_tables_priv")) {
449 echo join(':', $db_fields);
450 die("lock_tables_priv missing. The DB Admin must run mysql_fix_privilege_tables");
452 $row = $dbh->_backend->getRow($query);
453 if (isset($row[0]) and $row[0] == 'N') {
454 $dbh->genericSqlQuery("UPDATE mysql.db SET lock_tables_priv='Y'"
455 ." WHERE mysql.user='$username'");
456 $dbh->genericSqlQuery("FLUSH PRIVILEGES");
457 echo "mysql.db user='$username'", _("fixed"), "<br />\n";
460 $query = "SELECT lock_tables_priv FROM mysql.user WHERE user='$username'";
461 $row = $dbh->_backend->getRow($query);
462 if ($row and $row[0] == 'N') {
463 $dbh->genericSqlQuery("UPDATE mysql.user SET lock_tables_priv='Y'"
464 ." WHERE mysql.user='$username'");
465 $dbh->genericSqlQuery("FLUSH PRIVILEGES");
466 echo "mysql.user user='$username'", _("fixed"), "<br />\n";
468 echo " <b><font color=\"red\">", _("FAILED"), "</font></b>: ",
469 "Neither mysql.db nor mysql.user has a user='$username'"
470 ." or the lock_tables_priv field",
473 echo _("OK"), "<br />\n";
476 echo _("OK"), "<br />\n";
478 //mysql_select_db($dbh->_backend->database(), $dbh->_backend->connection());
480 echo sprintf(_("version <em>%s</em> not affected"), $mysql_version),"<br />\n";
484 // 1.3.10 mysql requires page.id auto_increment
485 // mysql, mysqli or mysqlt
486 if (phpwiki_version() >= 1030.099 and substr($backend_type,0,5) == 'mysql'
487 and DATABASE_TYPE != 'PDO') {
488 echo _("check for mysql page.id auto_increment flag")," ...";
489 assert(!empty($page_tbl));
490 $database = $dbh->_backend->database();
491 $fields = mysql_list_fields($database, $page_tbl, $dbh->_backend->connection());
492 $columns = mysql_num_fields($fields);
493 for ($i = 0; $i < $columns; $i++) {
494 if (mysql_field_name($fields, $i) == 'id') {
495 $flags = mysql_field_flags($fields, $i);
496 //DONE: something was wrong with ADODB here.
497 if (!strstr(strtolower($flags), "auto_increment")) {
498 echo "<b>",_("ADDING"),"</b>"," ... ";
499 // MODIFY col_def valid since mysql 3.22.16,
500 // older mysql's need CHANGE old_col col_def
501 $dbh->genericSqlQuery("ALTER TABLE $page_tbl CHANGE id"
502 ." id INT NOT NULL AUTO_INCREMENT");
503 $fields = mysql_list_fields($database, $page_tbl);
504 if (!strstr(strtolower(mysql_field_flags($fields, $i)), "auto_increment"))
505 echo " <b><font color=\"red\">", _("FAILED"), "</font></b><br />\n";
507 echo _("OK"), "<br />\n";
509 echo _("OK"), "<br />\n";
514 mysql_free_result($fields);
517 // Check for mysql 4.1.x/5.0.0a binary search problem.
518 // http://bugs.mysql.com/bug.php?id=4398
519 // "select * from page where LOWER(pagename) like '%search%'" does not apply LOWER!
520 // Confirmed for 4.1.0alpha,4.1.3-beta,5.0.0a; not yet tested for 4.1.2alpha,
521 // On windows only, though utf8 would be useful elsewhere also.
522 // Illegal mix of collations (latin1_bin,IMPLICIT) and
523 // (utf8_general_ci, COERCIBLE) for operation '='])
524 if (isWindows() and substr($backend_type,0,5) == 'mysql') {
525 echo _("check for mysql 4.1.x/5.0.0 binary search on windows problem")," ...";
526 $mysql_version = $dbh->_backend->_serverinfo['version'];
527 if ($mysql_version < 401.0) {
528 echo sprintf(_("version <em>%s</em>"), $mysql_version)," ",
529 _("not affected"),"<br />\n";
530 } elseif ($mysql_version >= 401.6) { // FIXME: since which version?
531 $row = $dbh->_backend->getRow("SHOW CREATE TABLE $page_tbl");
532 $result = join(" ", $row);
533 if (strstr(strtolower($result), "character set")
534 and strstr(strtolower($result), "collate"))
536 echo _("OK"), "<br />\n";
538 //SET CHARACTER SET latin1
540 if ($charset == 'iso-8859-1') $charset = 'latin1';
541 $dbh->genericSqlQuery("ALTER TABLE $page_tbl CHANGE pagename "
542 ."pagename VARCHAR(100) "
543 ."CHARACTER SET '$charset' COLLATE '$charset"."_bin' NOT NULL");
544 echo sprintf(_("version <em>%s</em>"), $mysql_version),
545 " <b>",_("FIXED"),"</b>",
548 } elseif (DATABASE_TYPE != 'PDO') {
549 // check if already fixed
550 extract($dbh->_backend->_table_names);
551 assert(!empty($page_tbl));
552 $database = $dbh->_backend->database();
553 $fields = mysql_list_fields($database, $page_tbl, $dbh->_backend->connection());
554 $columns = mysql_num_fields($fields);
555 for ($i = 0; $i < $columns; $i++) {
556 if (mysql_field_name($fields, $i) == 'pagename') {
557 $flags = mysql_field_flags($fields, $i);
558 // I think it was fixed with 4.1.6, but I tested it only with 4.1.8
559 if ($mysql_version > 401.0 and $mysql_version < 401.6) {
560 // remove the binary flag
561 if (strstr(strtolower($flags), "binary")) {
562 // FIXME: on duplicate pagenames this will fail!
563 $dbh->genericSqlQuery("ALTER TABLE $page_tbl CHANGE pagename"
564 ." pagename VARCHAR(100) NOT NULL");
565 echo sprintf(_("version <em>%s</em>"), $mysql_version),
566 "<b>",_("FIXED"),"</b>"
575 if ((ACCESS_LOG_SQL & 2)) {
576 echo _("check for ACCESS_LOG_SQL passwords in POST requests")," ...";
577 // Don't display passwords in POST requests (up to 2005-02-04 12:03:20)
578 $res = $dbh->genericSqlIter("SELECT time_stamp, remote_host, " .
579 "request_args FROM ${prefix}accesslog WHERE request_args LIKE " .
580 "'%s:6:\"passwd\"%' AND request_args NOT LIKE '%s:6:\"passwd\";" .
581 "s:15:\"<not displayed>\"%'");
583 while ($row = $res->next()) {
584 $args = preg_replace("/(s:6:\"passwd\";s:15:\").*(\")/",
585 "$1<not displayed>$2", $row["request_args"]);
586 $ts = $row["time_stamp"];
587 $rh = $row["remote_host"];
588 $dbh->genericSqlQuery("UPDATE ${prefix}accesslog SET " .
589 "request_args='$args' WHERE time_stamp=$ts AND " .
590 "remote_host='$rh'");
594 echo "<b>",_("FIXED"),"</b>", "<br />\n";
596 echo _("OK"),"<br />\n";
598 // TODO: change access_log.remote_host from varchar(50) to varchar(100)
599 _upgrade_cached_html($dbh);
604 function _upgrade_db_init (&$dbh) {
605 global $request, $DBParams, $DBAuthParams;
606 if (!$dbh->_backend->isSQL()) return;
608 /* SQLite never needs admin params */
609 $backend_type = $dbh->_backend->backendType();
610 if (substr($backend_type,0,6)=="sqlite") {
615 // if need to connect as the root user, for CREATE and ALTER privileges
616 $AdminParams = $DBParams;
617 if (DATABASE_TYPE == 'SQL')
618 $dsn = DB::parseDSN($AdminParams['dsn']);
620 $dsn = parseDSN($AdminParams['dsn']);
621 $AdminParams['dsn'] = sprintf("%s://%s:%s@%s/%s",
627 if (DEBUG & _DEBUG_SQL and DATABASE_TYPE == 'PDO') {
628 echo "<br>\nDBParams['dsn']: '", $DBParams['dsn'], "'";
629 echo "<br>\ndsn: '", print_r($dsn), "'";
630 echo "<br>\nAdminParams['dsn']: '", $AdminParams['dsn'], "'";
632 $dbh = WikiDB::open($AdminParams);
633 } elseif ($dbadmin = $request->getArg('dbadmin')) {
634 if (empty($dbadmin['user']) or isset($dbadmin['cancel']))
635 $dbh = &$request->_dbi;
637 $AdminParams = $DBParams;
638 if (DATABASE_TYPE == 'SQL')
639 $dsn = DB::parseDSN($AdminParams['dsn']);
641 $dsn = parseDSN($AdminParams['dsn']);
642 $AdminParams['dsn'] = sprintf("%s://%s:%s@%s/%s",
648 $dbh = WikiDB::open($AdminParams);
651 // Check if the privileges are enough. Need CREATE and ALTER perms.
652 // And on windows: SELECT FROM mysql, possibly: UPDATE mysql.
653 $form = HTML::form(array("method" => "post",
654 "action" => $request->getPostURL(),
655 "accept-charset"=>$GLOBALS['charset']),
656 HTML::p(_("Upgrade requires database privileges to CREATE and ALTER the phpwiki database."),
658 _("And on windows at least the privilege to SELECT FROM mysql, and possibly UPDATE mysql")),
659 HiddenInputs(array('action' => 'upgrade',
660 'overwrite' => $request->getArg('overwrite'))),
661 HTML::table(array("cellspacing"=>4),
662 HTML::tr(HTML::td(array('align'=>'right'),
663 _("DB admin user:")),
664 HTML::td(HTML::input(array('name'=>"dbadmin[user]",
668 HTML::tr(HTML::td(array('align'=>'right'),
669 _("DB admin password:")),
670 HTML::td(HTML::input(array('name'=>"dbadmin[passwd]",
673 'maxlength'=>256)))),
674 HTML::tr(HTML::td(array('align'=>'center', 'colspan' => 2),
675 Button("submit:", _("Submit"), 'wikiaction'),
677 Button("submit:dbadmin[cancel]", _("Cancel"),
680 echo "</div><!-- content -->\n";
681 echo asXML(Template("bottom"));
682 echo "</body></html>\n";
689 * if page.cached_html does not exists:
690 * put _cached_html from pagedata into a new seperate blob, not huge serialized string.
692 * it is only rarelely needed: for current page only, if-not-modified
693 * but was extracetd for every simple page iteration.
695 function _upgrade_cached_html (&$dbh, $verbose=true) {
697 if (!$dbh->_backend->isSQL()) return;
698 //if (!in_array(DATABASE_TYPE, array('SQL','ADODB','PDO'))) return;
700 if (phpwiki_version() >= 1030.10) {
702 echo _("check for extra page.cached_html column")," ... ";
703 $database = $dbh->_backend->database();
704 extract($dbh->_backend->_table_names);
705 $fields = $dbh->_backend->listOfFields($database, $page_tbl);
707 echo _("SKIP"), "<br />\n";
710 if (!strstr(strtolower(join(':', $fields)), "cached_html")) {
712 echo "<b>",_("ADDING"),"</b>"," ... ";
713 $backend_type = $dbh->_backend->backendType();
714 if (substr($backend_type,0,5) == 'mysql')
715 $dbh->genericSqlQuery("ALTER TABLE $page_tbl ADD cached_html MEDIUMBLOB");
717 $dbh->genericSqlQuery("ALTER TABLE $page_tbl ADD cached_html BLOB");
719 echo "<b>",_("CONVERTING"),"</b>"," ... ";
720 $count = _convert_cached_html($dbh);
722 echo $count, " ", _("OK"), "<br />\n";
725 echo _("OK"), "<br />\n";
732 * move _cached_html for all pages from pagedata into a new seperate blob.
733 * decoupled from action=upgrade, so that it can be used by a WikiAdminUtils button also.
735 function _convert_cached_html (&$dbh) {
737 if (!$dbh->_backend->isSQL()) return;
738 //if (!in_array(DATABASE_TYPE, array('SQL','ADODB'))) return;
740 $pages = $dbh->getAllPages();
741 $cache =& $dbh->_cache;
743 extract($dbh->_backend->_table_names);
744 while ($page = $pages->next()) {
745 $pagename = $page->getName();
746 $data = $dbh->_backend->get_pagedata($pagename);
747 if (!empty($data['_cached_html'])) {
748 $cached_html = $data['_cached_html'];
749 $data['_cached_html'] = '';
750 $cache->update_pagedata($pagename, $data);
751 // store as blob, not serialized
752 $dbh->genericSqlQuery("UPDATE $page_tbl SET cached_html=? WHERE pagename=?",
753 array($cached_html, $pagename));
760 function CheckPluginUpdate(&$request) {
761 echo "<h3>",_("check for necessary plugin argument updates"),"</h3>\n";
762 $process = array('msg' => _("change RandomPage pages => numpages"),
763 'match' => "/(<\?\s*plugin\s+ RandomPage\s+)pages/",
764 'replace' => "\\1numpages");
765 $dbi = $request->getDbh();
766 $allpages = $dbi->getAllPages(false);
767 while ($page = $allpages->next()) {
768 $current = $page->getCurrentRevision();
769 $pagetext = $current->getPackedContent();
770 foreach ($process as $p) {
771 if (preg_match($p['match'], $pagetext)) {
772 echo $page->getName()," ",$p['msg']," ... ";
773 if ($newtext = preg_replace($p['match'], $p['replace'], $pagetext)) {
774 $meta = $current->_data;
775 $meta['summary'] = "upgrade: ".$p['msg'];
776 $page->save($newtext, $current->getVersion() + 1, $meta);
777 echo _("OK"), "<br />\n";
779 echo " <b><font color=\"red\">", _("FAILED"), "</font></b><br />\n";
786 function fixConfigIni($match, $new) {
787 $file = FindFile("config/config.ini");
789 if (is_writable($file)) {
790 $in = fopen($file,"rb");
791 $out = fopen($tmp = tempnam(FindFile("uploads"),"cfg"),"wb");
793 $tmp = str_replace("/","\\",$tmp);
794 while ($s = fgets($in)) {
795 if (preg_match($match, $s)) {
796 $s = $new . (isWindows() ? "\r\n" : "\n");
804 echo " <b><font color=\"red\">",_("FAILED"),"</font></b>: ",
805 sprintf(_("%s not found"), $match);
808 @unlink("$file.bak");
809 @rename($file,"$file.bak");
810 if (rename($tmp, $file))
811 echo " <b>",_("FIXED"),"</b>";
813 echo " <b>",_("FAILED"),"</b>: ";
814 sprintf(_("couldn't move %s to %s"), $tmp, $file);
820 echo " <b><font color=\"red\">",_("FAILED"),"</font></b>: ",
821 sprintf(_("%s is not writable"), $file);
826 function CheckConfigUpdate(&$request) {
827 echo "<h3>",_("check for necessary config updates"),"</h3>\n";
828 echo _("check for old CACHE_CONTROL = NONE")," ... ";
829 if (defined('CACHE_CONTROL') and CACHE_CONTROL == '') {
830 echo "<br /> ",
831 _("CACHE_CONTROL is set to 'NONE', and must be changed to 'NO_CACHE'"),
833 fixConfigIni("/^\s*CACHE_CONTROL\s*=\s*NONE/","CACHE_CONTROL = NO_CACHE");
838 echo _("check for GROUP_METHOD = NONE")," ... ";
839 if (defined('GROUP_METHOD') and GROUP_METHOD == '') {
840 echo "<br /> ",
841 _("GROUP_METHOD is set to NONE, and must be changed to \"NONE\""),
843 fixConfigIni("/^\s*GROUP_METHOD\s*=\s*NONE/","GROUP_METHOD = \"NONE\"");
853 * Upgrade: Base class for multipage worksteps
854 * identify, validate, display options, next step
860 class Upgrade_CheckPgsrc extends Upgrade {
863 class Upgrade_CheckDatabaseUpdate extends Upgrade {
867 // TODO: At which step are we?
868 // validate and do it again or go on with next step.
870 /** entry function from lib/main.php
872 function DoUpgrade($request) {
874 if (!$request->_user->isAdmin()) {
875 $request->_notAuthorized(WIKIAUTH_ADMIN);
877 HTML::div(array('class' => 'disabled-plugin'),
878 fmt("Upgrade disabled: user != isAdmin")));
882 StartLoadDump($request, _("Upgrading this PhpWiki"));
883 //CheckOldIndexUpdate($request); // to upgrade from < 1.3.10
884 CheckDatabaseUpdate($request); // first check cached_html and friends
885 CheckActionPageUpdate($request);
886 CheckPgsrcUpdate($request);
887 //CheckThemeUpdate($request);
888 //CheckPluginUpdate($request);
889 CheckConfigUpdate($request);
890 EndLoadDump($request);
895 $Log: not supported by cvs2svn $
896 Revision 1.53 2006/12/03 17:03:18 rurban
897 #1535851 by matt brown
899 Revision 1.52 2006/12/03 17:01:18 rurban
900 #1535839 by matt brown
902 Revision 1.51 2006/08/07 21:05:30 rurban
903 patch #1535837 (debian)
905 Revision 1.50 2006/06/18 11:04:09 rurban
908 Revision 1.49 2006/05/18 06:03:39 rurban
909 use $dbh->_backend->isSQL
911 Revision 1.48 2005/11/14 22:32:38 rurban
912 remove user, SKIP on !session
914 Revision 1.47 2005/02/27 19:13:27 rurban
917 Revision 1.46 2005/02/12 17:22:18 rurban
918 locale update: missing . : fixed. unified strings
921 Revision 1.45 2005/02/10 19:01:19 rurban
924 Revision 1.44 2005/02/07 15:40:42 rurban
925 use defined CHARSET for db. more comments
927 Revision 1.43 2005/02/04 11:44:07 rurban
928 check passwd in access_log
930 Revision 1.42 2005/02/02 19:38:13 rurban
931 prefer utf8 pagenames for collate issues
933 Revision 1.41 2005/01/31 12:15:29 rurban
936 Revision 1.40 2005/01/30 23:22:17 rurban
939 Revision 1.39 2005/01/30 23:09:17 rurban
940 sanify session fields
942 Revision 1.38 2005/01/25 07:57:02 rurban
943 add dbadmin form, add mysql LOCK TABLES check, add plugin args updater (not yet activated)
945 Revision 1.37 2005/01/20 10:19:08 rurban
946 add InterWikiMap to special pages
948 Revision 1.36 2004/12/20 12:56:11 rurban
949 patch #1088128 by Kai Krakow. avoid chicken & egg problem
951 Revision 1.35 2004/12/13 14:35:41 rurban
954 Revision 1.34 2004/12/11 09:39:28 rurban
957 Revision 1.33 2004/12/10 22:33:39 rurban
958 add WikiAdminUtils method for convert-cached-html
961 Revision 1.32 2004/12/10 22:15:00 rurban
962 fix $page->get('_cached_html)
963 refactor upgrade db helper _convert_cached_html() to be able to call them from WikiAdminUtils also.
964 support 2nd genericSqlQuery param (bind huge arg)
966 Revision 1.31 2004/12/10 02:45:26 rurban
968 put _cached_html from pagedata into a new seperate blob, not huge serialized string.
969 it is only rarelely needed: for current page only, if-not-modified
970 but was extracted for every simple page iteration.
972 Revision 1.30 2004/11/29 17:58:57 rurban
975 Revision 1.29 2004/11/29 16:08:31 rurban
978 Revision 1.28 2004/11/16 16:25:14 rurban
979 fix accesslog tablename, print CREATED only if really done
981 Revision 1.27 2004/11/07 16:02:52 rurban
982 new sql access log (for spam prevention), and restructured access log class
984 pear_db: mysql specific parts seperated (using replace)
986 Revision 1.26 2004/10/14 19:19:34 rurban
987 loadsave: check if the dumped file will be accessible from outside.
988 and some other minor fixes. (cvsclient native not yet ready)
990 Revision 1.25 2004/09/06 08:28:00 rurban
991 rename genericQuery to genericSqlQuery
993 Revision 1.24 2004/07/05 13:56:22 rurban
994 sqlite autoincrement fix
996 Revision 1.23 2004/07/04 10:28:06 rurban
999 Revision 1.22 2004/07/03 17:21:28 rurban
1000 updated docs: submitted new mysql bugreport (#1491 did not fix it)
1002 Revision 1.21 2004/07/03 16:51:05 rurban
1003 optional DBADMIN_USER:DBADMIN_PASSWD for action=upgrade (if no ALTER permission)
1004 added atomic mysql REPLACE for PearDB as in ADODB
1005 fixed _lock_tables typo links => link
1006 fixes unserialize ADODB bug in line 180
1008 Revision 1.20 2004/07/03 14:48:18 rurban
1009 Tested new mysql 4.1.3-beta: binary search bug as fixed.
1010 => fixed action=upgrade,
1011 => version check in PearDB also (as in ADODB)
1013 Revision 1.19 2004/06/19 12:19:09 rurban
1014 slightly improved docs
1016 Revision 1.18 2004/06/19 11:47:17 rurban
1017 added CheckConfigUpdate: CACHE_CONTROL = NONE => NO_CACHE
1019 Revision 1.17 2004/06/17 11:31:50 rurban
1020 check necessary localized actionpages
1022 Revision 1.16 2004/06/16 10:38:58 rurban
1023 Disallow refernces in calls if the declaration is a reference
1024 ("allow_call_time_pass_reference clean").
1025 PhpWiki is now allow_call_time_pass_reference = Off clean,
1026 but several external libraries may not.
1027 In detail these libs look to be affected (not tested):
1031 Revision 1.15 2004/06/07 19:50:40 rurban
1032 add owner field to mimified dump
1034 Revision 1.14 2004/06/07 18:38:18 rurban
1035 added mysql 4.1.x search fix
1037 Revision 1.13 2004/06/04 20:32:53 rurban
1038 Several locale related improvements suggested by Pierrick Meignen
1039 LDAP fix by John Cole
1040 reanable admin check without ENABLE_PAGEPERM in the admin plugins
1042 Revision 1.12 2004/05/18 13:59:15 rurban
1043 rename simpleQuery to genericSqlQuery
1045 Revision 1.11 2004/05/15 13:06:17 rurban
1046 skip the HomePage, at first upgrade the ActionPages, then the database, then the rest
1048 Revision 1.10 2004/05/15 01:19:41 rurban
1049 upgrade prefix fix by Kai Krakow
1051 Revision 1.9 2004/05/14 11:33:03 rurban
1052 version updated to 1.3.11pre
1053 upgrade stability fix
1055 Revision 1.8 2004/05/12 10:49:55 rurban
1056 require_once fix for those libs which are loaded before FileFinder and
1057 its automatic include_path fix, and where require_once doesn't grok
1058 dirname(__FILE__) != './lib'
1059 upgrade fix with PearDB
1060 navbar.tmpl: remove spaces for IE button alignment
1062 Revision 1.7 2004/05/06 17:30:38 rurban
1063 CategoryGroup: oops, dos2unix eol
1064 improved phpwiki_version:
1065 pre -= .0001 (1.3.10pre: 1030.099)
1066 -p1 += .001 (1.3.9-p1: 1030.091)
1067 improved InstallTable for mysql and generic SQL versions and all newer tables so far.
1068 abstracted more ADODB/PearDB methods for action=upgrade stuff:
1069 backend->backendType(), backend->database(),
1070 backend->listOfFields(),
1071 backend->listOfTables(),
1073 Revision 1.6 2004/05/03 15:05:36 rurban
1076 Revision 1.4 2004/05/02 21:26:38 rurban
1077 limit user session data (HomePageHandle and auth_dbi have to invalidated anyway)
1078 because they will not survive db sessions, if too large.
1079 extended action=upgrade
1080 some WikiTranslation button work
1081 revert WIKIAUTH_UNOBTAINABLE (need it for main.php)
1082 some temp. session debug statements
1084 Revision 1.3 2004/04/29 22:33:30 rurban
1085 fixed sf.net bug #943366 (Kai Krakow)
1086 couldn't load localized url-undecoded pagenames
1088 Revision 1.2 2004/03/12 15:48:07 rurban
1089 fixed explodePageList: wrong sortby argument order in UnfoldSubpages
1090 simplified lib/stdlib.php:explodePageList
1098 // c-basic-offset: 4
1099 // c-hanging-comment-ender-p: nil
1100 // indent-tabs-mode: nil