2 rcs_id('$Id: upgrade.php,v 1.51 2006-08-07 21:05:30 rurban Exp $');
4 Copyright 2004,2005 $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(50),
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 $result = $dbh->genericSqlQuery(
579 "UPDATE ".$prefix."accesslog"
580 .' SET request_args=CONCAT(left(request_args, LOCATE("s:6:\"passwd\"",request_args)+12),"...")'
581 .' WHERE LOCATE("s:6:\"passwd\"", request_args)'
582 .' AND NOT(LOCATE("s:6:\"passwd\";s:15:\"<not displayed>\"", request_args))'
583 .' AND request_method="POST"');
584 if ((DATABASE_TYPE == 'SQL' and $backend->AffectedRows())
585 or (DATABASE_TYPE == 'ADODB' and $backend->Affected_Rows())
586 or (DATABASE_TYPE == 'PDO' and $result))
587 echo "<b>",_("FIXED"),"</b>", "<br />\n";
589 echo _("OK"),"<br />\n";
591 _upgrade_cached_html($dbh);
596 function _upgrade_db_init (&$dbh) {
597 global $request, $DBParams, $DBAuthParams;
598 if (!$dbh->_backend->isSQL()) return;
601 // if need to connect as the root user, for CREATE and ALTER privileges
602 $AdminParams = $DBParams;
603 if (DATABASE_TYPE == 'SQL')
604 $dsn = DB::parseDSN($AdminParams['dsn']);
606 $dsn = parseDSN($AdminParams['dsn']);
607 $AdminParams['dsn'] = sprintf("%s://%s:%s@%s/%s",
613 if (DEBUG & _DEBUG_SQL and DATABASE_TYPE == 'PDO') {
614 echo "<br>\nDBParams['dsn']: '", $DBParams['dsn'], "'";
615 echo "<br>\ndsn: '", print_r($dsn), "'";
616 echo "<br>\nAdminParams['dsn']: '", $AdminParams['dsn'], "'";
618 $dbh = WikiDB::open($AdminParams);
619 } elseif ($dbadmin = $request->getArg('dbadmin')) {
620 if (empty($dbadmin['user']) or isset($dbadmin['cancel']))
621 $dbh = &$request->_dbi;
623 $AdminParams = $DBParams;
624 if (DATABASE_TYPE == 'SQL')
625 $dsn = DB::parseDSN($AdminParams['dsn']);
627 $dsn = parseDSN($AdminParams['dsn']);
628 $AdminParams['dsn'] = sprintf("%s://%s:%s@%s/%s",
634 $dbh = WikiDB::open($AdminParams);
637 // Check if the privileges are enough. Need CREATE and ALTER perms.
638 // And on windows: SELECT FROM mysql, possibly: UPDATE mysql.
639 $form = HTML::form(array("method" => "post",
640 "action" => $request->getPostURL(),
641 "accept-charset"=>$GLOBALS['charset']),
642 HTML::p(_("Upgrade requires database privileges to CREATE and ALTER the phpwiki database."),
644 _("And on windows at least the privilege to SELECT FROM mysql, and possibly UPDATE mysql")),
645 HiddenInputs(array('action' => 'upgrade',
646 'overwrite' => $request->getArg('overwrite'))),
647 HTML::table(array("cellspacing"=>4),
648 HTML::tr(HTML::td(array('align'=>'right'),
649 _("DB admin user:")),
650 HTML::td(HTML::input(array('name'=>"dbadmin[user]",
654 HTML::tr(HTML::td(array('align'=>'right'),
655 _("DB admin password:")),
656 HTML::td(HTML::input(array('name'=>"dbadmin[passwd]",
659 'maxlength'=>256)))),
660 HTML::tr(HTML::td(array('align'=>'center', 'colspan' => 2),
661 Button("submit:", _("Submit"), 'wikiaction'),
663 Button("submit:dbadmin[cancel]", _("Cancel"),
666 echo "</div><!-- content -->\n";
667 echo asXML(Template("bottom"));
668 echo "</body></html>\n";
675 * if page.cached_html does not exists:
676 * put _cached_html from pagedata into a new seperate blob, not huge serialized string.
678 * it is only rarelely needed: for current page only, if-not-modified
679 * but was extracetd for every simple page iteration.
681 function _upgrade_cached_html (&$dbh, $verbose=true) {
683 if (!$dbh->_backend->isSQL()) return;
684 //if (!in_array(DATABASE_TYPE, array('SQL','ADODB','PDO'))) return;
686 if (phpwiki_version() >= 1030.10) {
688 echo _("check for extra page.cached_html column")," ... ";
689 $database = $dbh->_backend->database();
690 extract($dbh->_backend->_table_names);
691 $fields = $dbh->_backend->listOfFields($database, $page_tbl);
693 echo _("SKIP"), "<br />\n";
696 if (!strstr(strtolower(join(':', $fields)), "cached_html")) {
698 echo "<b>",_("ADDING"),"</b>"," ... ";
699 $backend_type = $dbh->_backend->backendType();
700 if (substr($backend_type,0,5) == 'mysql')
701 $dbh->genericSqlQuery("ALTER TABLE $page_tbl ADD cached_html MEDIUMBLOB");
703 $dbh->genericSqlQuery("ALTER TABLE $page_tbl ADD cached_html BLOB");
705 echo "<b>",_("CONVERTING"),"</b>"," ... ";
706 $count = _convert_cached_html($dbh);
708 echo $count, " ", _("OK"), "<br />\n";
711 echo _("OK"), "<br />\n";
718 * move _cached_html for all pages from pagedata into a new seperate blob.
719 * decoupled from action=upgrade, so that it can be used by a WikiAdminUtils button also.
721 function _convert_cached_html (&$dbh) {
723 if (!$dbh->_backend->isSQL()) return;
724 //if (!in_array(DATABASE_TYPE, array('SQL','ADODB'))) return;
726 $pages = $dbh->getAllPages();
727 $cache =& $dbh->_cache;
729 extract($dbh->_backend->_table_names);
730 while ($page = $pages->next()) {
731 $pagename = $page->getName();
732 $data = $dbh->_backend->get_pagedata($pagename);
733 if (!empty($data['_cached_html'])) {
734 $cached_html = $data['_cached_html'];
735 $data['_cached_html'] = '';
736 $cache->update_pagedata($pagename, $data);
737 // store as blob, not serialized
738 $dbh->genericSqlQuery("UPDATE $page_tbl SET cached_html=? WHERE pagename=?",
739 array($cached_html, $pagename));
746 function CheckPluginUpdate(&$request) {
747 echo "<h3>",_("check for necessary plugin argument updates"),"</h3>\n";
748 $process = array('msg' => _("change RandomPage pages => numpages"),
749 'match' => "/(<\?\s*plugin\s+ RandomPage\s+)pages/",
750 'replace' => "\\1numpages");
751 $dbi = $request->getDbh();
752 $allpages = $dbi->getAllPages(false);
753 while ($page = $allpages->next()) {
754 $current = $page->getCurrentRevision();
755 $pagetext = $current->getPackedContent();
756 foreach ($process as $p) {
757 if (preg_match($p['match'], $pagetext)) {
758 echo $page->getName()," ",$p['msg']," ... ";
759 if ($newtext = preg_replace($p['match'], $p['replace'], $pagetext)) {
760 $meta = $current->_data;
761 $meta['summary'] = "upgrade: ".$p['msg'];
762 $page->save($newtext, $current->getVersion() + 1, $meta);
763 echo _("OK"), "<br />\n";
765 echo " <b><font color=\"red\">", _("FAILED"), "</font></b><br />\n";
772 function fixConfigIni($match, $new) {
773 $file = FindFile("config/config.ini");
775 if (is_writable($file)) {
776 $in = fopen($file,"rb");
777 $out = fopen($tmp = tempnam(FindFile("uploads"),"cfg"),"wb");
779 $tmp = str_replace("/","\\",$tmp);
780 while ($s = fgets($in)) {
781 if (preg_match($match, $s)) {
782 $s = $new . (isWindows() ? "\r\n" : "\n");
790 echo " <b><font color=\"red\">",_("FAILED"),"</font></b>: ",
791 sprintf(_("%s not found"), $match);
794 @unlink("$file.bak");
795 @rename($file,"$file.bak");
796 if (rename($tmp, $file))
797 echo " <b>",_("FIXED"),"</b>";
799 echo " <b>",_("FAILED"),"</b>: ";
800 sprintf(_("couldn't move %s to %s"), $tmp, $file);
806 echo " <b><font color=\"red\">",_("FAILED"),"</font></b>: ",
807 sprintf(_("%s is not writable"), $file);
812 function CheckConfigUpdate(&$request) {
813 echo "<h3>",_("check for necessary config updates"),"</h3>\n";
814 echo _("check for old CACHE_CONTROL = NONE")," ... ";
815 if (defined('CACHE_CONTROL') and CACHE_CONTROL == '') {
816 echo "<br /> ",
817 _("CACHE_CONTROL is set to 'NONE', and must be changed to 'NO_CACHE'"),
819 fixConfigIni("/^\s*CACHE_CONTROL\s*=\s*NONE/","CACHE_CONTROL = NO_CACHE");
824 echo _("check for GROUP_METHOD = NONE")," ... ";
825 if (defined('GROUP_METHOD') and GROUP_METHOD == '') {
826 echo "<br /> ",
827 _("GROUP_METHOD is set to NONE, and must be changed to \"NONE\""),
829 fixConfigIni("/^\s*GROUP_METHOD\s*=\s*NONE/","GROUP_METHOD = \"NONE\"");
839 * Upgrade: Base class for multipage worksteps
840 * identify, validate, display options, next step
846 class Upgrade_CheckPgsrc extends Upgrade {
849 class Upgrade_CheckDatabaseUpdate extends Upgrade {
853 // TODO: At which step are we?
854 // validate and do it again or go on with next step.
856 /** entry function from lib/main.php
858 function DoUpgrade($request) {
860 if (!$request->_user->isAdmin()) {
861 $request->_notAuthorized(WIKIAUTH_ADMIN);
863 HTML::div(array('class' => 'disabled-plugin'),
864 fmt("Upgrade disabled: user != isAdmin")));
868 StartLoadDump($request, _("Upgrading this PhpWiki"));
869 //CheckOldIndexUpdate($request); // to upgrade from < 1.3.10
870 CheckDatabaseUpdate($request); // first check cached_html and friends
871 CheckActionPageUpdate($request);
872 CheckPgsrcUpdate($request);
873 //CheckThemeUpdate($request);
874 //CheckPluginUpdate($request);
875 CheckConfigUpdate($request);
876 EndLoadDump($request);
881 $Log: not supported by cvs2svn $
882 Revision 1.50 2006/06/18 11:04:09 rurban
885 Revision 1.49 2006/05/18 06:03:39 rurban
886 use $dbh->_backend->isSQL
888 Revision 1.48 2005/11/14 22:32:38 rurban
889 remove user, SKIP on !session
891 Revision 1.47 2005/02/27 19:13:27 rurban
894 Revision 1.46 2005/02/12 17:22:18 rurban
895 locale update: missing . : fixed. unified strings
898 Revision 1.45 2005/02/10 19:01:19 rurban
901 Revision 1.44 2005/02/07 15:40:42 rurban
902 use defined CHARSET for db. more comments
904 Revision 1.43 2005/02/04 11:44:07 rurban
905 check passwd in access_log
907 Revision 1.42 2005/02/02 19:38:13 rurban
908 prefer utf8 pagenames for collate issues
910 Revision 1.41 2005/01/31 12:15:29 rurban
913 Revision 1.40 2005/01/30 23:22:17 rurban
916 Revision 1.39 2005/01/30 23:09:17 rurban
917 sanify session fields
919 Revision 1.38 2005/01/25 07:57:02 rurban
920 add dbadmin form, add mysql LOCK TABLES check, add plugin args updater (not yet activated)
922 Revision 1.37 2005/01/20 10:19:08 rurban
923 add InterWikiMap to special pages
925 Revision 1.36 2004/12/20 12:56:11 rurban
926 patch #1088128 by Kai Krakow. avoid chicken & egg problem
928 Revision 1.35 2004/12/13 14:35:41 rurban
931 Revision 1.34 2004/12/11 09:39:28 rurban
934 Revision 1.33 2004/12/10 22:33:39 rurban
935 add WikiAdminUtils method for convert-cached-html
938 Revision 1.32 2004/12/10 22:15:00 rurban
939 fix $page->get('_cached_html)
940 refactor upgrade db helper _convert_cached_html() to be able to call them from WikiAdminUtils also.
941 support 2nd genericSqlQuery param (bind huge arg)
943 Revision 1.31 2004/12/10 02:45:26 rurban
945 put _cached_html from pagedata into a new seperate blob, not huge serialized string.
946 it is only rarelely needed: for current page only, if-not-modified
947 but was extracted for every simple page iteration.
949 Revision 1.30 2004/11/29 17:58:57 rurban
952 Revision 1.29 2004/11/29 16:08:31 rurban
955 Revision 1.28 2004/11/16 16:25:14 rurban
956 fix accesslog tablename, print CREATED only if really done
958 Revision 1.27 2004/11/07 16:02:52 rurban
959 new sql access log (for spam prevention), and restructured access log class
961 pear_db: mysql specific parts seperated (using replace)
963 Revision 1.26 2004/10/14 19:19:34 rurban
964 loadsave: check if the dumped file will be accessible from outside.
965 and some other minor fixes. (cvsclient native not yet ready)
967 Revision 1.25 2004/09/06 08:28:00 rurban
968 rename genericQuery to genericSqlQuery
970 Revision 1.24 2004/07/05 13:56:22 rurban
971 sqlite autoincrement fix
973 Revision 1.23 2004/07/04 10:28:06 rurban
976 Revision 1.22 2004/07/03 17:21:28 rurban
977 updated docs: submitted new mysql bugreport (#1491 did not fix it)
979 Revision 1.21 2004/07/03 16:51:05 rurban
980 optional DBADMIN_USER:DBADMIN_PASSWD for action=upgrade (if no ALTER permission)
981 added atomic mysql REPLACE for PearDB as in ADODB
982 fixed _lock_tables typo links => link
983 fixes unserialize ADODB bug in line 180
985 Revision 1.20 2004/07/03 14:48:18 rurban
986 Tested new mysql 4.1.3-beta: binary search bug as fixed.
987 => fixed action=upgrade,
988 => version check in PearDB also (as in ADODB)
990 Revision 1.19 2004/06/19 12:19:09 rurban
991 slightly improved docs
993 Revision 1.18 2004/06/19 11:47:17 rurban
994 added CheckConfigUpdate: CACHE_CONTROL = NONE => NO_CACHE
996 Revision 1.17 2004/06/17 11:31:50 rurban
997 check necessary localized actionpages
999 Revision 1.16 2004/06/16 10:38:58 rurban
1000 Disallow refernces in calls if the declaration is a reference
1001 ("allow_call_time_pass_reference clean").
1002 PhpWiki is now allow_call_time_pass_reference = Off clean,
1003 but several external libraries may not.
1004 In detail these libs look to be affected (not tested):
1008 Revision 1.15 2004/06/07 19:50:40 rurban
1009 add owner field to mimified dump
1011 Revision 1.14 2004/06/07 18:38:18 rurban
1012 added mysql 4.1.x search fix
1014 Revision 1.13 2004/06/04 20:32:53 rurban
1015 Several locale related improvements suggested by Pierrick Meignen
1016 LDAP fix by John Cole
1017 reanable admin check without ENABLE_PAGEPERM in the admin plugins
1019 Revision 1.12 2004/05/18 13:59:15 rurban
1020 rename simpleQuery to genericSqlQuery
1022 Revision 1.11 2004/05/15 13:06:17 rurban
1023 skip the HomePage, at first upgrade the ActionPages, then the database, then the rest
1025 Revision 1.10 2004/05/15 01:19:41 rurban
1026 upgrade prefix fix by Kai Krakow
1028 Revision 1.9 2004/05/14 11:33:03 rurban
1029 version updated to 1.3.11pre
1030 upgrade stability fix
1032 Revision 1.8 2004/05/12 10:49:55 rurban
1033 require_once fix for those libs which are loaded before FileFinder and
1034 its automatic include_path fix, and where require_once doesn't grok
1035 dirname(__FILE__) != './lib'
1036 upgrade fix with PearDB
1037 navbar.tmpl: remove spaces for IE button alignment
1039 Revision 1.7 2004/05/06 17:30:38 rurban
1040 CategoryGroup: oops, dos2unix eol
1041 improved phpwiki_version:
1042 pre -= .0001 (1.3.10pre: 1030.099)
1043 -p1 += .001 (1.3.9-p1: 1030.091)
1044 improved InstallTable for mysql and generic SQL versions and all newer tables so far.
1045 abstracted more ADODB/PearDB methods for action=upgrade stuff:
1046 backend->backendType(), backend->database(),
1047 backend->listOfFields(),
1048 backend->listOfTables(),
1050 Revision 1.6 2004/05/03 15:05:36 rurban
1053 Revision 1.4 2004/05/02 21:26:38 rurban
1054 limit user session data (HomePageHandle and auth_dbi have to invalidated anyway)
1055 because they will not survive db sessions, if too large.
1056 extended action=upgrade
1057 some WikiTranslation button work
1058 revert WIKIAUTH_UNOBTAINABLE (need it for main.php)
1059 some temp. session debug statements
1061 Revision 1.3 2004/04/29 22:33:30 rurban
1062 fixed sf.net bug #943366 (Kai Krakow)
1063 couldn't load localized url-undecoded pagenames
1065 Revision 1.2 2004/03/12 15:48:07 rurban
1066 fixed explodePageList: wrong sortby argument order in UnfoldSubpages
1067 simplified lib/stdlib.php:explodePageList
1075 // c-basic-offset: 4
1076 // c-hanging-comment-ender-p: nil
1077 // indent-tabs-mode: nil