2 rcs_id('$Id: upgrade.php,v 1.26 2004-10-14 19:19:34 rurban Exp $');
5 Copyright 2004 $ThePhpWikiProgrammingTeam
7 This file is part of PhpWiki.
9 PhpWiki is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 PhpWiki is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with PhpWiki; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 * Upgrade the WikiDB and config settings after installing a new
28 * Status: experimental, no queries for verification yet, no db update,
31 * Installation on an existing PhpWiki database needs some
32 * additional worksteps. Each step will require multiple pages.
35 * 1. Check for new or changed database schema and update it
36 * according to some predefined upgrade tables. (medium)
37 * 2. Check for new or changed (localized) pgsrc/ pages and ask
38 * for upgrading these. Check timestamps, upgrade silently or
39 * show diffs if existing. Overwrite or merge (easy)
40 * 3. Check for new or changed or deprecated index.php/config.ini settings
41 * and help in upgrading these. (hard)
42 * 3a Convert old-style index.php into config/config.ini. (easy)
43 * 4. Check for changed plugin invocation arguments. (hard)
44 * 5. Check for changed theme variables. (hard)
45 * 6. Convert the automatic update to a class-based multi-page
48 * TODO: overwrite=1 link on edit conflicts at end of page to overwrite all.
50 * @author: Reini Urban
52 require_once("lib/loadsave.php");
53 //define('DBADMIN_USER','rurban');
54 //define('DBADMIN_PASSWD','');
57 * TODO: check for the pgsrc_version number, not the revision mtime only
59 function doPgsrcUpdate(&$request,$pagename,$path,$filename) {
60 $dbi = $request->getDbh();
61 $page = $dbi->getPage($pagename);
62 if ($page->exists()) {
63 // check mtime: update automatically if pgsrc is newer
64 $rev = $page->getCurrentRevision();
65 $page_mtime = $rev->get('mtime');
66 $data = implode("", file($path."/".$filename));
67 if (($parts = ParseMimeifiedPages($data))) {
68 usort($parts, 'SortByPageVersion');
70 $pageinfo = $parts[0];
71 $stat = stat($path."/".$filename);
72 $new_mtime = @$pageinfo['versiondata']['mtime'];
74 $new_mtime = @$pageinfo['versiondata']['lastmodified'];
76 $new_mtime = @$pageinfo['pagedata']['date'];
78 $new_mtime = $stat[9];
79 if ($new_mtime > $page_mtime) {
80 echo "$path/$pagename: ",_("newer than the existing page."),
81 _(" replace "),"($new_mtime > $page_mtime)","<br />\n";
82 LoadAny($request,$path."/".$filename);
85 echo "$path/$pagename: ",_("older than the existing page."),
86 _(" skipped"),".<br />\n";
89 echo "$path/$pagename: ",("unknown format."),
90 _(" skipped"),".<br />\n";
93 echo sprintf(_("%s does not exist"),$pagename),"<br />\n";
94 LoadAny($request,$path."/".$filename);
99 /** need the english filename (required precondition: urlencode == urldecode)
100 * returns the plugin name.
102 function isActionPage($filename) {
103 static $special = array("DebugInfo" => "_BackendInfo",
104 "PhpWikiRecentChanges" => "RssFeed",
105 "ProjectSummary" => "RssFeed",
106 "RecentReleases" => "RssFeed",
108 $base = preg_replace("/\..{1,4}$/","",basename($filename));
109 if (isset($special[$base])) return $special[$base];
110 if (FindFile("lib/plugin/".$base.".php",true)) return $base;
114 function CheckActionPageUpdate(&$request) {
115 echo "<h3>",_("check for necessary ActionPage updates"),"</h3>\n";
116 $dbi = $request->getDbh();
117 $path = FindFile('pgsrc');
118 $pgsrc = new fileSet($path);
119 // most actionpages have the same name as the plugin
120 $loc_path = FindLocalizedFile('pgsrc');
121 foreach ($pgsrc->getFiles() as $filename) {
122 if (substr($filename,-1,1) == '~') continue;
123 $pagename = urldecode($filename);
124 if (isActionPage($filename)) {
125 $translation = gettext($pagename);
126 if ($translation == $pagename)
127 doPgsrcUpdate($request, $pagename, $path, $filename);
128 elseif (FindLocalizedFile('pgsrc/'.urlencode($translation),1))
129 doPgsrcUpdate($request, $translation, $loc_path,
130 urlencode($translation));
132 doPgsrcUpdate($request, $pagename, $path, $filename);
137 // see loadsave.php for saving new pages.
138 function CheckPgsrcUpdate(&$request) {
139 echo "<h3>",_("check for necessary pgsrc updates"),"</h3>\n";
140 $dbi = $request->getDbh();
141 $path = FindLocalizedFile(WIKI_PGSRC);
142 $pgsrc = new fileSet($path);
143 // fixme: verification, ...
145 foreach ($pgsrc->getFiles() as $filename) {
146 if (substr($filename,-1,1) == '~') continue;
147 $pagename = urldecode($filename);
148 // don't ever update the HomePage
149 if (defined(HOME_PAGE))
150 if ($pagename == HOME_PAGE) $isHomePage = true;
152 if ($pagename == _("HomePage")) $isHomePage = true;
153 if ($pagename == "HomePage") $isHomePage = true;
155 echo "$path/$pagename: ",_("always skip the HomePage."),
156 _(" skipped"),".<br />\n";
160 if (!isActionPage($filename)) {
161 doPgsrcUpdate($request,$pagename,$path,$filename);
168 * TODO: Search table definition in appropriate schema
170 * Supported: mysql and generic SQL, for ADODB and PearDB.
172 function installTable(&$dbh, $table, $backend_type) {
174 if (!in_array($DBParams['dbtype'],array('SQL','ADODB'))) return;
175 echo _("MISSING")," ... \n";
176 $backend = &$dbh->_backend->_dbh;
178 $schema = findFile("schemas/${backend_type}.sql");
180 echo " ",_("FAILED"),": ",sprintf(_("no schema %s found"),"schemas/${backend_type}.sql")," ... <br />\n";
184 extract($dbh->_backend->_table_names);
185 $prefix = isset($DBParams['prefix']) ? $DBParams['prefix'] : '';
188 assert($session_tbl);
189 if ($backend_type == 'mysql') {
190 $dbh->genericSqlQuery("
191 CREATE TABLE $session_tbl (
192 sess_id CHAR(32) NOT NULL DEFAULT '',
193 sess_data BLOB NOT NULL,
194 sess_date INT UNSIGNED NOT NULL,
195 sess_ip CHAR(15) NOT NULL,
196 PRIMARY KEY (sess_id),
200 $dbh->genericSqlQuery("
201 CREATE TABLE $session_tbl (
202 sess_id CHAR(32) NOT NULL DEFAULT '',
203 sess_data ".($backend_type == 'pgsql'?'TEXT':'BLOB')." NOT NULL,
205 sess_ip CHAR(15) NOT NULL
207 $dbh->genericSqlQuery("CREATE UNIQUE INDEX sess_id ON $session_tbl (sess_id)");
209 $dbh->genericSqlQuery("CREATE INDEX sess_date on session (sess_date)");
212 $user_tbl = $prefix.'user';
213 if ($backend_type == 'mysql') {
214 $dbh->genericSqlQuery("
215 CREATE TABLE $user_tbl (
216 userid CHAR(48) BINARY NOT NULL UNIQUE,
217 passwd CHAR(48) BINARY DEFAULT '',
221 $dbh->genericSqlQuery("
222 CREATE TABLE $user_tbl (
223 userid CHAR(48) NOT NULL,
224 passwd CHAR(48) DEFAULT ''
226 $dbh->genericSqlQuery("CREATE UNIQUE INDEX userid ON $user_tbl (userid)");
230 $pref_tbl = $prefix.'pref';
231 if ($backend_type == 'mysql') {
232 $dbh->genericSqlQuery("
233 CREATE TABLE $pref_tbl (
234 userid CHAR(48) BINARY NOT NULL UNIQUE,
235 prefs TEXT NULL DEFAULT '',
239 $dbh->genericSqlQuery("
240 CREATE TABLE $pref_tbl (
241 userid CHAR(48) NOT NULL,
242 prefs TEXT NULL DEFAULT '',
244 $dbh->genericSqlQuery("CREATE UNIQUE INDEX userid ON $pref_tbl (userid)");
248 $member_tbl = $prefix.'member';
249 if ($backend_type == 'mysql') {
250 $dbh->genericSqlQuery("
251 CREATE TABLE $member_tbl (
252 userid CHAR(48) BINARY NOT NULL,
253 groupname CHAR(48) BINARY NOT NULL DEFAULT 'users',
258 $dbh->genericSqlQuery("
259 CREATE TABLE $member_tbl (
260 userid CHAR(48) NOT NULL,
261 groupname CHAR(48) NOT NULL DEFAULT 'users',
263 $dbh->genericSqlQuery("CREATE INDEX userid ON $member_tbl (userid)");
264 $dbh->genericSqlQuery("CREATE INDEX groupname ON $member_tbl (groupname)");
268 $rating_tbl = $prefix.'rating';
269 if ($backend_type == 'mysql') {
270 $dbh->genericSqlQuery("
271 CREATE TABLE $rating_tbl (
272 dimension INT(4) NOT NULL,
273 raterpage INT(11) NOT NULL,
274 rateepage INT(11) NOT NULL,
275 ratingvalue FLOAT NOT NULL,
276 rateeversion INT(11) NOT NULL,
277 tstamp TIMESTAMP(14) NOT NULL,
278 PRIMARY KEY (dimension, raterpage, rateepage)
281 $dbh->genericSqlQuery("
282 CREATE TABLE $rating_tbl (
283 dimension INT(4) NOT NULL,
284 raterpage INT(11) NOT NULL,
285 rateepage INT(11) NOT NULL,
286 ratingvalue FLOAT NOT NULL,
287 rateeversion INT(11) NOT NULL,
288 tstamp TIMESTAMP(14) NOT NULL,
290 $dbh->genericSqlQuery("CREATE UNIQUE INDEX rating ON $rating_tbl (dimension, raterpage, rateepage)");
294 echo " ",_("CREATED"),"<br />\n";
298 * currently update only session, user, pref and member
299 * jeffs-hacks database api (around 1.3.2) later
300 * people should export/import their pages if using that old versions.
302 function CheckDatabaseUpdate(&$request) {
303 global $DBParams, $DBAuthParams;
304 if (!in_array($DBParams['dbtype'], array('SQL','ADODB'))) return;
305 echo "<h3>",_("check for necessary database updates"),"</h3>\n";
306 if (defined('DBADMIN_USER') and DBADMIN_USER) {
307 // if need to connect as the root user, for alter permissions
308 $AdminParams = $DBParams;
309 if ($DBParams['dbtype'] == 'SQL')
310 $dsn = DB::parseDSN($AdminParams['dsn']);
312 $dsn = parseDSN($AdminParams['dsn']);
313 $AdminParams['dsn'] = sprintf("%s://%s:%s@%s/%s",
319 $dbh = WikiDB::open($AdminParams);
321 $dbh = &$request->_dbi;
323 $tables = $dbh->_backend->listOfTables();
324 $backend_type = $dbh->_backend->backendType();
325 $prefix = isset($DBParams['prefix']) ? $DBParams['prefix'] : '';
326 foreach (explode(':','session:user:pref:member') as $table) {
327 echo sprintf(_("check for table %s"), $table)," ...";
328 if (!in_array($prefix.$table, $tables)) {
329 installTable($dbh, $table, $backend_type);
331 echo _("OK")," <br />\n";
334 $backend = &$dbh->_backend->_dbh;
335 // 1.3.8 added session.sess_ip
336 if (phpwiki_version() >= 1030.08 and USE_DB_SESSION and isset($request->_dbsession)) {
337 echo _("check for new session.sess_ip column")," ... ";
338 $database = $dbh->_backend->database();
339 assert(!empty($DBParams['db_session_table']));
340 $session_tbl = $prefix . $DBParams['db_session_table'];
341 $sess_fields = $dbh->_backend->listOfFields($database, $session_tbl);
342 if (!strstr(strtolower(join(':', $sess_fields)),"sess_ip")) {
343 // TODO: postgres test (should be able to add columns at the end, but not in between)
344 echo "<b>",_("ADDING"),"</b>"," ... ";
345 $dbh->genericSqlQuery("ALTER TABLE $session_tbl ADD sess_ip CHAR(15) NOT NULL");
346 $dbh->genericSqlQuery("CREATE INDEX sess_date ON $session_tbl (sess_date)");
352 // 1.3.10 mysql requires page.id auto_increment
353 // mysql, mysqli or mysqlt
354 if (phpwiki_version() >= 1030.099 and substr($backend_type,0,5) == 'mysql') {
355 echo _("check for page.id auto_increment flag")," ...";
356 extract($dbh->_backend->_table_names);
357 assert(!empty($page_tbl));
358 $database = $dbh->_backend->database();
359 $fields = mysql_list_fields($database, $page_tbl, $dbh->_backend->connection());
360 $columns = mysql_num_fields($fields);
361 for ($i = 0; $i < $columns; $i++) {
362 if (mysql_field_name($fields, $i) == 'id') {
363 $flags = mysql_field_flags($fields, $i);
364 //DONE: something was wrong with ADODB here.
365 if (!strstr(strtolower($flags), "auto_increment")) {
366 echo "<b>",_("ADDING"),"</b>"," ... ";
367 // MODIFY col_def valid since mysql 3.22.16,
368 // older mysql's need CHANGE old_col col_def
369 $dbh->genericSqlQuery("ALTER TABLE $page_tbl CHANGE id id INT NOT NULL AUTO_INCREMENT");
370 $fields = mysql_list_fields($database, $page_tbl);
371 if (!strstr(strtolower(mysql_field_flags($fields, $i)), "auto_increment"))
372 echo " <b><font color=\"red\">", _("FAILED"), "</font></b><br />\n";
374 echo _("OK"), "<br />\n";
376 echo _("OK"), "<br />\n";
381 mysql_free_result($fields);
383 // check for mysql 4.1.x/5.0.0a binary search problem.
384 // http://bugs.mysql.com/bug.php?id=4398
385 // "select * from page where LOWER(pagename) like '%search%'" does not apply LOWER!
386 // confirmed for 4.1.0alpha,4.1.3-beta,5.0.0a; not yet tested for 4.1.2alpha,
387 // TODO: there's a known workaround, not yet applied. on windows only.
388 if (substr($backend_type,0,5) == 'mysql') {
389 echo _("check for mysql 4.1.x/5.0.0 binary search problem")," ...";
390 $result = mysql_query("SELECT VERSION()",$dbh->_backend->connection());
391 $row = mysql_fetch_row($result);
392 $mysql_version = $row[0];
393 $arr = explode('.',$mysql_version);
394 $version = (string)(($arr[0] * 100) + $arr[1]) . "." . (integer)$arr[2];
395 if ($version >= 401.0) {
396 $dbh->genericSqlQuery("ALTER TABLE $page_tbl CHANGE pagename pagename VARCHAR(100) NOT NULL;");
397 echo sprintf(_("version <em>%s</em> <b>FIXED</b>"), $mysql_version),"<br />\n";
399 echo sprintf(_("version <em>%s</em> not affected"), $mysql_version),"<br />\n";
405 function fixConfigIni($match, $new) {
406 $file = FindFile("config/config.ini");
408 if (is_writable($file)) {
409 $in = fopen($file,"rb");
410 $out = fopen($tmp = tempnam(FindFile("uploads"),"cfg"),"wb");
412 $tmp = str_replace("/","\\",$tmp);
413 while ($s = fgets($in)) {
414 if (preg_match($match, $s)) {
415 $s = $new . (isWindows() ? "\r\n" : "\n");
423 echo " <b><font color=\"red\">",_("FAILED"),"</font></b>: ",
424 sprintf(_("%s not found"), $match);
427 @unlink("$file.bak");
428 @rename($file,"$file.bak");
429 if (rename($tmp, $file))
430 echo " <b>",_("FIXED"),"</b>";
432 echo " <b>",_("FAILED"),"</b>: ";
433 sprintf(_("couldn't move %s to %s"), $tmp, $file);
439 echo " <b><font color=\"red\">",_("FAILED"),"</font></b>: ",
440 sprintf(_("%s is not writable"), $file);
445 function CheckConfigUpdate(&$request) {
446 echo "<h3>",_("check for necessary config updates"),"</h3>\n";
447 echo _("check for old CACHE_CONTROL = NONE")," ... ";
448 if (defined('CACHE_CONTROL') and CACHE_CONTROL == '') {
449 echo "<br /> ",_("CACHE_CONTROL is set to 'NONE', and must be changed to 'NO_CACHE'")," ...";
450 fixConfigIni("/^\s*CACHE_CONTROL\s*=\s*NONE/","CACHE_CONTROL = NO_CACHE");
460 * Upgrade: Base class for multipage worksteps
461 * identify, validate, display options, next step
466 class Upgrade_CheckPgsrc extends Upgrade {
469 class Upgrade_CheckDatabaseUpdate extends Upgrade {
472 // TODO: At which step are we?
473 // validate and do it again or go on with next step.
475 /** entry function from lib/main.php
477 function DoUpgrade($request) {
479 if (!$request->_user->isAdmin()) {
480 $request->_notAuthorized(WIKIAUTH_ADMIN);
482 HTML::div(array('class' => 'disabled-plugin'),
483 fmt("Upgrade disabled: user != isAdmin")));
487 StartLoadDump($request, _("Upgrading this PhpWiki"));
488 CheckActionPageUpdate($request);
489 CheckDatabaseUpdate($request);
490 CheckPgsrcUpdate($request);
491 //CheckThemeUpdate($request);
492 CheckConfigUpdate($request);
493 EndLoadDump($request);
498 $Log: not supported by cvs2svn $
499 Revision 1.25 2004/09/06 08:28:00 rurban
500 rename genericQuery to genericSqlQuery
502 Revision 1.24 2004/07/05 13:56:22 rurban
503 sqlite autoincrement fix
505 Revision 1.23 2004/07/04 10:28:06 rurban
508 Revision 1.22 2004/07/03 17:21:28 rurban
509 updated docs: submitted new mysql bugreport (#1491 did not fix it)
511 Revision 1.21 2004/07/03 16:51:05 rurban
512 optional DBADMIN_USER:DBADMIN_PASSWD for action=upgrade (if no ALTER permission)
513 added atomic mysql REPLACE for PearDB as in ADODB
514 fixed _lock_tables typo links => link
515 fixes unserialize ADODB bug in line 180
517 Revision 1.20 2004/07/03 14:48:18 rurban
518 Tested new mysql 4.1.3-beta: binary search bug as fixed.
519 => fixed action=upgrade,
520 => version check in PearDB also (as in ADODB)
522 Revision 1.19 2004/06/19 12:19:09 rurban
523 slightly improved docs
525 Revision 1.18 2004/06/19 11:47:17 rurban
526 added CheckConfigUpdate: CACHE_CONTROL = NONE => NO_CACHE
528 Revision 1.17 2004/06/17 11:31:50 rurban
529 check necessary localized actionpages
531 Revision 1.16 2004/06/16 10:38:58 rurban
532 Disallow refernces in calls if the declaration is a reference
533 ("allow_call_time_pass_reference clean").
534 PhpWiki is now allow_call_time_pass_reference = Off clean,
535 but several external libraries may not.
536 In detail these libs look to be affected (not tested):
540 Revision 1.15 2004/06/07 19:50:40 rurban
541 add owner field to mimified dump
543 Revision 1.14 2004/06/07 18:38:18 rurban
544 added mysql 4.1.x search fix
546 Revision 1.13 2004/06/04 20:32:53 rurban
547 Several locale related improvements suggested by Pierrick Meignen
548 LDAP fix by John Cole
549 reanable admin check without ENABLE_PAGEPERM in the admin plugins
551 Revision 1.12 2004/05/18 13:59:15 rurban
552 rename simpleQuery to genericSqlQuery
554 Revision 1.11 2004/05/15 13:06:17 rurban
555 skip the HomePage, at first upgrade the ActionPages, then the database, then the rest
557 Revision 1.10 2004/05/15 01:19:41 rurban
558 upgrade prefix fix by Kai Krakow
560 Revision 1.9 2004/05/14 11:33:03 rurban
561 version updated to 1.3.11pre
562 upgrade stability fix
564 Revision 1.8 2004/05/12 10:49:55 rurban
565 require_once fix for those libs which are loaded before FileFinder and
566 its automatic include_path fix, and where require_once doesn't grok
567 dirname(__FILE__) != './lib'
568 upgrade fix with PearDB
569 navbar.tmpl: remove spaces for IE button alignment
571 Revision 1.7 2004/05/06 17:30:38 rurban
572 CategoryGroup: oops, dos2unix eol
573 improved phpwiki_version:
574 pre -= .0001 (1.3.10pre: 1030.099)
575 -p1 += .001 (1.3.9-p1: 1030.091)
576 improved InstallTable for mysql and generic SQL versions and all newer tables so far.
577 abstracted more ADODB/PearDB methods for action=upgrade stuff:
578 backend->backendType(), backend->database(),
579 backend->listOfFields(),
580 backend->listOfTables(),
582 Revision 1.6 2004/05/03 15:05:36 rurban
585 Revision 1.4 2004/05/02 21:26:38 rurban
586 limit user session data (HomePageHandle and auth_dbi have to invalidated anyway)
587 because they will not survive db sessions, if too large.
588 extended action=upgrade
589 some WikiTranslation button work
590 revert WIKIAUTH_UNOBTAINABLE (need it for main.php)
591 some temp. session debug statements
593 Revision 1.3 2004/04/29 22:33:30 rurban
594 fixed sf.net bug #943366 (Kai Krakow)
595 couldn't load localized url-undecoded pagenames
597 Revision 1.2 2004/03/12 15:48:07 rurban
598 fixed explodePageList: wrong sortby argument order in UnfoldSubpages
599 simplified lib/stdlib.php:explodePageList
608 // c-hanging-comment-ender-p: nil
609 // indent-tabs-mode: nil