2 rcs_id('$Id: upgrade.php,v 1.57 2007-01-04 16:43:09 rurban Exp $');
4 Copyright 2004,2005,2006,2007 $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");
54 function Upgrade (&$request) {
55 $this->request =& $request;
56 $this->dbi =& $request->_dbi; // no reference for dbadmin ?
60 * TODO: check for the pgsrc_version number, not the revision mtime only
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');
72 $pageinfo = $parts[0];
73 $stat = stat($path."/".$filename);
74 $new_mtime = @$pageinfo['versiondata']['mtime'];
76 $new_mtime = @$pageinfo['versiondata']['lastmodified'];
78 $new_mtime = @$pageinfo['pagedata']['date'];
80 $new_mtime = $stat[9];
81 if ($new_mtime > $page_mtime) {
82 echo "$path/$pagename: ",_("newer than the existing page."),
83 _(" replace "),"($new_mtime > $page_mtime)","<br />\n";
84 LoadAny($this->request, $path."/".$filename);
87 echo "$path/$pagename: ",_("older than the existing page."),
88 _(" skipped"),".<br />\n";
91 echo "$path/$pagename: ",("unknown format."),
92 _(" skipped"),".<br />\n";
95 echo sprintf(_("%s does not exist"),$pagename),"<br />\n";
96 LoadAny($this->request, $path."/".$filename);
102 * If a matching pgsrc => pluginname exists
103 * Need the english filename (required precondition: urlencode == urldecode).
104 * Returns the plugin name.
106 function isActionPage($filename) {
107 static $special = array("DebugInfo" => "_BackendInfo",
108 "PhpWikiRecentChanges" => "RssFeed",
109 "ProjectSummary" => "RssFeed",
110 "RecentReleases" => "RssFeed",
111 "InterWikiMap" => "InterWikiMap",
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;
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
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));
144 $this->doPgsrcUpdate($pagename, $path, $filename);
149 // see loadsave.php for saving new pages.
150 function CheckPgsrcUpdate() {
151 echo "<h3>",sprintf(_("check for necessary %s updates"),
153 if ($this->dbi->get_db_version() < 1030.12200612) {
154 echo "<h4>",_("rename to Help: pages"),"</h4>\n";
156 $path = FindLocalizedFile(WIKI_PGSRC);
157 $pgsrc = new fileSet($path);
158 // fixme: verification, ...
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;
168 if ($pagename == _("HomePage")) $isHomePage = true;
169 if ($pagename == "HomePage") $isHomePage = true;
171 echo "$path/$pagename: ",_("always skip the HomePage."),
172 _(" skipped"),".<br />\n";
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);
182 $this->doPgsrcUpdate($pagename,$path,$filename);
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";
194 echo " <b><font color=\"red\">", _("FAILED"), "</font></b>",
197 echo _(" skipped")," <br />\n";
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);
209 * TODO: Search table definition in appropriate schema
211 * Supported: mysql and generic SQL, for ADODB and PearDB.
213 function installTable($table, $backend_type) {
215 if (!$this->dbi->_backend->isSQL()) return;
216 echo _("MISSING")," ... \n";
217 $backend = &$this->dbi->_backend->_dbh;
219 $schema = findFile("schemas/${backend_type}.sql");
221 echo " ",_("FAILED"),": ",sprintf(_("no schema %s found"),
222 "schemas/${backend_type}.sql")," ... <br />\n";
226 extract($this->dbi->_backend->_table_names);
227 $prefix = isset($DBParams['prefix']) ? $DBParams['prefix'] : '';
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),
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,
247 sess_ip CHAR(15) NOT NULL
249 $this->dbi->genericSqlQuery("CREATE UNIQUE INDEX sess_id ON $session_tbl (sess_id)");
251 $this->dbi->genericSqlQuery("CREATE INDEX sess_date on session (sess_date)");
252 echo " ",_("CREATED");
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 '',
264 $this->dbi->genericSqlQuery("
265 CREATE TABLE $pref_tbl (
266 userid CHAR(48) NOT NULL,
267 prefs TEXT NULL DEFAULT ''
269 $this->dbi->genericSqlQuery("CREATE UNIQUE INDEX userid ON $pref_tbl (userid)");
271 echo " ",_("CREATED");
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',
284 $this->dbi->genericSqlQuery("
285 CREATE TABLE $member_tbl (
286 userid CHAR(48) NOT NULL,
287 groupname CHAR(48) NOT NULL DEFAULT 'users'
289 $this->dbi->genericSqlQuery("CREATE INDEX userid ON $member_tbl (userid)");
290 $this->dbi->genericSqlQuery("CREATE INDEX groupname ON $member_tbl (groupname)");
292 echo " ",_("CREATED");
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)
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
317 $this->dbi->genericSqlQuery("CREATE UNIQUE INDEX rating"
318 ." ON $rating_tbl (dimension, raterpage, rateepage)");
320 echo " ",_("CREATED");
323 $log_tbl = $prefix.'accesslog';
324 // fields according to http://www.outoforder.cc/projects/apache/mod_log_sql/docs-2.0/#id2756178
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)
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),
362 request_duration float
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");
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.
378 function CheckDatabaseUpdate() {
379 global $DBAuthParams;
381 echo "<h3>",sprintf(_("check for necessary %s updates"),
383 " - ", DATABASE_TYPE,"</h3>\n";
384 $dbadmin = $this->request->getArg('dbadmin');
385 if ($this->dbi->_backend->isSQL()) {
387 if (isset($dbadmin['cancel'])) {
388 echo _("CANCEL")," <br />\n";
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);
403 echo _("OK")," <br />\n";
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();
414 if (!$this->dbi->_backend->isSQL()) return;
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);
422 echo _("OK")," <br />\n";
425 if ((class_exists("RatingsUserFactory") or $this->dbi->isWikiPage(_("RateIt")))) {
427 echo sprintf(_("check for table %s"), $table)," ...";
428 if (!in_array($prefix.$table, $tables)) {
429 $this->installTable($table, $backend_type);
431 echo _("OK")," <br />\n";
434 $backend = &$this->dbi->_backend->_dbh;
435 extract($this->dbi->_backend->_table_names);
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);
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)");
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);
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"), " => CHAR(32) ";
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"), " => CHAR(15) ";
480 echo _("OK"), "<br />\n";
485 ALTER TABLE link ADD relation INT DEFAULT 0;
486 CREATE INDEX linkrelation ON link (relation);
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']);
499 $parseDSN = DB::parseDSN($DBParams['dsn']);
500 $username = $this->dbi->_backend->qstr($parseDSN['username']);
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");
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";
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";
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",
530 echo _("OK"), "<br />\n";
533 echo _("OK"), "<br />\n";
535 //mysql_select_db($this->dbi->_backend->database(), $this->dbi->_backend->connection());
537 echo sprintf(_("version <em>%s</em> not affected"), $mysql_version),"<br />\n";
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')
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";
565 echo _("OK"), "<br />\n";
567 echo _("OK"), "<br />\n";
572 mysql_free_result($fields);
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"))
594 echo _("OK"), "<br />\n";
596 //SET CHARACTER SET latin1
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>",
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>"
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>\"%'");
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'");
652 echo "<b>",_("FIXED"),"</b>", "<br />\n";
654 echo _("OK"),"<br />\n";
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);
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)");
673 $this->_upgrade_cached_html();
675 if (phpwiki_version() >= 1030.122006 and $this->dbi->get_db_version() < 1030.13) {
676 $this->dbi->set_db_version(1030.13);
683 * Filter SQL missing permissions errors.
685 * A wrong DBADMIN user will not be able to connect
686 * @see _is_false_error, ErrorManager
689 function _dbpermission_filter($err) {
690 if ( $err->isWarning() ) {
691 global $ErrorManager;
692 $this->error_caught = 1;
693 $ErrorManager->_postponed_errors[] = $err;
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']);
705 $dsn = parseDSN($AdminParams['dsn']);
707 $AdminParams['dsn'] = sprintf("%s://%s:%s@%s/%s",
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();
727 function _db_init () {
728 if (!$this->dbi->_backend->isSQL()) return;
730 /* SQLite never needs admin params */
731 $backend_type = $this->dbi->_backend->backendType();
732 if (substr($backend_type,0,6)=="sqlite") {
735 $dbadmin_user = 'root';
736 if ($dbadmin = $this->request->getArg('dbadmin')) {
737 $dbadmin_user = $dbadmin['user'];
738 if (isset($dbadmin['cancel'])) {
740 } elseif (!empty($dbadmin_user)) {
741 if ($this->_try_dbadmin_user($dbadmin['user'], $dbadmin['passwd']))
744 } elseif (DBADMIN_USER) {
745 if ($this->_try_dbadmin_user(DBADMIN_USER, DBADMIN_PASSWD))
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."),
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]",
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]",
770 'maxlength'=>256)))),
771 HTML::tr(HTML::td(array('align'=>'center', 'colspan' => 2),
772 Button("submit:", _("Submit"), 'wikiaction'),
774 Button("submit:dbadmin[cancel]", _("Cancel"),
777 echo "</div><!-- content -->\n";
778 echo asXML(Template("bottom"));
779 echo "</body></html>\n";
780 $this->request->finish();
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.
789 * It is only rarelely needed: for current page only, if-not-modified,
790 * but was extracetd for every simple page iteration.
792 function _upgrade_cached_html ( $verbose=true ) {
794 if (!$this->dbi->_backend->isSQL()) return;
796 if (phpwiki_version() >= 1030.10) {
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);
803 echo _("SKIP"), "<br />\n";
806 if (!strstr(strtolower(join(':', $fields)), "cached_html")) {
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");
813 $this->dbi->genericSqlQuery("ALTER TABLE $page_tbl ADD cached_html BLOB");
815 echo "<b>",_("CONVERTING"),"</b>"," ... ";
816 $count = _convert_cached_html();
818 echo $count, " ", _("OK"), "<br />\n";
821 echo _("OK"), "<br />\n";
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.
831 function _convert_cached_html () {
833 if (!$this->dbi->_backend->isSQL()) return;
834 //if (!in_array(DATABASE_TYPE, array('SQL','ADODB'))) return;
836 $pages = $this->dbi->getAllPages();
837 $cache =& $this->dbi->_cache;
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));
857 * upgrade to 1.3.13 link structure.
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>"," ... ";
866 $this->dbi->_backend->rebuild();
867 echo _("OK"), "<br />\n";
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")
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";
891 echo " <b><font color=\"red\">", _("FAILED"), "</font></b><br />\n";
898 function fixConfigIni($match, $new) {
899 $file = FindFile("config/config.ini");
901 if (is_writable($file)) {
902 $in = fopen($file,"rb");
903 $out = fopen($tmp = tempnam(FindFile("uploads"),"cfg"),"wb");
905 $tmp = str_replace("/","\\",$tmp);
906 while ($s = fgets($in)) {
907 if (preg_match($match, $s)) {
908 $s = $new . (isWindows() ? "\r\n" : "\n");
916 echo " <b><font color=\"red\">",_("FAILED"),"</font></b>: ",
917 sprintf(_("%s not found"), $match);
920 @unlink("$file.bak");
921 @rename($file,"$file.bak");
922 if (rename($tmp, $file))
923 echo " <b>",_("FIXED"),"</b>";
925 echo " <b>",_("FAILED"),"</b>: ";
926 sprintf(_("couldn't move %s to %s"), $tmp, $file);
932 echo " <b><font color=\"red\">",_("FAILED"),"</font></b>: ",
933 sprintf(_("%s is not writable"), $file);
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 /> ",
944 _("CACHE_CONTROL is set to 'NONE', and must be changed to 'NO_CACHE'"),
946 fixConfigIni("/^\s*CACHE_CONTROL\s*=\s*NONE/","CACHE_CONTROL = NO_CACHE");
951 echo _("check for GROUP_METHOD = NONE")," ... ";
952 if (defined('GROUP_METHOD') and GROUP_METHOD == '') {
953 echo "<br /> ",
954 _("GROUP_METHOD is set to NONE, and must be changed to \"NONE\""),
956 fixConfigIni("/^\s*GROUP_METHOD\s*=\s*NONE/","GROUP_METHOD = \"NONE\"");
968 * Upgrade: Base class for multipage worksteps
969 * identify, validate, display options, next step
974 // TODO: At which step are we?
975 // validate and do it again or go on with next step.
977 /** entry function from lib/main.php
979 function DoUpgrade($request) {
981 if (!$request->_user->isAdmin()) {
982 $request->_notAuthorized(WIKIAUTH_ADMIN);
984 HTML::div(array('class' => 'disabled-plugin'),
985 fmt("Upgrade disabled: user != isAdmin")));
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);
998 //CheckThemeUpdate($request);
999 if (!$request->getArg('noplugin'))
1000 $upgrade->CheckPluginUpdate($request);
1001 if (!$request->getArg('noconfig'))
1002 $upgrade->CheckConfigUpdate($request);
1003 EndLoadDump($request);
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.
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
1015 Revision 1.54 2006/12/03 17:07:29 rurban
1016 #1535843 by matt brown: Upgrade Wizard Password fixes are not portable
1018 Revision 1.53 2006/12/03 17:03:18 rurban
1019 #1535851 by matt brown
1021 Revision 1.52 2006/12/03 17:01:18 rurban
1022 #1535839 by matt brown
1024 Revision 1.51 2006/08/07 21:05:30 rurban
1025 patch #1535837 (debian)
1027 Revision 1.50 2006/06/18 11:04:09 rurban
1030 Revision 1.49 2006/05/18 06:03:39 rurban
1031 use $dbh->_backend->isSQL
1033 Revision 1.48 2005/11/14 22:32:38 rurban
1034 remove user, SKIP on !session
1036 Revision 1.47 2005/02/27 19:13:27 rurban
1039 Revision 1.46 2005/02/12 17:22:18 rurban
1040 locale update: missing . : fixed. unified strings
1043 Revision 1.45 2005/02/10 19:01:19 rurban
1046 Revision 1.44 2005/02/07 15:40:42 rurban
1047 use defined CHARSET for db. more comments
1049 Revision 1.43 2005/02/04 11:44:07 rurban
1050 check passwd in access_log
1052 Revision 1.42 2005/02/02 19:38:13 rurban
1053 prefer utf8 pagenames for collate issues
1055 Revision 1.41 2005/01/31 12:15:29 rurban
1058 Revision 1.40 2005/01/30 23:22:17 rurban
1061 Revision 1.39 2005/01/30 23:09:17 rurban
1062 sanify session fields
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)
1067 Revision 1.37 2005/01/20 10:19:08 rurban
1068 add InterWikiMap to special pages
1070 Revision 1.36 2004/12/20 12:56:11 rurban
1071 patch #1088128 by Kai Krakow. avoid chicken & egg problem
1073 Revision 1.35 2004/12/13 14:35:41 rurban
1076 Revision 1.34 2004/12/11 09:39:28 rurban
1079 Revision 1.33 2004/12/10 22:33:39 rurban
1080 add WikiAdminUtils method for convert-cached-html
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)
1088 Revision 1.31 2004/12/10 02:45:26 rurban
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.
1094 Revision 1.30 2004/11/29 17:58:57 rurban
1097 Revision 1.29 2004/11/29 16:08:31 rurban
1100 Revision 1.28 2004/11/16 16:25:14 rurban
1101 fix accesslog tablename, print CREATED only if really done
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)
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)
1112 Revision 1.25 2004/09/06 08:28:00 rurban
1113 rename genericQuery to genericSqlQuery
1115 Revision 1.24 2004/07/05 13:56:22 rurban
1116 sqlite autoincrement fix
1118 Revision 1.23 2004/07/04 10:28:06 rurban
1121 Revision 1.22 2004/07/03 17:21:28 rurban
1122 updated docs: submitted new mysql bugreport (#1491 did not fix it)
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
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)
1135 Revision 1.19 2004/06/19 12:19:09 rurban
1136 slightly improved docs
1138 Revision 1.18 2004/06/19 11:47:17 rurban
1139 added CheckConfigUpdate: CACHE_CONTROL = NONE => NO_CACHE
1141 Revision 1.17 2004/06/17 11:31:50 rurban
1142 check necessary localized actionpages
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):
1153 Revision 1.15 2004/06/07 19:50:40 rurban
1154 add owner field to mimified dump
1156 Revision 1.14 2004/06/07 18:38:18 rurban
1157 added mysql 4.1.x search fix
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
1164 Revision 1.12 2004/05/18 13:59:15 rurban
1165 rename simpleQuery to genericSqlQuery
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
1170 Revision 1.10 2004/05/15 01:19:41 rurban
1171 upgrade prefix fix by Kai Krakow
1173 Revision 1.9 2004/05/14 11:33:03 rurban
1174 version updated to 1.3.11pre
1175 upgrade stability fix
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 button alignment
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(),
1195 Revision 1.6 2004/05/03 15:05:36 rurban
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
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
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
1220 // c-basic-offset: 4
1221 // c-hanging-comment-ender-p: nil
1222 // indent-tabs-mode: nil