2 rcs_id('$Id: upgrade.php,v 1.25 2004-09-06 08:28:00 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 extract($dbh->_backend->_table_names);
327 foreach (explode(':','session:user:pref:member') as $table) {
328 echo sprintf(_("check for table %s"), $table)," ...";
329 if (!in_array($prefix.$table, $tables)) {
330 installTable($dbh, $table, $backend_type);
332 echo _("OK")," <br />\n";
335 $backend = &$dbh->_backend->_dbh;
336 // 1.3.8 added session.sess_ip
337 if (phpwiki_version() >= 1030.08 and USE_DB_SESSION and isset($request->_dbsession)) {
338 echo _("check for new session.sess_ip column")," ... ";
339 $database = $dbh->_backend->database();
340 assert(!empty($DBParams['db_session_table']));
341 $session_tbl = $prefix . $DBParams['db_session_table'];
342 $sess_fields = $dbh->_backend->listOfFields($database, $session_tbl);
343 if (!strstr(strtolower(join(':', $sess_fields)),"sess_ip")) {
344 // TODO: postgres test (should be able to add columns at the end, but not in between)
345 echo "<b>",_("ADDING"),"</b>"," ... ";
346 $dbh->genericSqlQuery("ALTER TABLE $session_tbl ADD sess_ip CHAR(15) NOT NULL");
347 $dbh->genericSqlQuery("CREATE INDEX sess_date ON $session_tbl (sess_date)");
353 // 1.3.10 mysql requires page.id auto_increment
354 // mysql, mysqli or mysqlt
355 if (phpwiki_version() >= 1030.099 and substr($backend_type,0,5) == 'mysql') {
356 echo _("check for page.id auto_increment flag")," ...";
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.24 2004/07/05 13:56:22 rurban
500 sqlite autoincrement fix
502 Revision 1.23 2004/07/04 10:28:06 rurban
505 Revision 1.22 2004/07/03 17:21:28 rurban
506 updated docs: submitted new mysql bugreport (#1491 did not fix it)
508 Revision 1.21 2004/07/03 16:51:05 rurban
509 optional DBADMIN_USER:DBADMIN_PASSWD for action=upgrade (if no ALTER permission)
510 added atomic mysql REPLACE for PearDB as in ADODB
511 fixed _lock_tables typo links => link
512 fixes unserialize ADODB bug in line 180
514 Revision 1.20 2004/07/03 14:48:18 rurban
515 Tested new mysql 4.1.3-beta: binary search bug as fixed.
516 => fixed action=upgrade,
517 => version check in PearDB also (as in ADODB)
519 Revision 1.19 2004/06/19 12:19:09 rurban
520 slightly improved docs
522 Revision 1.18 2004/06/19 11:47:17 rurban
523 added CheckConfigUpdate: CACHE_CONTROL = NONE => NO_CACHE
525 Revision 1.17 2004/06/17 11:31:50 rurban
526 check necessary localized actionpages
528 Revision 1.16 2004/06/16 10:38:58 rurban
529 Disallow refernces in calls if the declaration is a reference
530 ("allow_call_time_pass_reference clean").
531 PhpWiki is now allow_call_time_pass_reference = Off clean,
532 but several external libraries may not.
533 In detail these libs look to be affected (not tested):
537 Revision 1.15 2004/06/07 19:50:40 rurban
538 add owner field to mimified dump
540 Revision 1.14 2004/06/07 18:38:18 rurban
541 added mysql 4.1.x search fix
543 Revision 1.13 2004/06/04 20:32:53 rurban
544 Several locale related improvements suggested by Pierrick Meignen
545 LDAP fix by John Cole
546 reanable admin check without ENABLE_PAGEPERM in the admin plugins
548 Revision 1.12 2004/05/18 13:59:15 rurban
549 rename simpleQuery to genericSqlQuery
551 Revision 1.11 2004/05/15 13:06:17 rurban
552 skip the HomePage, at first upgrade the ActionPages, then the database, then the rest
554 Revision 1.10 2004/05/15 01:19:41 rurban
555 upgrade prefix fix by Kai Krakow
557 Revision 1.9 2004/05/14 11:33:03 rurban
558 version updated to 1.3.11pre
559 upgrade stability fix
561 Revision 1.8 2004/05/12 10:49:55 rurban
562 require_once fix for those libs which are loaded before FileFinder and
563 its automatic include_path fix, and where require_once doesn't grok
564 dirname(__FILE__) != './lib'
565 upgrade fix with PearDB
566 navbar.tmpl: remove spaces for IE button alignment
568 Revision 1.7 2004/05/06 17:30:38 rurban
569 CategoryGroup: oops, dos2unix eol
570 improved phpwiki_version:
571 pre -= .0001 (1.3.10pre: 1030.099)
572 -p1 += .001 (1.3.9-p1: 1030.091)
573 improved InstallTable for mysql and generic SQL versions and all newer tables so far.
574 abstracted more ADODB/PearDB methods for action=upgrade stuff:
575 backend->backendType(), backend->database(),
576 backend->listOfFields(),
577 backend->listOfTables(),
579 Revision 1.6 2004/05/03 15:05:36 rurban
582 Revision 1.4 2004/05/02 21:26:38 rurban
583 limit user session data (HomePageHandle and auth_dbi have to invalidated anyway)
584 because they will not survive db sessions, if too large.
585 extended action=upgrade
586 some WikiTranslation button work
587 revert WIKIAUTH_UNOBTAINABLE (need it for main.php)
588 some temp. session debug statements
590 Revision 1.3 2004/04/29 22:33:30 rurban
591 fixed sf.net bug #943366 (Kai Krakow)
592 couldn't load localized url-undecoded pagenames
594 Revision 1.2 2004/03/12 15:48:07 rurban
595 fixed explodePageList: wrong sortby argument order in UnfoldSubpages
596 simplified lib/stdlib.php:explodePageList
605 // c-hanging-comment-ender-p: nil
606 // indent-tabs-mode: nil