]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/upgrade.php
added CheckConfigUpdate: CACHE_CONTROL = NONE => NO_CACHE
[SourceForge/phpwiki.git] / lib / upgrade.php
1 <?php //-*-php-*-
2 rcs_id('$Id: upgrade.php,v 1.18 2004-06-19 11:47:17 rurban Exp $');
3
4 /*
5  Copyright 2004 $ThePhpWikiProgrammingTeam
6
7  This file is part of PhpWiki.
8
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.
13
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.
18
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
22  */
23
24
25 /**
26  * Upgrade the WikiDB and config settings after installing a new 
27  * PhpWiki upgrade.
28  * Status: experimental, no queries for verification yet, no db update,
29  *         no merge conflict
30  * Installation on an existing PhpWiki database needs some 
31  * additional worksteps. Each step will require multiple pages.
32  *
33  * This is the plan:
34  *  1. Check for new or changed database schema and update it 
35  *     according to some predefined upgrade tables. (medium)
36  *  2. Check for new or changed (localized) pgsrc/ pages and ask 
37  *     for upgrading these. Check timestamps, upgrade silently or 
38  *     show diffs if existing. Overwrite or merge (easy)
39  *  3. Check for new or changed or deprecated index.php settings
40  *     and help in upgrading these. (hard)
41  *  4. Check for changed plugin invocation arguments. (hard)
42  *  5. Check for changed theme variables. (hard)
43  *
44  * @author: Reini Urban
45  */
46 require_once("lib/loadsave.php");
47
48 /**
49  * TODO: check for the pgsrc_version number, not the revision
50  */
51 function doPgsrcUpdate(&$request,$pagename,$path,$filename) {
52     $dbi = $request->getDbh(); 
53     $page = $dbi->getPage($pagename);
54     if ($page->exists()) {
55         // check mtime: update automatically if pgsrc is newer
56         $rev = $page->getCurrentRevision();
57         $page_mtime = $rev->get('mtime');
58         $data  = implode("", file($path."/".$filename));
59         if (($parts = ParseMimeifiedPages($data))) {
60             usort($parts, 'SortByPageVersion');
61             reset($parts);
62             $pageinfo = $parts[0];
63             $stat  = stat($path."/".$filename);
64             $new_mtime = @$pageinfo['versiondata']['mtime'];
65             if (!$new_mtime)
66                 $new_mtime = @$pageinfo['versiondata']['lastmodified'];
67             if (!$new_mtime)
68                 $new_mtime = @$pageinfo['pagedata']['date'];
69             if (!$new_mtime)
70                 $new_mtime = $stat[9];
71             if ($new_mtime > $page_mtime) {
72                 echo "$path/$pagename: ",_("newer than the existing page."),
73                     _(" replace "),"($new_mtime &gt; $page_mtime)","<br />\n";
74                 LoadAny($request,$path."/".$filename);
75                 echo "<br />\n";
76             } else {
77                 echo "$path/$pagename: ",_("older than the existing page."),
78                     _(" skipped"),".<br />\n";
79             }
80         } else {
81             echo "$path/$pagename: ",("unknown format."),
82                     _(" skipped"),".<br />\n";
83         }
84     } else {
85         echo sprintf(_("%s does not exist"),$pagename),"<br />\n";
86         LoadAny($request,$path."/".$filename);
87         echo "<br />\n";
88     }
89 }
90
91 /** need the english filename (required precondition: urlencode == urldecode)
92  *  returns the plugin name.
93  */ 
94 function isActionPage($filename) {
95     static $special = array("DebugInfo"         => "_BackendInfo",
96                             "PhpWikiRecentChanges" => "RssFeed",
97                             "ProjectSummary"    => "RssFeed",
98                             "RecentReleases"    => "RssFeed",
99                             );
100     $base = preg_replace("/\..{1,4}$/","",basename($filename));
101     if (isset($special[$base])) return $special[$base];
102     if (FindFile("lib/plugin/".$base.".php",true)) return $base;
103     else return false;
104 }
105
106 function CheckActionPageUpdate(&$request) {
107     echo "<h3>",_("check for necessary ActionPage updates"),"</h3>\n";
108     $dbi = $request->getDbh(); 
109     $path = FindFile('pgsrc');
110     $pgsrc = new fileSet($path);
111     // most actionpages have the same name as the plugin
112     $loc_path = FindLocalizedFile('pgsrc');
113     foreach ($pgsrc->getFiles() as $filename) {
114         if (substr($filename,-1,1) == '~') continue;
115         $pagename = urldecode($filename);
116         if (isActionPage($filename)) {
117             $translation = gettext($pagename);
118             if ($translation == $pagename)
119                 doPgsrcUpdate($request, $pagename, $path, $filename);
120             elseif (FindLocalizedFile('pgsrc/'.urlencode($translation),1))
121                 doPgsrcUpdate($request, $translation, $loc_path, 
122                               urlencode($translation));
123             else
124                 doPgsrcUpdate($request, $pagename, $path, $filename);
125         }
126     }
127 }
128
129 // see loadsave.php for saving new pages.
130 function CheckPgsrcUpdate(&$request) {
131     echo "<h3>",_("check for necessary pgsrc updates"),"</h3>\n";
132     $dbi = $request->getDbh(); 
133     $path = FindLocalizedFile(WIKI_PGSRC);
134     $pgsrc = new fileSet($path);
135     // fixme: verification, ...
136     $isHomePage = false;
137     foreach ($pgsrc->getFiles() as $filename) {
138         if (substr($filename,-1,1) == '~') continue;
139         $pagename = urldecode($filename);
140         // don't ever update the HomePage
141         if (defined(HOME_PAGE))
142             if ($pagename == HOME_PAGE) $isHomePage = true;
143         else
144             if ($pagename == _("HomePage")) $isHomePage = true;
145         if ($pagename == "HomePage") $isHomePage = true;
146         if ($isHomePage) {
147             echo "$path/$pagename: ",_("always skip the HomePage."),
148                 _(" skipped"),".<br />\n";
149             $isHomePage = false;
150             continue;
151         }
152         if (!isActionPage($filename)) {
153             doPgsrcUpdate($request,$pagename,$path,$filename);
154         }
155     }
156     return;
157 }
158
159 /**
160  * TODO: Search table definition in appropriate schema
161  *       and create it.
162  * Supported: mysql and generic SQL, for ADODB and PearDB.
163  */
164 function installTable(&$dbh, $table, $backend_type) {
165     global $DBParams;
166     if (!in_array($DBParams['dbtype'],array('SQL','ADODB'))) return;
167     echo _("MISSING")," ... \n";
168     $backend = &$dbh->_backend->_dbh;
169     /*
170     $schema = findFile("schemas/${backend_type}.sql");
171     if (!$schema) {
172         echo "  ",_("FAILED"),": ",sprintf(_("no schema %s found"),"schemas/${backend_type}.sql")," ... <br />\n";
173         return false;
174     }
175     */
176     extract($dbh->_backend->_table_names);
177     $prefix = isset($DBParams['prefix']) ? $DBParams['prefix'] : '';
178     switch ($table) {
179     case 'session':
180         assert($session_tbl);
181         if ($backend_type == 'mysql') {
182             $dbh->genericQuery("
183 CREATE TABLE $session_tbl (
184         sess_id         CHAR(32) NOT NULL DEFAULT '',
185         sess_data       BLOB NOT NULL,
186         sess_date       INT UNSIGNED NOT NULL,
187         sess_ip         CHAR(15) NOT NULL,
188         PRIMARY KEY (sess_id),
189         INDEX (sess_date)
190 )");
191         } else {
192             $dbh->genericQuery("
193 CREATE TABLE $session_tbl (
194         sess_id         CHAR(32) NOT NULL DEFAULT '',
195         sess_data       ".($backend_type == 'pgsql'?'TEXT':'BLOB')." NOT NULL,
196         sess_date       INT,
197         sess_ip         CHAR(15) NOT NULL
198 )");
199             $dbh->genericQuery("CREATE UNIQUE INDEX sess_id ON $session_tbl (sess_id)");
200         }
201         $dbh->genericQuery("CREATE INDEX sess_date on session (sess_date)");
202         break;
203     case 'user':
204         $user_tbl = $prefix.'user';
205         if ($backend_type == 'mysql') {
206             $dbh->genericQuery("
207 CREATE TABLE $user_tbl (
208         userid  CHAR(48) BINARY NOT NULL UNIQUE,
209         passwd  CHAR(48) BINARY DEFAULT '',
210         PRIMARY KEY (userid)
211 )");
212         } else {
213             $dbh->genericQuery("
214 CREATE TABLE $user_tbl (
215         userid  CHAR(48) NOT NULL,
216         passwd  CHAR(48) DEFAULT ''
217 )");
218             $dbh->genericQuery("CREATE UNIQUE INDEX userid ON $user_tbl (userid)");
219         }
220         break;
221     case 'pref':
222         $pref_tbl = $prefix.'pref';
223         if ($backend_type == 'mysql') {
224             $dbh->genericQuery("
225 CREATE TABLE $pref_tbl (
226         userid  CHAR(48) BINARY NOT NULL UNIQUE,
227         prefs   TEXT NULL DEFAULT '',
228         PRIMARY KEY (userid)
229 )");
230         } else {
231             $dbh->genericQuery("
232 CREATE TABLE $pref_tbl (
233         userid  CHAR(48) NOT NULL,
234         prefs   TEXT NULL DEFAULT '',
235 )");
236             $dbh->genericQuery("CREATE UNIQUE INDEX userid ON $pref_tbl (userid)");
237         }
238         break;
239     case 'member':
240         $member_tbl = $prefix.'member';
241         if ($backend_type == 'mysql') {
242             $dbh->genericQuery("
243 CREATE TABLE $member_tbl (
244         userid    CHAR(48) BINARY NOT NULL,
245         groupname CHAR(48) BINARY NOT NULL DEFAULT 'users',
246         INDEX (userid),
247         INDEX (groupname)
248 )");
249         } else {
250             $dbh->genericQuery("
251 CREATE TABLE $member_tbl (
252         userid    CHAR(48) NOT NULL,
253         groupname CHAR(48) NOT NULL DEFAULT 'users',
254 )");
255             $dbh->genericQuery("CREATE INDEX userid ON $member_tbl (userid)");
256             $dbh->genericQuery("CREATE INDEX groupname ON $member_tbl (groupname)");
257         }
258         break;
259     case 'rating':
260         $rating_tbl = $prefix.'rating';
261         if ($backend_type == 'mysql') {
262             $dbh->genericQuery("
263 CREATE TABLE $rating_tbl (
264         dimension INT(4) NOT NULL,
265         raterpage INT(11) NOT NULL,
266         rateepage INT(11) NOT NULL,
267         ratingvalue FLOAT NOT NULL,
268         rateeversion INT(11) NOT NULL,
269         tstamp TIMESTAMP(14) NOT NULL,
270         PRIMARY KEY (dimension, raterpage, rateepage)
271 )");
272         } else {
273             $dbh->genericQuery("
274 CREATE TABLE $rating_tbl (
275         dimension INT(4) NOT NULL,
276         raterpage INT(11) NOT NULL,
277         rateepage INT(11) NOT NULL,
278         ratingvalue FLOAT NOT NULL,
279         rateeversion INT(11) NOT NULL,
280         tstamp TIMESTAMP(14) NOT NULL,
281 )");
282             $dbh->genericQuery("CREATE UNIQUE INDEX rating ON $rating_tbl (dimension, raterpage, rateepage)");
283         }
284         break;
285     }
286     echo "  ",_("CREATED"),"<br />\n";
287 }
288
289 /**
290  * currently update only session, user, pref and member
291  * jeffs-hacks database api (around 1.3.2) later
292  *   people should export/import their pages if using that old versions.
293  */
294 function CheckDatabaseUpdate(&$request) {
295     global $DBParams, $DBAuthParams;
296     if (!in_array($DBParams['dbtype'], array('SQL','ADODB'))) return;
297     echo "<h3>",_("check for necessary database updates"),"</h3>\n";
298     $dbh = &$request->_dbi;
299     $tables = $dbh->_backend->listOfTables();
300     $backend_type = $dbh->_backend->backendType();
301     $prefix = isset($DBParams['prefix']) ? $DBParams['prefix'] : '';
302     extract($dbh->_backend->_table_names);
303     foreach (explode(':','session:user:pref:member') as $table) {
304         echo sprintf(_("check for table %s"), $table)," ...";
305         if (!in_array($prefix.$table, $tables)) {
306             installTable($dbh, $table, $backend_type);
307         } else {
308             echo _("OK")," <br />\n";
309         }
310     }
311     $backend = &$dbh->_backend->_dbh;
312     // 1.3.8 added session.sess_ip
313     if (phpwiki_version() >= 1030.08 and USE_DB_SESSION and isset($request->_dbsession)) {
314         echo _("check for new session.sess_ip column")," ... ";
315         $database = $dbh->_backend->database();
316         assert(!empty($DBParams['db_session_table']));
317         $session_tbl = $prefix . $DBParams['db_session_table'];
318         $sess_fields = $dbh->_backend->listOfFields($database, $session_tbl);
319         if (!strstr(strtolower(join(':', $sess_fields)),"sess_ip")) {
320             echo "<b>",_("ADDING"),"</b>"," ... ";              
321             $dbh->genericQuery("ALTER TABLE $session_tbl ADD sess_ip CHAR(15) NOT NULL");
322         } else {
323             echo _("OK");
324         }
325         echo "<br />\n";
326     }
327     // 1.3.10 mysql requires page.id auto_increment
328     // mysql, mysqli or mysqlt
329     if (phpwiki_version() >= 1030.099 and substr($backend_type,0,5) == 'mysql') {
330         echo _("check for page.id auto_increment flag")," ...";
331         assert(!empty($page_tbl));
332         $database = $dbh->_backend->database();
333         $fields = mysql_list_fields($database, $page_tbl, $dbh->_backend->connection());
334         $columns = mysql_num_fields($fields); 
335         for ($i = 0; $i < $columns; $i++) {
336             if (mysql_field_name($fields, $i) == 'id') {
337                 $flags = mysql_field_flags($fields, $i);
338                 //FIXME: something wrong with ADODB here!
339                 if (!strstr(strtolower($flags),"auto_increment")) {
340                     echo "<b>",_("ADDING"),"</b>"," ... ";              
341                     // MODIFY col_def valid since mysql 3.22.16,
342                     // older mysql's need CHANGE old_col col_def
343                     $dbh->genericQuery("ALTER TABLE $page_tbl CHANGE id id INT NOT NULL AUTO_INCREMENT");
344                     $fields = mysql_list_fields($database, $page_tbl);
345                     if (!strstr(strtolower(mysql_field_flags($fields, $i)),"auto_increment"))
346                         echo " <b><font color=\"red\">",_("FAILED"),"</font></b><br />\n";
347                     else     
348                         echo _("OK"),"<br />\n";
349                 } else {
350                     echo _("OK"),"<br />\n";                            
351                 }
352                 break;
353             }
354         }
355         mysql_free_result($fields);
356     }
357     // check for mysql 4.1.x binary search bug
358     // http://bugs.mysql.com/bug.php?id=1491
359     // not yet tested for 4.1.2alpha, but confirmed for 4.1.0alpha
360     if (substr($backend_type,0,5) == 'mysql') {
361         echo _("check for mysql 4.1.x binary search bug")," ...";
362         $result = mysql_query("SELECT VERSION()",$dbh->_backend->connection());
363         $row = mysql_fetch_row($result);
364         $mysql_version = $row[0];
365         $arr = explode('.',$mysql_version);
366         $version = (string)(($arr[0] * 100) + $arr[1]) . "." . $arr[2];
367         if ($version > 410.0 and $version < 420.0) {
368             $dbh->genericQuery("ALTER TABLE $page_tbl CHANGE pagename pagename VARCHAR(100) NOT NULL;");
369             echo sprintf(_("version <em>%s</em> <b>FIXED</b>"), $mysql_version),"<br />\n";     
370         } else {
371             echo sprintf(_("version <em>%s</em> not affected"), $mysql_version),"<br />\n";
372         }
373     }
374     return;
375 }
376
377 function fixConfigIni($match, $new) {
378     $file = FindFile("config/config.ini");
379     $found = false;
380     if (is_writable($file)) {
381         $in = fopen($file,"rb");
382         $out = fopen($tmp = tempnam(FindFile("uploads"),"cfg"),"wb");
383         if (isWindows())
384             $tmp = str_replace("/","\\",$tmp);
385         while ($s = fgets($in)) {
386             if (preg_match($match, $s)) {
387                 $s = $new . (isWindows() ? "\r\n" : "\n");
388                 $found = true;
389             }
390             fputs($out, $s);
391         }
392         fclose($in);
393         fclose($out);
394         if (!$found) {
395             echo " <b><font color=\"red\">",_("FAILED"),"</font></b>: ",
396                 sprintf(_("%s not found"), $match);
397             unlink($out);
398         } else {
399             @unlink("$file.bak");
400             @rename($file,"$file.bak");
401             if (rename($tmp, $file))
402                 echo " <b>",_("FIXED"),"</b>";
403             else {
404                 echo " <b>",_("FAILED"),"</b>: ";
405                 sprintf(_("couldn't move %s to %s"), $tmp, $file);
406                 return false;
407             }
408         }
409         return $found;
410     } else {
411         echo " <b><font color=\"red\">",_("FAILED"),"</font></b>: ",
412             sprintf(_("%s is not writable"), $file);
413         return false;
414     }
415 }
416
417 function CheckConfigUpdate(&$request) {
418     echo "<h3>",_("check for necessary config updates"),"</h3>\n";
419     echo _("check for old CACHE_CONTROL = NONE")," ... ";
420     if (defined('CACHE_CONTROL') and CACHE_CONTROL == '') {
421         echo "<br />&nbsp;&nbsp;",_("CACHE_CONTROL is set to 'NONE', and must be changed to 'NO_CACHE'")," ...";
422         fixConfigIni("/^\s*CACHE_CONTROL\s*=\s*NONE/","CACHE_CONTROL = NO_CACHE");
423     } else {
424         echo _("OK");
425     }
426     echo "<br />\n";
427 }
428
429 /**
430  * Upgrade: Base class for multipage worksteps
431  * identify, validate, display options, next step
432  */
433 class Upgrade {
434 }
435
436 class Upgrade_CheckPgsrc extends Upgrade {
437 }
438
439 class Upgrade_CheckDatabaseUpdate extends Upgrade {
440 }
441
442 // TODO: At which step are we? 
443 // validate and do it again or go on with next step.
444
445 /** entry function from lib/main.php
446  */
447 function DoUpgrade($request) {
448
449     if (!$request->_user->isAdmin()) {
450         $request->_notAuthorized(WIKIAUTH_ADMIN);
451         $request->finish(
452                          HTML::div(array('class' => 'disabled-plugin'),
453                                    fmt("Upgrade disabled: user != isAdmin")));
454         return;
455     }
456
457     StartLoadDump($request, _("Upgrading this PhpWiki"));
458     CheckActionPageUpdate($request);
459     CheckDatabaseUpdate($request);
460     CheckPgsrcUpdate($request);
461     //CheckThemeUpdate($request);
462     CheckConfigUpdate($request);
463     EndLoadDump($request);
464 }
465
466
467 /**
468  $Log: not supported by cvs2svn $
469  Revision 1.17  2004/06/17 11:31:50  rurban
470  check necessary localized actionpages
471
472  Revision 1.16  2004/06/16 10:38:58  rurban
473  Disallow refernces in calls if the declaration is a reference
474  ("allow_call_time_pass_reference clean").
475    PhpWiki is now allow_call_time_pass_reference = Off clean,
476    but several external libraries may not.
477    In detail these libs look to be affected (not tested):
478    * Pear_DB odbc
479    * adodb oracle
480
481  Revision 1.15  2004/06/07 19:50:40  rurban
482  add owner field to mimified dump
483
484  Revision 1.14  2004/06/07 18:38:18  rurban
485  added mysql 4.1.x search fix
486
487  Revision 1.13  2004/06/04 20:32:53  rurban
488  Several locale related improvements suggested by Pierrick Meignen
489  LDAP fix by John Cole
490  reanable admin check without ENABLE_PAGEPERM in the admin plugins
491
492  Revision 1.12  2004/05/18 13:59:15  rurban
493  rename simpleQuery to genericQuery
494
495  Revision 1.11  2004/05/15 13:06:17  rurban
496  skip the HomePage, at first upgrade the ActionPages, then the database, then the rest
497
498  Revision 1.10  2004/05/15 01:19:41  rurban
499  upgrade prefix fix by Kai Krakow
500
501  Revision 1.9  2004/05/14 11:33:03  rurban
502  version updated to 1.3.11pre
503  upgrade stability fix
504
505  Revision 1.8  2004/05/12 10:49:55  rurban
506  require_once fix for those libs which are loaded before FileFinder and
507    its automatic include_path fix, and where require_once doesn't grok
508    dirname(__FILE__) != './lib'
509  upgrade fix with PearDB
510  navbar.tmpl: remove spaces for IE &nbsp; button alignment
511
512  Revision 1.7  2004/05/06 17:30:38  rurban
513  CategoryGroup: oops, dos2unix eol
514  improved phpwiki_version:
515    pre -= .0001 (1.3.10pre: 1030.099)
516    -p1 += .001 (1.3.9-p1: 1030.091)
517  improved InstallTable for mysql and generic SQL versions and all newer tables so far.
518  abstracted more ADODB/PearDB methods for action=upgrade stuff:
519    backend->backendType(), backend->database(),
520    backend->listOfFields(),
521    backend->listOfTables(),
522
523  Revision 1.6  2004/05/03 15:05:36  rurban
524  + table messages
525
526  Revision 1.4  2004/05/02 21:26:38  rurban
527  limit user session data (HomePageHandle and auth_dbi have to invalidated anyway)
528    because they will not survive db sessions, if too large.
529  extended action=upgrade
530  some WikiTranslation button work
531  revert WIKIAUTH_UNOBTAINABLE (need it for main.php)
532  some temp. session debug statements
533
534  Revision 1.3  2004/04/29 22:33:30  rurban
535  fixed sf.net bug #943366 (Kai Krakow)
536    couldn't load localized url-undecoded pagenames
537
538  Revision 1.2  2004/03/12 15:48:07  rurban
539  fixed explodePageList: wrong sortby argument order in UnfoldSubpages
540  simplified lib/stdlib.php:explodePageList
541
542  */
543
544 // For emacs users
545 // Local Variables:
546 // mode: php
547 // tab-width: 8
548 // c-basic-offset: 4
549 // c-hanging-comment-ender-p: nil
550 // indent-tabs-mode: nil
551 // End:
552 ?>